Files
org-hyperion-cules/cckdutil.c
T
Greg Smith d984d56cb5 17 Jun 2002 Fix segmentation fault during cckd compress - Greg Smith
git-svn-id: file:///home/jj/hercules.svn/trunk@919 956126f8-22a0-4046-8f4a-272fa8102e63
2002-06-18 03:28:03 +00:00

2226 lines
81 KiB
C

/* CCKDUTIL.C (c) Copyright Roger Bowler, 1999-2002 */
/* ESA/390 Compressed CKD Common routines */
/*-------------------------------------------------------------------*/
/* This module contains functions for compressed CKD devices */
/* used by more than 1 main program. */
/*-------------------------------------------------------------------*/
#include "hercules.h"
#define ENDMSG(m, format, a...) \
if(m!=NULL) fprintf (m, "cckdend: " format, ## a)
#define COMPMSG(m, format, a...) \
if(m!=NULL) fprintf (m, "cckdcomp: " format, ## a)
#define CDSKMSG(m, format, a...) \
if(m!=NULL) fprintf (m, "cckdcdsk: " format, ## a)
typedef struct _SPCTAB { /* Space table */
off_t pos; /* Space offset */
long long len; /* Space length */
long long siz; /* Space size */
unsigned int val; /* Value for space */
void *ptr; /* Pointer to recovered space*/
int typ; /* Type of space */
} SPCTAB;
#define SPCTAB_NONE 0 /* Ignore this space entry */
#define SPCTAB_DEVHDR 1 /* Space is device header */
#define SPCTAB_CDEVHDR 2 /* Space is compressed hdr */
#define SPCTAB_L1TAB 3 /* Space is level 1 table */
#define SPCTAB_L2TAB 4 /* Space is level 2 table */
#define SPCTAB_TRKIMG 5 /* Space is track image */
#define SPCTAB_FREE 6 /* Space is free block */
#define SPCTAB_END 7 /* Space is end-of-file */
/*-------------------------------------------------------------------*/
/* Internal functions */
/*-------------------------------------------------------------------*/
int cdsk_spctab_comp(const void *, const void *);
int cdsk_rcvtab_comp(const void *, const void *);
int cdsk_valid_trk (int, BYTE *, int, int, int, BYTE *);
int cdsk_valid_blk (int, BYTE *, int, int, int, BYTE *);
int cdsk_recover_trk (int, BYTE *, int, int, int);
int cdsk_build_gap (SPCTAB *, int *, SPCTAB *);
int cdsk_build_gap_long (SPCTAB *, int *, SPCTAB *);
/*-------------------------------------------------------------------*/
/* Static data areas */
/*-------------------------------------------------------------------*/
static BYTE eighthexFF[] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
/*-------------------------------------------------------------------*/
/* Change the endianess of a compressed file */
/*-------------------------------------------------------------------*/
int cckd_swapend (int fd, FILE *m)
{
int rc; /* Return code */
int i; /* Index */
CCKDDASD_DEVHDR cdevhdr; /* Compressed ckd header */
CCKD_L1ENT *l1; /* Level 1 table */
CCKD_L2ENT l2[256]; /* Level 2 table */
CCKD_FREEBLK fb; /* Free block */
int swapend; /* 1 = New endianess doesn't
match machine endianess */
int n; /* Level 1 table entries */
U32 o; /* Level 2 table offset */
/* fix the compressed ckd header */
rc = lseek (fd, CKDDASD_DEVHDR_SIZE, SEEK_SET);
if (rc == -1)
{
ENDMSG (m, "lseek error fd %d offset %lld: %s\n",
fd, (long long)CKDDASD_DEVHDR_SIZE, strerror(errno));
return -1;
}
rc = read (fd, &cdevhdr, CCKDDASD_DEVHDR_SIZE);
if (rc != CCKDDASD_DEVHDR_SIZE)
{
ENDMSG (m, "cdevhdr read error fd %d: %s\n",
fd, strerror(errno));
return -1;
}
cckd_swapend_chdr (&cdevhdr);
rc = lseek (fd, CKDDASD_DEVHDR_SIZE, SEEK_SET);
if (rc == -1)
{
ENDMSG (m, "lseek error fd %d offset %lld: %s\n",
fd, (long long)CKDDASD_DEVHDR_SIZE, strerror(errno));
return -1;
}
cdevhdr.options |= CCKD_ORDWR;
rc = write (fd, &cdevhdr, CCKDDASD_DEVHDR_SIZE);
if (rc != CCKDDASD_DEVHDR_SIZE)
{
ENDMSG (m, "cdevhdr write error fd %d: %s\n",
fd, strerror(errno));
return -1;
}
/* Determine need to swap */
swapend = (cckd_endian() !=
((cdevhdr.options & CCKD_BIGENDIAN) != 0));
/* fix the level 1 table */
n = cdevhdr.numl1tab;
if (swapend) cckd_swapend4((char *)&n);
l1 = malloc (n * CCKD_L1ENT_SIZE);
if (l1 == NULL)
{
ENDMSG (m, "l1tab malloc error fd %d size %ud: %s\n",
fd, (unsigned int)(n * CCKD_L1ENT_SIZE), strerror(errno));
return -1;
}
rc = lseek (fd, CCKD_L1TAB_POS, SEEK_SET);
if (rc == -1)
{
ENDMSG (m, "lseek error fd %d offset %lld: %s\n",
fd, (long long)CCKD_L1TAB_POS, strerror(errno));
free (l1);
return -1;
}
rc = read (fd, l1, n * CCKD_L1ENT_SIZE);
if (rc != n * CCKD_L1ENT_SIZE)
{
ENDMSG (m, "l1tab read error fd %d: %s\n",
fd, strerror(errno));
free (l1);
return -1;
}
cckd_swapend_l1 (l1, n);
rc = lseek (fd, CCKD_L1TAB_POS, SEEK_SET);
if (rc == -1)
{
ENDMSG (m, "lseek error fd %d offset %lld: %s\n",
fd, (long long)CCKD_L1TAB_POS, strerror(errno));
free (l1);
return -1;
}
rc = write (fd, l1, n * CCKD_L1ENT_SIZE);
if (rc != n * CCKD_L1ENT_SIZE)
{
ENDMSG (m, "l1tab write error fd %d: %s\n",
fd, strerror(errno));
free (l1);
return -1;
}
/* fix the level 2 tables */
for (i=0; i < n; i++)
{
o = l1[i];
if (swapend) cckd_swapend4((char *)&o);
if (o != 0 && o != 0xffffffff)
{
rc = lseek (fd, (off_t)o, SEEK_SET);
if (rc == -1)
{
ENDMSG (m, "lseek error fd %d offset %lld: %s\n",
fd, (long long)o, strerror(errno));
free (l1);
return -1;
}
rc = read (fd, &l2, CCKD_L2TAB_SIZE);
if (rc != CCKD_L2TAB_SIZE)
{
ENDMSG (m, "l2[%d] read error, offset %lld bytes read %d : %s\n",
i, (long long)o, rc, strerror(errno));
free (l1);
return -1;
}
cckd_swapend_l2 ((CCKD_L2ENT *)&l2);
rc = lseek (fd, (off_t)o, SEEK_SET);
if (rc == -1)
{
ENDMSG (m, "lseek error fd %d offset %lld: %s\n",
fd, (long long)o, strerror(errno));
free (l1);
return -1;
}
rc = write (fd, &l2, CCKD_L2TAB_SIZE);
if (rc != CCKD_L2TAB_SIZE)
{
ENDMSG (m, "l2[%d] write error fd %d (%d): %s\n",
i, fd, rc, strerror(errno));
free (l1);
return -1;
}
}
}
free (l1);
/* fix the free chain */
for (o = cdevhdr.free; o != 0; o = fb.pos)
{
if (swapend) cckd_swapend4 ((char *)&o);
rc = lseek (fd, o, SEEK_SET);
if (rc == -1)
{
ENDMSG (m, "lseek error fd %d offset %lld: %s\n",
fd, (long long)o, strerror(errno));
return -1;
}
rc = read (fd, &fb, CCKD_FREEBLK_SIZE);
if (rc != CCKD_FREEBLK_SIZE)
{
ENDMSG (m, "free block read error fd %d: %s\n",
fd, strerror(errno));
return -1;
}
cckd_swapend_free (&fb);
rc = lseek (fd, (off_t)o, SEEK_SET);
if (rc == -1)
{
ENDMSG (m, "lseek error fd %d offset %lld: %s\n",
fd, (long long)o, strerror(errno));
return -1;
}
rc = write (fd, &fb, CCKD_FREEBLK_SIZE);
if (rc != CCKD_FREEBLK_SIZE)
{
ENDMSG (m, "free block write error fd %d: %s\n",
fd, strerror(errno));
return -1;
}
}
return 0;
}
/*-------------------------------------------------------------------*/
/* Swap endian - compressed device header */
/*-------------------------------------------------------------------*/
void cckd_swapend_chdr (CCKDDASD_DEVHDR *cdevhdr)
{
/* fix the compressed ckd header */
cdevhdr->options ^= CCKD_BIGENDIAN;
cckd_swapend4 ((char *) &cdevhdr->numl1tab);
cckd_swapend4 ((char *) &cdevhdr->numl2tab);
cckd_swapend4 ((char *) &cdevhdr->size);
cckd_swapend4 ((char *) &cdevhdr->used);
cckd_swapend4 ((char *) &cdevhdr->free);
cckd_swapend4 ((char *) &cdevhdr->free_total);
cckd_swapend4 ((char *) &cdevhdr->free_largest);
cckd_swapend4 ((char *) &cdevhdr->free_number);
cckd_swapend4 ((char *) &cdevhdr->free_imbed);
cckd_swapend2 ((char *) &cdevhdr->compress_parm);
}
/*-------------------------------------------------------------------*/
/* Swap endian - level 1 table */
/*-------------------------------------------------------------------*/
void cckd_swapend_l1 (CCKD_L1ENT *l1, int n)
{
int i; /* Index */
for (i=0; i < n; i++)
cckd_swapend4 ((char *) &l1[i]);
}
/*-------------------------------------------------------------------*/
/* Swap endian - level 2 table */
/*-------------------------------------------------------------------*/
void cckd_swapend_l2 (CCKD_L2ENT *l2)
{
int i; /* Index */
for (i=0; i < 256; i++)
{
cckd_swapend4 ((char *) &l2[i].pos);
cckd_swapend2 ((char *) &l2[i].len);
cckd_swapend2 ((char *) &l2[i].size);
}
}
/*-------------------------------------------------------------------*/
/* Swap endian - free space entry */
/*-------------------------------------------------------------------*/
void cckd_swapend_free (CCKD_FREEBLK *fb)
{
cckd_swapend4 ((char *) &fb->pos);
cckd_swapend4 ((char *) &fb->len);
}
/*-------------------------------------------------------------------*/
/* Swap endian - 4 bytes */
/*-------------------------------------------------------------------*/
void cckd_swapend4 (char *c)
{
char temp[4];
memcpy (&temp, c, 4);
c[0] = temp[3];
c[1] = temp[2];
c[2] = temp[1];
c[3] = temp[0];
}
/*-------------------------------------------------------------------*/
/* Swap endian - 2 bytes */
/*-------------------------------------------------------------------*/
void cckd_swapend2 (char *c)
{
char temp[2];
memcpy (&temp, c, 2);
c[0] = temp[1];
c[1] = temp[0];
}
/*-------------------------------------------------------------------*/
/* Are we little or big endian? From Harbison&Steele. */
/*-------------------------------------------------------------------*/
int cckd_endian()
{
union
{
long l;
char c[sizeof (long)];
} u;
u.l = 1;
return (u.c[sizeof (long) - 1] == 1);
}
/*-------------------------------------------------------------------*/
/* Remove all free space from a compressed ckd file */
/*-------------------------------------------------------------------*/
int cckd_comp (int fd, FILE *m)
{
int rc; /* Return code */
off_t pos; /* Current file offset */
int i; /* Loop index */
CKDDASD_DEVHDR devhdr; /* CKD device header */
CCKDDASD_DEVHDR cdevhdr; /* Compressed CKD device hdr */
CCKD_L1ENT *l1=NULL; /* -> Primary lookup table */
int l1tabsz; /* Primary lookup table size */
CCKD_L2ENT l2; /* Secondary lookup table */
CCKD_FREEBLK fb; /* Free space block */
int len; /* Space length */
int trksz; /* Maximum track size */
int heads; /* Heads per cylinder */
int blks; /* Number fba blocks */
int trk; /* Track number */
int l1x,l2x; /* Lookup indices */
int freed=0; /* Total space freed */
int imbedded=0; /* Imbedded space freed */
int moved=0; /* Total space moved */
int ckddasd; /* 1=CKD dasd 0=FBA dasd */
BYTE buf[65536]; /* Buffer */
/*-------------------------------------------------------------------*/
/* Read the headers and level 1 table */
/*-------------------------------------------------------------------*/
restart:
/* Read the headers */
rc = lseek (fd, 0, SEEK_SET);
if (rc < 0)
{
COMPMSG (m, "lseek error offset 0: %s\n",strerror(errno));
return -1;
}
rc = read (fd, &devhdr, CKDDASD_DEVHDR_SIZE);
if (rc < CKDDASD_DEVHDR_SIZE)
{
COMPMSG (m, "devhdr read error: %s\n",strerror(errno));
return -1;
}
if (memcmp (devhdr.devid, "CKD_C370", 8) == 0
|| memcmp (devhdr.devid, "CKD_S370", 8) == 0)
ckddasd = 1;
else if (memcmp (devhdr.devid, "FBA_C370", 8) == 0
|| memcmp (devhdr.devid, "FBA_S370", 8) == 0)
ckddasd = 0;
else
{
COMPMSG (m, "incorrect header id\n");
return -1;
}
rc = read (fd, &cdevhdr, CCKDDASD_DEVHDR_SIZE);
if (rc < CCKDDASD_DEVHDR_SIZE)
{
COMPMSG (m, "cdevhdr read error: %s\n",strerror(errno));
return -1;
}
cdevhdr.options |= CCKD_ORDWR;
/* Check the endianess of the file */
if (((cdevhdr.options & CCKD_BIGENDIAN) != 0 && cckd_endian() == 0) ||
((cdevhdr.options & CCKD_BIGENDIAN) == 0 && cckd_endian() != 0))
{
rc = cckd_swapend (fd, m);
if (rc < 0) return -1;
goto restart;
}
if (cdevhdr.free_number == 0)
{
COMPMSG (m, "file has no free space%s\n", "");
return 0;
}
l1tabsz = cdevhdr.numl1tab * CCKD_L1ENT_SIZE;
l1 = malloc (l1tabsz);
if (l1 == NULL)
{
COMPMSG (m, "l1 table malloc error: %s\n",strerror(errno));
return -1;
}
rc = read (fd, l1, l1tabsz);
if (rc < l1tabsz)
{
COMPMSG (m, "l1 table read error: %s\n",strerror(errno));
return -1;
}
if (ckddasd)
{
trksz = ((U32)(devhdr.trksize[3]) << 24)
| ((U32)(devhdr.trksize[2]) << 16)
| ((U32)(devhdr.trksize[1]) << 8)
| (U32)(devhdr.trksize[0]);
heads = ((U32)(devhdr.heads[3]) << 24)
| ((U32)(devhdr.heads[2]) << 16)
| ((U32)(devhdr.heads[1]) << 8)
| (U32)(devhdr.heads[0]);
blks = -1;
}
else
{
trksz = CFBA_BLOCK_SIZE + CKDDASD_TRKHDR_SIZE;
heads = -1;
blks = ((U32)(cdevhdr.cyls[0]) << 24)
| ((U32)(cdevhdr.cyls[2]) << 16)
| ((U32)(cdevhdr.cyls[1]) << 8)
| (U32)(cdevhdr.cyls[0]);
}
/*-------------------------------------------------------------------*/
/* Relocate spaces */
/*-------------------------------------------------------------------*/
/* figure out where to start; if imbedded free space
(ie old format), start right after the level 1 table,
otherwise start at the first free space */
if (cdevhdr.free_imbed) pos = (off_t)(CCKD_L1TAB_POS + l1tabsz);
else pos = (off_t)cdevhdr.free;
#ifdef EXTERNALGUI
if (extgui) fprintf (stderr,"SIZE=%d\n",cdevhdr.size);
#endif /*EXTERNALGUI*/
/* process each space in file sequence; the only spaces we expect
are free blocks, level 2 tables, and track images */
for ( ; pos + freed < cdevhdr.size; pos += len)
{
#ifdef EXTERNALGUI
if (extgui) fprintf (stderr,"POS=%lu\n",pos);
#endif /*EXTERNALGUI*/
/* check for free space */
if ((U32)(pos + freed) == cdevhdr.free)
{ /* space is free space */
rc = lseek (fd, pos + freed, SEEK_SET);
rc = read (fd, &fb, CCKD_FREEBLK_SIZE);
cdevhdr.free = fb.pos;
cdevhdr.free_number--;
cdevhdr.free_total -= fb.len;
freed += fb.len;
len = 0;
continue;
}
/* check for l2 table */
for (i = 0; i < cdevhdr.numl1tab; i++)
if (l1[i] == (U32)(pos + freed)) break;
if (i < cdevhdr.numl1tab)
{ /* space is a l2 table */
len = CCKD_L2TAB_SIZE;
if (freed)
{
rc = lseek (fd, (off_t)l1[i], SEEK_SET);
rc = read (fd, buf, len);
l1[i] -= freed;
rc = lseek (fd, (off_t)l1[i], SEEK_SET);
rc = write (fd, buf, len);
moved += len;
}
continue;
}
/* check for track image */
rc = lseek (fd, pos + freed, SEEK_SET);
rc = read (fd, buf, 8);
if (ckddasd)
{
trk = ((buf[1] << 8) + buf[2]) * heads
+ ((buf[3] << 8) + buf[4]);
}
else
{
trk = (buf[1] << 24)
| (buf[2] << 16)
| (buf[3] << 8)
| buf[4];
}
l1x = trk >> 8;
l2x = trk & 0xff;
l2.pos = l2.len = l2.size = 0;
if (l1[l1x])
{
rc = lseek (fd, (off_t)(l1[l1x] + l2x * CCKD_L2ENT_SIZE), SEEK_SET);
rc = read (fd, &l2, CCKD_L2ENT_SIZE);
}
if (l2.pos == pos + freed)
{ /* space is a track image */
len = l2.len;
imbedded = l2.size - l2.len;
if (freed)
{
rc = lseek (fd, (off_t)l2.pos, SEEK_SET);
rc = read (fd, buf, len);
l2.pos -= freed;
rc = lseek (fd, (off_t)l2.pos, SEEK_SET);
rc = write (fd, buf, len);
}
if (freed || imbedded )
{
l2.size = l2.len;
rc = lseek (fd, (off_t)(l1[l1x] + l2x * CCKD_L2ENT_SIZE), SEEK_SET);
rc = write (fd, &l2, CCKD_L2ENT_SIZE);
}
cdevhdr.free_imbed -= imbedded;
cdevhdr.free_total -= imbedded;
freed += imbedded;
moved += len;
continue;
}
/* space is unknown -- have to punt
if we have freed some space, then add a free space */
COMPMSG (m, "unknown space at offset 0x%llx : "
"%2.2x%2.2x%2.2x%2.2x %2.2x%2.2x%2.2x%2.2x\n",
(long long)(pos + freed), buf[0], buf[1], buf[2],
buf[3], buf[4], buf[5], buf[6], buf[7]);
if (freed)
{
fb.pos = cdevhdr.free;
fb.len = freed;
rc = lseek (fd, pos, SEEK_SET);
rc = write (fd, &fb, CCKD_FREEBLK_SIZE);
cdevhdr.free = pos;
cdevhdr.free_number++;
cdevhdr.free_total += freed;
freed = 0;
}
break;
}
/* update the largest free size -- will be zero unless we punted */
cdevhdr.free_largest = 0;
for (pos = cdevhdr.free; pos; pos = fb.pos)
{
#ifdef EXTERNALGUI
if (extgui) fprintf (stderr,"POS=%lu\n",pos);
#endif /*EXTERNALGUI*/
rc = lseek (fd, pos, SEEK_SET);
rc = read (fd, &fb, CCKD_FREEBLK_SIZE);
if (fb.len > cdevhdr.free_largest)
cdevhdr.free_largest = fb.len;
}
/* write the compressed header and l1 table and truncate the file */
cdevhdr.options |= CCKD_NOFUDGE;
if (cdevhdr.free_imbed == 0)
{
cdevhdr.vrm[0] = CCKD_VERSION;
cdevhdr.vrm[1] = CCKD_RELEASE;
cdevhdr.vrm[2] = CCKD_MODLVL;
}
cdevhdr.size -= freed;
rc = lseek (fd, CKDDASD_DEVHDR_SIZE, SEEK_SET);
rc = write (fd, &cdevhdr, CCKDDASD_DEVHDR_SIZE);
rc = write (fd, l1, l1tabsz);
rc = ftruncate (fd, cdevhdr.size);
COMPMSG (m, "file %ssuccessfully compacted, %d freed and %d moved\n",
freed ? "" : "un", freed, moved);
free (l1);
return (freed ? freed : -1);
}
/*-------------------------------------------------------------------*/
/* Perform check function on a compressed ckd file */
/*-------------------------------------------------------------------*/
int cckd_chkdsk(int fd, FILE *m, int level)
{
CKDDASD_DEVHDR devhdr; /* CKD device header */
CCKDDASD_DEVHDR cdevhdr; /* Compressed CKD device hdr */
CCKDDASD_DEVHDR cdevhdr2; /* CCKD header 2 */
CCKD_L1ENT *l1=NULL; /* -> Primary lookup table */
int l1tabsz; /* Primary lookup table size */
CCKD_L2ENT l2[256], *l2p=NULL; /* Secondary lookup table */
CKDDEV *ckd=NULL; /* -> CKD DASD table entry */
BYTE *buf=NULL, *buf2=NULL; /* Buffers for track image */
long buf2len; /* Buffer length */
BYTE *trkbuf=NULL; /* Pointer to track image */
long trklen=0; /* Length of track image */
int rc; /* Return code */
int crc=-1; /* Chdsk return code */
int i,j,k; /* Indexes */
struct stat fst; /* File status information */
int cyls=-1, hdrcyls=-1; /* Total cylinders */
int trks; /* Total tracks */
int heads=-1, hdrheads=-1; /* Heads per cylinder */
int rec0len=8; /* R0 length */
int maxdlen=-1; /* Max data length for device*/
int trksz=-1, hdrtrksz=-1; /* Track size */
off_t hipos, lopos; /* Valid high/low offsets */
off_t pos; /* File offset */
int hdrerr=0, fsperr=0, l1errs=0, l2errs=0, trkerrs=0,
othererrs = 0; /* Error indicators */
off_t fsp; /* Free space offset */
CCKD_FREEBLK fb; /* Free space block */
int n; /* Size of space tables */
SPCTAB *spc=NULL, *rcv=NULL, /* Space/Recovery tables */
*gap=NULL; /* Gaps in the file */
int s=0, r=0; /* Space/Recovery indices */
int trk; /* Current track number */
int maxlen; /* Max length of track image */
int gaps=0, gapsize=0; /* Gaps and size in file */
BYTE *gapbuf=NULL; /* Buffer containing gap data*/
int x,y; /* Lookup table indices */
int rcvtrks; /* Nbr trks to be recovered */
int fdflags; /* File flags */
int shadow=0; /* 1=Shadow file */
int swapend=0; /* 1=Wrong endianess */
int fend,mend; /* Byte order indicators */
int ckddasd; /* 1=CKD dasd 0=FBA dasd */
char msg[256]; /* Message */
BYTE *space[] = {"none", "devhdr", "cdevhdr", "l1tab", "l2tab",
"trkimg", "free_blk", "file_end"};
BYTE *compression[] = {"none", "zlib", "bzip2"};
#ifdef EXTERNALGUI
/* The number of "steps" (checks) we'll be doing... */
if (extgui) fprintf (stderr,"STEPS=10\n");
#endif /*EXTERNALGUI*/
#ifdef EXTERNALGUI
/* Beginning first step... */
if (extgui) fprintf (stderr,"STEP=1\n");
#endif /*EXTERNALGUI*/
/*-------------------------------------------------------------------*/
/* Read the device header. */
/*-------------------------------------------------------------------*/
rc = fstat (fd, &fst);
if (rc < 0)
{
CDSKMSG (m, "fstat(%d) error: %s\n",
fd, strerror(errno));
return -1;
}
fdflags = fcntl (fd, F_GETFL);
rc = lseek (fd, 0, SEEK_SET);
if (rc < 0)
{
CDSKMSG (m, "devhdr lseek error: %s\n",
strerror(errno));
}
rc = read (fd, &devhdr, CKDDASD_DEVHDR_SIZE);
if (rc != CKDDASD_DEVHDR_SIZE)
{
CDSKMSG (m, "devhdr read error: %s\n",
strerror(errno));
return -1;
}
/*-------------------------------------------------------------------*/
/* Perform checks on the device header. The following fields */
/* *must* be correct or the chkdsk function terminates: */
/* devid */
/* In this circumstance, the header will need to be */
/* manually repaired -- see cckdfix.c for a sample */
/*-------------------------------------------------------------------*/
/* Check the identifier */
if (memcmp(devhdr.devid, "CKD_C370", 8) == 0
|| memcmp(devhdr.devid, "CKD_S370", 8) == 0)
ckddasd = 1;
else if (memcmp(devhdr.devid, "FBA_C370", 8) == 0
|| memcmp(devhdr.devid, "FBA_S370", 8) == 0)
ckddasd = 0;
else
{
CDSKMSG (m, "file is not a compressed dasd file\n");
goto cdsk_return;
}
if (memcmp(devhdr.devid, "CKD_S370", 8) == 0
|| memcmp(devhdr.devid, "FBA_S370", 8) == 0)
shadow = 1;
/*-------------------------------------------------------------------*/
/* read the compressed CKD device header */
/*-------------------------------------------------------------------*/
rc = read (fd, &cdevhdr, CCKDDASD_DEVHDR_SIZE);
if (rc != CCKDDASD_DEVHDR_SIZE)
{
CDSKMSG (m, "cdevhdr read error: %s\n",
strerror(errno));
goto cdsk_return;
}
/*-------------------------------------------------------------------*/
/* Check the endianess of the compressed CKD file */
/*-------------------------------------------------------------------*/
fend = ((cdevhdr.options & CCKD_BIGENDIAN) != 0);
mend = cckd_endian ();
if (fend != mend)
{
if (fdflags & O_RDWR)
{
CDSKMSG (m, "converting file(%d) to %s\n",
fd, mend ? "big-endian" : "little-endian");
rc = cckd_swapend (fd, m);
if (rc == -1) goto cdsk_return;
rc = lseek (fd, CKDDASD_DEVHDR_SIZE, SEEK_SET);
rc = read (fd, &cdevhdr, CCKDDASD_DEVHDR_SIZE);
cdevhdr.options |= CCKD_ORDWR;
}
else
{
swapend = 1;
cckd_swapend_chdr (&cdevhdr);
}
}
/*-------------------------------------------------------------------*/
/* Lookup the DASD table entry for the device */
/*-------------------------------------------------------------------*/
if (ckddasd)
{
hdrcyls = ((U32)(cdevhdr.cyls[3]) << 24)
| ((U32)(cdevhdr.cyls[2]) << 16)
| ((U32)(cdevhdr.cyls[1]) << 8)
| (U32)(cdevhdr.cyls[0]);
ckd = dasd_lookup (DASD_CKDDEV, NULL, devhdr.devtype, hdrcyls);
if (ckd == NULL)
{
CDSKMSG (m, "DASD table entry not found for 0x%2.2X cyls %d\n",
devhdr.devtype, hdrcyls);
goto cdsk_return;
}
maxdlen = ckd->r1;
heads = ckd->heads;
}
/*-------------------------------------------------------------------*/
/* Perform checks on the headers. The following fields must be */
/* correct or the chkdsk function terminates: */
/* devhdr: */
/* trksz */
/* heads */
/* cdevhdr: */
/* cyls */
/* numl1tab */
/* In this circumstance, the headers will need to be manually */
/* repaired -- see cckdfix.c for a sample */
/*-------------------------------------------------------------------*/
if (ckddasd)
{
/* Check track size */
trksz = sizeof(CKDDASD_TRKHDR)
+ sizeof(CKDDASD_RECHDR) + rec0len
+ sizeof(CKDDASD_RECHDR) + maxdlen
+ sizeof(eighthexFF);
trksz = ((trksz+511)/512)*512;
hdrtrksz = ((U32)(devhdr.trksize[3]) << 24)
| ((U32)(devhdr.trksize[2]) << 16)
| ((U32)(devhdr.trksize[1]) << 8)
| (U32)(devhdr.trksize[0]);
if (trksz != hdrtrksz)
{
CDSKMSG (m, "Invalid track size in header: "
"0x%x, expecting 0x%x\n",
hdrtrksz, trksz);
goto cdsk_return;
}
/* Check number of heads */
hdrheads = ((U32)(devhdr.heads[3]) << 24)
| ((U32)(devhdr.heads[2]) << 16)
| ((U32)(devhdr.heads[1]) << 8)
| (U32)(devhdr.heads[0]);
if (heads != hdrheads)
{
CDSKMSG (m, "Invalid number of heads in header: "
"%d, expecting %d\n",
hdrheads, heads);
goto cdsk_return;
}
/* Check number of cylinders */
if (hdrcyls < ckd->cyls || hdrcyls > ckd->cyls + ckd->altcyls)
{
CDSKMSG (m, "Invalid number of cylinders in header: %d, "
"expecting %d - %d\n",
hdrcyls, ckd->cyls, ckd->cyls + ckd->altcyls);
goto cdsk_return;
}
if (cdevhdr.numl1tab < (hdrcyls * heads + 255) / 256
|| cdevhdr.numl1tab > (hdrcyls * heads + 255) / 256 + 1)
{
CDSKMSG (m, "Invalid number of l1 table entries in header: "
"%d, expecting %d\n",
cdevhdr.numl1tab, (hdrcyls * heads + 255) / 256);
goto cdsk_return;
}
cyls = hdrcyls;
trks = cyls * heads;
}
else
{
int numl1;
trks = ((U32)(cdevhdr.cyls[3]) << 24)
| ((U32)(cdevhdr.cyls[2]) << 16)
| ((U32)(cdevhdr.cyls[1]) << 8)
| (U32)(cdevhdr.cyls[0]);
trks = (trks / CFBA_BLOCK_NUM) + 1;
numl1 = (trks + 255) / 256;
trksz = CFBA_BLOCK_SIZE + CKDDASD_TRKHDR_SIZE;
cyls = heads = -1;
if (cdevhdr.numl1tab != numl1)
{
CDSKMSG (m, "Invalid number of l1 table entries in header: "
"%d, expecting %d\n", cdevhdr.numl1tab, numl1);
goto cdsk_return;
}
}
l1tabsz = cdevhdr.numl1tab * CCKD_L1ENT_SIZE;
/* Perform space checks */
if (cdevhdr.size != fst.st_size
|| cdevhdr.used + cdevhdr.free_total != fst.st_size
|| cdevhdr.free_largest > cdevhdr.free_total
|| (cdevhdr.free == 0
&& (cdevhdr.free_total != 0 || cdevhdr.free_number != 0))
|| (cdevhdr.free != 0
&& (cdevhdr.free_total == 0 || cdevhdr.free_number == 0))
|| (cdevhdr.free_number == 0 && cdevhdr.free_total != 0)
|| (cdevhdr.free_number != 0 && cdevhdr.free_total ==0))
{
CDSKMSG (m, "Recoverable header errors found: "
"%d %d %d %d %d %d %d\n",
(unsigned int)fst.st_size,
(unsigned int)cdevhdr.size,
(unsigned int)cdevhdr.used,
(unsigned int)cdevhdr.free_total,
(unsigned int)cdevhdr.free_largest,
(unsigned int)cdevhdr.free,
(unsigned int)cdevhdr.free_number);
hdrerr = 1;
if (level < 1)
{ level = 1;
CDSKMSG (m, "forcing check level %d\n", level);
}
}
/*-------------------------------------------------------------------*/
/* Read the primary lookup table */
/* We *must* be able to successfully read this table, too */
/*-------------------------------------------------------------------*/
l1 = malloc (l1tabsz);
if (l1 == NULL)
{
CDSKMSG (m, "malloc() l1tab error, size %d: %s\n", l1tabsz,
strerror(errno));
goto cdsk_return;
}
rc = read (fd, l1, l1tabsz);
if (rc != l1tabsz)
{
CDSKMSG (m, "read(%d) l1tab error, size %d: %s\n",
(int) fd, l1tabsz, strerror(errno));
goto cdsk_return;
}
if (swapend) cckd_swapend_l1 (l1, cdevhdr.numl1tab);
/*-------------------------------------------------------------------*/
/* If minimal checking is specified then */
/* If the file was not closed then perform level 1 checking */
/* Else if the file hasn't been written to since the last check */
/* Then perform very minimal checking. */
/*-------------------------------------------------------------------*/
if (level == 0)
{
if (cdevhdr.options & CCKD_OPENED)
{
CDSKMSG (m, "forcing check level 1; file not closed\n");
level = 1;
}
else if ((cdevhdr.options & (CCKD_OPENED | CCKD_ORDWR)) == 0)
level = -1;
}
/*-------------------------------------------------------------------*/
/* Set space boundaries */
/*-------------------------------------------------------------------*/
lopos = (off_t)CCKD_L1TAB_POS + l1tabsz;
hipos = (off_t)fst.st_size;
/*-------------------------------------------------------------------*/
/* Get some buffers */
/*-------------------------------------------------------------------*/
buf = malloc(trksz);
if (buf == NULL)
{
CDSKMSG (m, "malloc() failed for buffer, size %d: %s\n",
trksz, strerror(errno));
goto cdsk_return;
}
buf2 = malloc(trksz);
if (buf2 == NULL)
{
CDSKMSG (m, "malloc() failed for buffer, size %d: %s\n",
trksz, strerror(errno));
goto cdsk_return;
}
/*-------------------------------------------------------------------*/
/* get the space/recovery/gap tables */
/*-------------------------------------------------------------------*/
n = 4 + (cdevhdr.numl1tab + trks) * 2;
spc = malloc (n * sizeof(SPCTAB));
if (spc == NULL)
{ CDSKMSG (m, "malloc() failed for space table size %ud: %s\n",
(unsigned int) (n * sizeof(SPCTAB)), strerror(errno));
goto cdsk_return;
}
rcv = malloc (n * sizeof(SPCTAB));
if (rcv == NULL)
{ CDSKMSG (m, "malloc() failed for recovery table size %ud: %s\n",
(unsigned int) (n * sizeof(SPCTAB)), strerror(errno));
goto cdsk_return;
}
gap = malloc (n * sizeof(SPCTAB));
if (gap == NULL)
{ CDSKMSG (m, "malloc() failed for gap table size %ud: %s\n",
(unsigned int) (n * sizeof(SPCTAB)), strerror(errno));
goto cdsk_return;
}
/*-------------------------------------------------------------------*/
/* Check free space chain */
/* */
/* Things we check for : */
/* (1) free space offset is within a valid position in the file */
/* (2) free space block can be lseek()ed and read() without error */
/* (3) length of the free space is at least the size of a free */
/* space block */
/* (4) next free space does not precede this free space */
/* (5) free space does not extend beyond the end of the file */
/* */
/*-------------------------------------------------------------------*/
free_space_check:
if (level >= 0)
{
memset (&cdevhdr2, 0, CCKDDASD_DEVHDR_SIZE);
cdevhdr2.size = (U32)hipos;
cdevhdr2.used = CKDDASD_DEVHDR_SIZE + CCKDDASD_DEVHDR_SIZE +
l1tabsz;
cdevhdr2.free = cdevhdr.free;
/* if the file wasn't closed then rebuild the free space */
if (cdevhdr.options & CCKD_OPENED)
{ fsperr = 1;
sprintf (msg, "file not closed");
}
else if (hdrerr)
{ fsperr = 1;
sprintf (msg, "header errors");
}
else fsperr = 0;
#ifdef EXTERNALGUI
/* Beginning next step... */
if (extgui) fprintf (stderr,"STEP=2\n");
#endif /*EXTERNALGUI*/
for (fsp = (off_t)cdevhdr.free; fsp && !fsperr; fsp = (off_t)fb.pos)
{
fsperr = 1; /* turn on error indicator */
memset (&fb, 0, CCKD_FREEBLK_SIZE);
sprintf (msg, "pos=0x%llx nxt=0x%llx len=%d\n",
(long long)fsp, (long long)fb.pos, fb.len);
if (fsp < lopos || fsp > hipos - CCKD_FREEBLK_SIZE) break;
rc = lseek (fd, fsp, SEEK_SET);
if (rc == -1) break;
rc = read (fd, &fb, CCKD_FREEBLK_SIZE);
if (rc != CCKD_FREEBLK_SIZE) break;
if (swapend) cckd_swapend_free (&fb);
if (fb.len < CCKD_FREEBLK_SIZE || fsp + fb.len > hipos) break;
if (fb.pos && (fb.pos <= fsp + fb.len ||
fb.pos > hipos - CCKD_FREEBLK_SIZE)) break;
sprintf (msg, "free space at end of the file");
if (fsp + fb.len == hipos && (fdflags & O_RDWR)) break;
fsperr = 0; /* reset error indicator */
cdevhdr2.free_number++;
cdevhdr2.free_total += fb.len;
if (fb.len > cdevhdr2.free_largest)
cdevhdr2.free_largest = fb.len;
}
}
if (fsperr)
{ CDSKMSG (m, "free space errors found: %s\n", msg);
if (level < 1)
{ level = 1;
CDSKMSG (m, "forcing check level %d\n", level);
}
}
/*-------------------------------------------------------------------*/
/* populate the space/recovery tables; perform space checks */
/*-------------------------------------------------------------------*/
space_check:
memset (spc, 0, n * sizeof(SPCTAB));
memset (rcv, 0, n * sizeof(SPCTAB));
memset (gap, 0, n * sizeof(SPCTAB));
s = r = 0;
/* ckddasd device header space */
spc[s].pos = 0;
spc[s].len = spc[s].siz = CKDDASD_DEVHDR_SIZE;
spc[s++].typ = SPCTAB_DEVHDR;
/* compressed ckddasd device header space */
spc[s].pos = CKDDASD_DEVHDR_SIZE;
spc[s].len = spc[s].siz = CCKDDASD_DEVHDR_SIZE;
spc[s++].typ = SPCTAB_CDEVHDR;
/* level 1 lookup table space */
spc[s].pos = CCKD_L1TAB_POS;
spc[s].len = spc[s].siz = l1tabsz;
spc[s++].typ = SPCTAB_L1TAB;
/* end-of-file */
spc[s].pos = hipos;
spc[s++].typ = SPCTAB_END;
#ifdef EXTERNALGUI
/* Beginning next step... */
if (extgui) fprintf (stderr,"STEP=3\n");
#endif /*EXTERNALGUI*/
/* free spaces */
if (level >= 0)
{
for (fsp = (off_t)cdevhdr.free, i=0; i < cdevhdr2.free_number;
fsp = (off_t)fb.pos, i++)
{
rc = lseek (fd, fsp, SEEK_SET);
rc = read (fd, &fb, CCKD_FREEBLK_SIZE);
if (swapend) cckd_swapend_free (&fb);
spc[s].pos = fsp;
spc[s].len = spc[s].siz = fb.len;
spc[s++].typ = SPCTAB_FREE;
}
}
l1errs = l2errs = trkerrs = 0;
#ifdef EXTERNALGUI
/* Beginning next step... */
if (extgui) fprintf (stderr,"STEP=4\n");
#endif /*EXTERNALGUI*/
/* level 2 lookup table and track image spaces */
for (i = 0; i < cdevhdr.numl1tab; i++)
{
int valid_l2, valid_trks, invalid_trks;
if ((l1[i] == 0)
|| (shadow && l1[i] == 0xffffffff)) continue;
/* check for valid offset in l1tab entry */
if ((off_t)l1[i] < lopos || (off_t)l1[i] > hipos - CCKD_L2TAB_SIZE)
{
CDSKMSG (m, "l1[%d] has bad offset 0x%x\n", i, l1[i]);
l1errs++;
/* recover all tracks for the level 2 table entry */
bad_l2:
if (level <= 0)
{ int orig_lvl = level;
level = 1;
CDSKMSG (m, "forcing check level %d\n", level);
if (orig_lvl < 0) goto free_space_check;
goto space_check;
}
CDSKMSG (m, "tracks %d thru %d will be recovered\n",
i * 256, i * 256 + 255 < trks ?
i * 256 + 255 : trks - 1);
rcv[r].typ = SPCTAB_L2TAB;
rcv[r].len = rcv[r].siz = CCKD_L2TAB_SIZE;
rcv[r++].val = i;
for (j = 0; j < 256; j++)
{
if (i * 256 + j >= trks) break;
rcv[r].typ = SPCTAB_TRKIMG;
rcv[r++].val = i * 256 + j;
}
continue;
}
/* continue if very minimal checking */
if (level < 0)
continue;
/* read the level 2 table */
rc = lseek (fd, (off_t)l1[i], SEEK_SET);
if (rc == -1)
{
CDSKMSG (m, "l1[%d] lseek error offset 0x%x: %s\n",
i, l1[i], strerror(errno));
l2errs++;
goto bad_l2;
}
rc = read (fd, &l2, CCKD_L2TAB_SIZE);
if (rc != CCKD_L2TAB_SIZE)
{
CDSKMSG (m, "l1[%d] read error offset 0x%x: %s\n",
i , l1[i], strerror(errno));
l2errs++;
goto bad_l2;
}
if (swapend) cckd_swapend_l2 ((CCKD_L2ENT *)&l2);
/* validate each level 2 entry */
valid_l2 = 1;
validate_l2:
valid_trks = invalid_trks = 0;
for (j = 0; j < 256; j++)
{
if ((l2[j].pos == 0)
|| (shadow && l2[j].pos == 0xffffffff)) continue;
trk = i * 256 + j;
/* consistency check on level 2 table entry */
if (trk >= trks || (off_t)(l2[j].pos) < lopos ||
(off_t)(l2[j].pos + l2[j].len) > hipos ||
l2[j].len <= CKDDASD_TRKHDR_SIZE ||
l2[j].len > trksz)
{
sprintf(msg, "l2tab inconsistency track %d", trk);
bad_trk:
if (level == 0)
{
level = 1;
CDSKMSG (m, "forcing check level %d\n", level);
goto space_check;
}
if (valid_l2)
{ /* start over but with valid_l2 false */
valid_l2 = 0;
for ( ;
spc[s - 1].typ == SPCTAB_TRKIMG &&
spc[s - 1].val >= i * 256 &&
spc[s - 1].val <= i * 256 + 255;
s--)
memset (&spc[s - 1], 0, sizeof (SPCTAB));
goto validate_l2;
}
CDSKMSG (m, "%s\n"
" l2[%d,%d] offset 0x%x len %d\n",
msg, i, j, l2[j].pos, l2[j].len);
trkerrs++;
invalid_trks++;
if (fdflags & O_RDWR)
CDSKMSG (m, " track %d will be recovered\n", trk);
rcv[r].pos = l2[j].pos;
rcv[r].len = l2[j].len;
rcv[r].siz = l2[j].size;
rcv[r].val = trk;
rcv[r++].typ = SPCTAB_TRKIMG;
continue;
}
/* read the track header */
if (level >= 1)
{
rc = lseek (fd, (off_t)l2[j].pos, SEEK_SET);
if (rc == -1)
{
sprintf (msg, "lseek error track %d: %s", trk,
strerror(errno));
goto bad_trk;
}
rc = read (fd, buf, CKDDASD_TRKHDR_SIZE);
if (rc != CKDDASD_TRKHDR_SIZE)
{
sprintf (msg, "read error track %d: %s", trk,
strerror(errno));
goto bad_trk;
}
/* consistency check on track header */
if (ckddasd)
{
if ((buf[0] & ~CCKD_COMPRESS_STRESSED) > CCKD_COMPRESS_MAX
|| (buf[1] * 256 + buf[2]) >= cyls
|| (buf[3] * 256 + buf[4]) >= heads
|| (buf[1] * 256 + buf[2]) * heads +
(buf[3] * 256 + buf[4]) != trk)
{
sprintf (msg, "track %d invalid header "
"0x%2.2x%2.2x%2.2x%2.2x%2.2x", trk,
buf[0], buf[1], buf[2], buf[3], buf[4]);
goto bad_trk;
}
}
else
{
if ((buf[0] & ~CCKD_COMPRESS_STRESSED) > CCKD_COMPRESS_MAX
|| (buf[1] << 24) + (buf[2] << 16) + (buf[3] << 8) + buf[4] != trk)
{
sprintf (msg, "block %d invalid header "
"0x%2.2x%2.2x%2.2x%2.2x%2.2x", trk,
buf[0], buf[1], buf[2], buf[3], buf[4]);
goto bad_trk;
}
}
}
/* if we've had problems with the level 2 table
then validate the entire track */
if (!valid_l2 || level >= 3)
{
rc = read (fd, &buf[CKDDASD_TRKHDR_SIZE],
l2[j].len - CKDDASD_TRKHDR_SIZE);
if (rc != l2[j].len - CKDDASD_TRKHDR_SIZE)
{
sprintf (msg, "read error track %d: %s",
trk, strerror(errno));
goto bad_trk;
}
switch (buf[0] & ~CCKD_COMPRESS_STRESSED)
{
case CCKD_COMPRESS_NONE:
trkbuf = buf;
trklen = l2[j].len;
break;
case CCKD_COMPRESS_ZLIB:
memcpy (buf2, buf, CKDDASD_TRKHDR_SIZE);
buf2len = trksz - CKDDASD_TRKHDR_SIZE;
rc = uncompress (&buf2[CKDDASD_TRKHDR_SIZE],
&buf2len,
&buf[CKDDASD_TRKHDR_SIZE],
l2[j].len);
if (rc != Z_OK)
{
sprintf (msg, "uncompress error for "
"track %d: rc=%d", trk, rc);
goto bad_trk;
}
trkbuf = buf2;
trklen = buf2len + CKDDASD_TRKHDR_SIZE;
break;
#ifdef CCKD_BZIP2
case CCKD_COMPRESS_BZIP2:
memcpy (buf2, buf, CKDDASD_TRKHDR_SIZE);
buf2len = trksz - CKDDASD_TRKHDR_SIZE;
rc = BZ2_bzBuffToBuffDecompress (
&buf2[CKDDASD_TRKHDR_SIZE],
(unsigned int *)&buf2len,
&buf[CKDDASD_TRKHDR_SIZE],
l2[j].len, 0, 0);
if (rc != BZ_OK)
{
sprintf (msg, "decompress error for "
"track %d: rc=%d", trk, rc);
goto bad_trk;
}
trkbuf = buf2;
trklen = buf2len + CKDDASD_TRKHDR_SIZE;
break;
#endif
default:
sprintf (msg, "track %d unknown "
"compression: 0x%x", trk, buf[0]);
goto bad_trk;
}
trkbuf[0] = 0;
rc = cdsk_valid_trk (trk, trkbuf, heads, trklen, trksz, msg);
if (rc != trklen)
goto bad_trk;
}
/* add track to the space table */
valid_trks++;
cdevhdr2.used += l2[j].len;
if ((l2[j].size - l2[j].len) && (fdflags & O_RDWR))
{
if (!fsperr)
CDSKMSG (m, "imbedded free space will be removed%s\n","");
fsperr = 1;
l2[j].size = l2[j].len;
rc = lseek (fd, (off_t)l1[i], SEEK_SET);
rc = write (fd, &l2, CCKD_L2TAB_SIZE);
}
cdevhdr2.free_imbed += l2[j].size - l2[j].len;
cdevhdr2.free_total += l2[j].size - l2[j].len;
spc[s].pos = l2[j].pos;
spc[s].len = l2[j].len;
spc[s].siz = l2[j].size;
spc[s].val = trk;
spc[s++].typ = SPCTAB_TRKIMG;
}
/* if the level 2 table appears valid, add the table to the
space table; otherwise add the table to the recover table */
if (valid_l2 || valid_trks > 0)
{
cdevhdr2.used += CCKD_L2TAB_SIZE;
spc[s].pos = l1[i];
spc[s].len = spc[s].siz = CCKD_L2TAB_SIZE;
spc[s].typ = SPCTAB_L2TAB;
spc[s++].val = i;
}
else
{
CDSKMSG (m, "l2[%d] will be recovered due to errors\n", i);
l2errs++;
for ( ;
spc[s - 1].typ == SPCTAB_TRKIMG &&
spc[s - 1].val >= i * 256 &&
spc[s - 1].val <= i * 256 + 255;
s--)
memset (&spc[s - 1], 0, sizeof (SPCTAB));
for ( ;
rcv[r - 1].typ == SPCTAB_TRKIMG &&
rcv[r - 1].val >= i * 256 &&
rcv[r - 1].val <= i * 256 + 255;
r--)
memset (&rcv[r - 1], 0, sizeof (SPCTAB));
goto bad_l2;
}
}
/*-------------------------------------------------------------------*/
/* we will rebuild free space on any kind of error */
/*-------------------------------------------------------------------*/
othererrs = r || l1errs || l2errs || trkerrs;
if ((level >= 0
&& memcmp (&cdevhdr.CCKD_FREEHDR, &cdevhdr2.CCKD_FREEHDR, CCKD_FREEHDR_SIZE))
|| othererrs)
fsperr = 1;
/*-------------------------------------------------------------------*/
/* look for gaps and overlaps */
/* */
/* Overlaps aren't handled particularly elegantly, but then, */
/* overlaps aren't exactly expected either. */
/* */
/*-------------------------------------------------------------------*/
overlap:
qsort ((void *)spc, s, sizeof(SPCTAB), cdsk_spctab_comp);
for ( ; spc[s-1].typ == SPCTAB_NONE; s--);
#ifdef EXTERNALGUI
/* Beginning next step... */
if (extgui) fprintf (stderr,"STEP=5\n");
#endif /*EXTERNALGUI*/
for (i = 0; i < s - 1; i++)
{
// CDSKMSG (m, "%s[%d] pos 0x%x length %d\n",
// space[spc[i].typ], spc[i].val, spc[i].pos, spc[i].len);
if (spc[i].pos + spc[i].siz < spc[i+1].pos)
{
if (othererrs)
CDSKMSG (m, "gap at pos 0x%llx length %d\n",
(long long)(spc[i].pos + spc[i].siz),
(int)(spc[i+1].pos - (spc[i].pos + spc[i].siz)));
}
else if (spc[i].pos + spc[i].len > spc[i+1].pos)
{
CDSKMSG (m, "%s at pos 0x%llx length %lld overlaps "
"%s at pos 0x%llx\n",
space[spc[i].typ], (long long)spc[i].pos, spc[i].len,
space[spc[i+1].typ], (long long)spc[i+1].pos);
for (j = i; j < s; j++)
{
if (spc[i].pos + spc[i].siz <= spc[j].pos) break;
switch (spc[j].typ) {
case SPCTAB_FREE:
fsperr = 1;
memset (&spc[j], 0, sizeof(SPCTAB));
break;
case SPCTAB_L2TAB:
l2errs++;
memcpy (&rcv[r++], &spc[j], sizeof(SPCTAB));
for (k = 0; k < s - 1; k++)
{ /* move any tracks for the l2tab from the
space table to the recovery table */
if (spc[k].typ == SPCTAB_TRKIMG &&
spc[k].val >= spc[j].val * 256 &&
spc[k].val <= spc[j].val * 256 + 255)
{
memcpy (&rcv[r++], &spc[k], sizeof(SPCTAB));
memset (&spc[k], 0, sizeof(SPCTAB));
}
}
memset (&spc[j], 0, sizeof(SPCTAB));
break;
case SPCTAB_TRKIMG:
trkerrs++;
memcpy (&rcv[r++], &spc[j], sizeof(SPCTAB));
memset (&spc[j], 0, sizeof(SPCTAB));
break;
case SPCTAB_END:
break;
} /* switch */
}
goto overlap;
}
}
/* return if we are still doing `very minimal' checking */
if (level < 0)
{
crc = 0;
goto cdsk_return;
}
/* build the gap table */
gaps = cdsk_build_gap (spc, &s, gap);
/* sort the recovery table by track */
if (r > 0) qsort ((void *)rcv, r, sizeof(SPCTAB), cdsk_rcvtab_comp);
/* if any kind of error, indicate free space error */
if (gaps || r || l1errs || l2errs || trkerrs) fsperr = 1;
#if 0
/*-------------------------------------------------------------------*/
/* some debugging stuff */
/*-------------------------------------------------------------------*/
CDSKMSG (m, "gap table size %d\n position size data\n", gaps);
for (i = 0; i < gaps; i++)
{char buf[5];
lseek (fd, gap[i].pos, SEEK_SET);
read (fd, &buf, 5);
CDSKMSG (m, "%3d 0x%8.8x %5lld %2.2x%2.2x%2.2x%2.2x%2.2x\n",
i+1, (int)gap[i].pos, gap[i].siz, buf[0], buf[1], buf[2], buf[3], buf[4]);
}
CDSKMSG (m,"recovery table size %d\n type value\n", r);
for (i = 0; i < r; i++)
CDSKMSG (m, "%3d %8s %5d\n", i+1, space[rcv[i].typ], rcv[i].val);
// if (gaps || r) goto cdsk_return;
#endif
/*-------------------------------------------------------------------*/
/* return if file opened read-only */
/*-------------------------------------------------------------------*/
if (!(fdflags & O_RDWR))
{
crc = fsperr;
if (l1errs || l2errs || trkerrs) crc = -1;
if (crc)
CDSKMSG (m, "errors detected on read-only file%s\n","");
goto cdsk_return;
}
/*-------------------------------------------------------------------*/
/* perform track image space recovery */
/*-------------------------------------------------------------------*/
/* count the number of tracks to be recovered */
for (i = rcvtrks = 0; i < r; i++)
if (rcv[i].typ == SPCTAB_TRKIMG) rcvtrks++;
#ifdef EXTERNALGUI
/* Beginning next step... */
if (extgui) fprintf (stderr,"STEP=6\n");
#endif /*EXTERNALGUI*/
for (gapsize = i = 0; r > 0 && i < gaps && rcvtrks > 0; i++)
{
/* get new buffer if previous isn't large enough */
if (gap[i].siz > gapsize)
{
if (gapbuf != NULL) free (gapbuf);
gapsize = gap[i].siz;
gapbuf = malloc (gapsize);
if (gapbuf ==NULL)
{
CDSKMSG (m, "malloc failed for gap buf, size %d:%s\n",
gapsize, strerror(errno));
goto cdsk_return;
}
}
/* read the gap */
rc = lseek (fd, gap[i].pos, SEEK_SET);
if (rc == -1)
{
CDSKMSG (m, "lseek failed for gap at pos 0x%llx: %s\n",
(long long)gap[i].pos, strerror(errno));
goto cdsk_return;
}
rc = read (fd, gapbuf, gap[i].siz);
if (rc != gap[i].siz)
{
CDSKMSG (m, "read failed for gap at pos 0x%llx length %lld: %s\n",
(long long)gap[i].pos, gap[i].siz, strerror(errno));
goto cdsk_return;
}
CDSKMSG (m, "recovery for gap at pos 0x%llx length %lld\n",
(long long)gap[i].pos, gap[i].siz);
/* search for track images in the gap */
for (j = 0; j < gap[i].siz; )
{
/* test for possible track header */
if (ckddasd)
{
if (!(gap[i].siz - j > CKDDASD_TRKHDR_SIZE
&& (gapbuf[j] & ~CCKD_COMPRESS_STRESSED) <= CCKD_COMPRESS_MAX
&& (gapbuf[j+1] << 8) + gapbuf[j+2] < cyls
&& (gapbuf[j+3] << 8) + gapbuf[j+4] < heads))
{ j++; continue;
}
/* get track number */
trk = ((gapbuf[j+1] << 8) + gapbuf[j+2]) * heads +
(gapbuf[j+3] << 8) + gapbuf[j+4];
}
/* test for possible block header */
else
{
/* get block number */
trk = (gapbuf[j+1] << 24) + (gapbuf[j+2] << 16) +
(gapbuf[j+3] << 8) + gapbuf[j+4];
if (!(gap[i].siz - j > CKDDASD_TRKHDR_SIZE
&& (gapbuf[j] & ~CCKD_COMPRESS_STRESSED) <= CCKD_COMPRESS_MAX
&& trk < trks))
{ j++; continue;
}
}
/* see if track is to be recovered */
for (k = 0; k < r; k++)
if (rcv[k].typ == SPCTAB_TRKIMG &&
trk <= rcv[k].val) break;
/* if track is not being recovered, continue */
if (!(k < r && rcv[k].typ == SPCTAB_TRKIMG &&
rcv[k].val == trk))
{ j++; continue;
}
/* calculate maximum track length */
if (gap[i].siz - j < trksz) maxlen = gap[i].siz - j;
else maxlen = trksz;
CDSKMSG (m, "%s %d at 0x%llx maxlen %d comp %s: ",
ckddasd ? "trk" : "blk",
trk, (long long)(gap[i].pos + j), maxlen,
compression[gapbuf[j] & ~CCKD_COMPRESS_STRESSED]);
/* Try to recover the track at the space in the gap */
trklen = cdsk_recover_trk (trk, &gapbuf[j], heads, maxlen, trksz);
/* continue if track couldn't be uncompressed or isn't valid */
if (trklen < 0)
{
if (m) fprintf (m,"not recovered here\n");
j++; continue;
}
if (m) fprintf (m,"recovered!! len %ld\n", trklen);
/* enter the recovered track into the space table */
spc[s].pos = gap[i].pos + j;
spc[s].len = spc[s].siz = trklen;
spc[s].val = trk;
spc[s++].typ = SPCTAB_TRKIMG;
/* remove the entry from the recovery table */
memset (&rcv[k], 0, sizeof(SPCTAB));
/* update the level 2 table entry */
x = trk / 256; y = trk % 256;
for (k = k - 1; k >= 0; k--)
{
if (rcv[k].typ == SPCTAB_L2TAB && rcv[k].val == x)
break;
}
if (k >= 0) /* level 2 table is also being recovered */
{
if (rcv[k].ptr == NULL)
{
rcv[k].ptr = calloc (256, CCKD_L2ENT_SIZE);
if (rcv[k].ptr == NULL)
{
CDSKMSG (m, "calloc failed l2 recovery buf, size %ud: %s\n",
(unsigned int) (256*CCKD_L2ENT_SIZE),
strerror(errno));
goto cdsk_return;
}
}
l2p = (CCKD_L2ENT *) rcv[k].ptr;
l2p[y].pos = spc[s-1].pos;
l2p[y].len = l2p[y].size = spc[s-1].len;
}
else /* level 2 table entry is in the file */
{
rc = lseek (fd, (off_t)(l1[x] + y * CCKD_L2ENT_SIZE), SEEK_SET);
rc = read (fd, &l2, CCKD_L2ENT_SIZE);
l2[0].pos = (U32)spc[s-1].pos;
l2[0].len = l2[0].size = spc[s-1].len;
rc = lseek (fd, (off_t)(l1[x] + y * CCKD_L2ENT_SIZE), SEEK_SET);
rc = write (fd, &l2, CCKD_L2ENT_SIZE);
}
/* position past the track image */
j += trklen;
/* decrement trks to be recovered; exit if none left */
rcvtrks--;
if (rcvtrks == 0) break;
} /* for each byte in the gap */
} /* for each gap */
/*-------------------------------------------------------------------*/
/* figure out what we have left to recover */
/* */
/* 'r' will have the index of the last l2tab entry in the recovery */
/* or zero if no level 2 tables need to be recovered. */
/*-------------------------------------------------------------------*/
if (r > 0)
{ qsort ((void *)rcv, r, sizeof(SPCTAB), cdsk_rcvtab_comp);
for ( ; rcv[r-1].typ == SPCTAB_NONE; r--);
}
#ifdef EXTERNALGUI
/* Beginning next step... */
if (extgui) fprintf (stderr,"STEP=7\n");
#endif /*EXTERNALGUI*/
for (i = 0, j = -1; i < r; i++)
{
switch (rcv[i].typ) {
case SPCTAB_L2TAB:
j = i;
break;
case SPCTAB_TRKIMG:
x = rcv[i].val / 256; y = rcv[i].val % 256;
rc = lseek (fd, (off_t)(l1[x] + y * CCKD_L2ENT_SIZE),
SEEK_SET);
rc = read (fd, &l2, CCKD_L2ENT_SIZE);
if (l2[0].pos != 0)
{
CDSKMSG (m, "*** track %d was not recovered\n",
rcv[i].val);
l2[0].pos = l2[0].len = l2[0].size = 0;
rc = lseek (fd, (off_t)(l1[x] + y * CCKD_L2ENT_SIZE),
SEEK_SET);
rc = write (fd, &l2, CCKD_L2ENT_SIZE);
}
break;
} /* switch */
}
r = j + 1;
/*-------------------------------------------------------------------*/
/* remove free space if any errors were encountered */
/*-------------------------------------------------------------------*/
if (fsperr)
for (i = 0; i < s; i++)
if (spc[i].typ == SPCTAB_FREE)
memset (&spc[i], 0, sizeof(SPCTAB));
/*-------------------------------------------------------------------*/
/* rebuild the gap/space tables */
/*-------------------------------------------------------------------*/
gaps = cdsk_build_gap (spc, &s, gap);
/*-------------------------------------------------------------------*/
/* recover any level 2 tables */
/*-------------------------------------------------------------------*/
#ifdef EXTERNALGUI
/* Beginning next step... */
if (extgui) fprintf (stderr,"STEP=8\n");
#endif /*EXTERNALGUI*/
for (i = 0; i < r; i++)
{
if (rcv[i].typ != SPCTAB_L2TAB) continue;
if (rcv[i].ptr == NULL)
{
CDSKMSG (m, "l2[%d] not recovered\n", rcv[i].val);
l1[rcv[i].val] = 0;
}
else
{ /* find a space for the recovered level 2 table */
for (j = 0; j < gaps; j++)
if (gap[j].siz == CCKD_L2TAB_SIZE ||
gap[j].siz >= CCKD_L2TAB_SIZE + CCKD_FREEBLK_SIZE)
break;
if (j < gaps)
{ /* found a place for the recovered level 2 table */
pos = gap[j].pos;
gap[j].pos += CCKD_L2TAB_SIZE;
gap[j].len = gap[j].siz -= CCKD_L2TAB_SIZE;
}
else
{ /* don't think this should ever happen */
pos = hipos;
rc = ftruncate (fd, pos + CCKD_L2TAB_SIZE);
rc = fstat (fd, &fst);
hipos = fst.st_size;
spc[s-1].pos = hipos; /* should be SPCTAB_END */
}
/* write the recovered level 2 table to the file */
l2p = (CCKD_L2ENT *) rcv[i].ptr;
rc = lseek (fd, pos, SEEK_SET);
rc = write (fd, l2p, CCKD_L2TAB_SIZE);
CDSKMSG (m, "l2[%d] recovered\n", rcv[i].val);
l1[rcv[i].val] = pos;
spc[s].typ = SPCTAB_L2TAB;
spc[s].pos = pos;
spc[s].len = spc[s++].siz = CCKD_L2TAB_SIZE;
}
}
/*-------------------------------------------------------------------*/
/* if any errors at all occurred then we rebuild the free space */
/*-------------------------------------------------------------------*/
if (fsperr)
{
/*-------------------------------------------------------------------*/
/* Remove short gaps... */
/* Short gaps should either be preceded by a l2 table or a trk img; */
/* in this case, we relocate the l2 tab or trk img somewhere else. */
/* Otherwise, the short gap is ignored. */
/*-------------------------------------------------------------------*/
short_gap:
gaps = cdsk_build_gap (spc, &s, gap);
#ifdef EXTERNALGUI
/* Beginning next step... */
if (extgui) fprintf (stderr,"STEP=9\n");
#endif /*EXTERNALGUI*/
for (i = j = 0; i < gaps; i++)
{
if (gap[i].siz == 0 || gap[i].siz >= CCKD_FREEBLK_SIZE)
continue; /* continue if not a short gap */
/* find the space preceding the gap, it should
always be a l2tab or a trkimg */
for ( ; j < s; j++)
if (spc[j].pos + spc[j].siz == gap[i].pos) break;
if (spc[j].typ == SPCTAB_TRKIMG || spc[j].typ == SPCTAB_L2TAB)
{ /* move the preceding space somewhere else */
for (k = 0; k < gaps; k++)
if (gap[k].siz == spc[j].siz ||
gap[k].siz >= spc[j].siz + CCKD_FREEBLK_SIZE)
break;
if (k < gaps) /* use space in an existing gap */
pos = gap[k].pos;
else
{ /* use space at the end of the file */
pos = hipos;
rc = ftruncate (fd, hipos + spc[j].siz);
rc = fstat (fd, &fst);
hipos = fst.st_size;
spc[s-1].pos = hipos; /* should be SPCTAB_END */
}
/* write the preceding space to its new location */
rc = lseek (fd, spc[j].pos, SEEK_SET);
rc = read (fd, buf, spc[j].siz);
spc[j].pos = pos;
rc = lseek (fd, spc[j].pos, SEEK_SET);
rc = write (fd, buf, spc[j].siz);
if (spc[j].typ == SPCTAB_L2TAB) l1[spc[j].val] = spc[j].pos;
else
{ /* update level 2 table entry for track image */
x = spc[j].val / 256; y = spc[j].val % 256;
rc = lseek (fd, (off_t)(l1[x] + CCKD_L2ENT_SIZE * y), SEEK_SET);
rc = read (fd, &l2, CCKD_L2ENT_SIZE);
l2[0].pos = spc[j].pos;
rc = lseek (fd, (off_t)(l1[x] + CCKD_L2ENT_SIZE * y), SEEK_SET);
rc = write (fd, &l2, CCKD_L2ENT_SIZE);
}
goto short_gap;
}
CDSKMSG (m, "short gap pos 0x%llx preceded by %s pos 0x%llx\n",
(long long)gap[i].pos, space[spc[j].typ],
(long long)spc[j].pos);
}
/*-------------------------------------------------------------------*/
/* Finally, we are ready to rebuild the free space */
/*-------------------------------------------------------------------*/
gaps = cdsk_build_gap_long (spc, &s, gap);
/* if the last gap is at the end of the file, then
truncate the file */
if (gaps > 0 && gap[gaps-1].pos + gap[gaps-1].siz == hipos)
{
hipos -= gap[gaps-1].siz;
rc = ftruncate (fd, hipos);
gaps--;
}
memset (&cdevhdr.CCKD_FREEHDR, 0, CCKD_FREEHDR_SIZE);
cdevhdr.size = hipos;
if (gaps) cdevhdr.free = gap[0].pos;
cdevhdr.free_number = gaps;
#ifdef EXTERNALGUI
/* Beginning next step... */
if (extgui) fprintf (stderr,"STEP=10\n");
#endif /*EXTERNALGUI*/
for (i = 0; i < gaps; i++)
{
if (i < gaps - 1) fb.pos = gap[i+1].pos;
else fb.pos = 0;
fb.len = gap[i].siz;
rc = lseek (fd, gap[i].pos, SEEK_SET);
rc = write (fd, &fb, CCKD_FREEBLK_SIZE);
cdevhdr.free_total += gap[i].siz;
if (gap[i].siz > cdevhdr.free_largest)
cdevhdr.free_largest = gap[i].siz;
}
for (i = 0; i < s - 1; i++)
{
cdevhdr.free_total += spc[i].siz - spc[i].len;
cdevhdr.free_imbed += spc[i].siz - spc[i].len;
cdevhdr.used += spc[i].len;
}
/*-------------------------------------------------------------------*/
/* Update the header and l1 table */
/*-------------------------------------------------------------------*/
if (cdevhdr.free_imbed == 0)
{
cdevhdr.vrm[0] = CCKD_VERSION;
cdevhdr.vrm[1] = CCKD_RELEASE;
cdevhdr.vrm[2] = CCKD_MODLVL;
}
cdevhdr.options |= CCKD_NOFUDGE;
rc = lseek (fd, CKDDASD_DEVHDR_SIZE, SEEK_SET);
rc = write (fd, &cdevhdr, CCKDDASD_DEVHDR_SIZE);
rc = write (fd, l1, l1tabsz);
CDSKMSG (m, "free space rebuilt: 1st 0x%x nbr %d\n "
"total %d imbed %d largest %d\n",
cdevhdr.free, cdevhdr.free_number, cdevhdr.free_total,
cdevhdr.free_imbed, cdevhdr.free_largest);
}
crc = fsperr;
/*-------------------------------------------------------------------*/
/* Free all space and return */
/*-------------------------------------------------------------------*/
cdsk_return:
/* free all malloc()ed storage */
for (i = 0; i < r; i++) if (rcv[i].ptr) free (rcv[i].ptr);
if (spc) free (spc);
if (rcv) free (rcv);
if (gap) free (gap);
if (gapbuf) free (gapbuf);
if (buf) free (buf);
if (buf2) free (buf2);
if (l1) free (l1);
/* if file is ok or has been repaired, turn off the
opened bit if it's on */
if (crc >= 0
&& (cdevhdr.options & (CCKD_OPENED | CCKD_ORDWR)) != 0
&& (fdflags & O_RDWR))
{
cdevhdr.options &= ~(CCKD_OPENED | CCKD_ORDWR);
rc = lseek (fd, CKDDASD_DEVHDR_SIZE + 3, SEEK_SET);
rc = write (fd, &cdevhdr.options, 1);
}
return crc;
} /* end function cckd_chkdsk */
/*-------------------------------------------------------------------*/
/* Sort Space Table by pos. Always return entry type NONE behind */
/* any other entries */
/*-------------------------------------------------------------------*/
int cdsk_spctab_comp(const void *a, const void *b)
{
const SPCTAB *x = a, *y = b; /* Entries to be sorted */
if (x->typ == SPCTAB_NONE) return 1;
if (y->typ == SPCTAB_NONE) return -1;
if (x->pos < y->pos) return -1;
else return 1;
} /* end function cdsk_spctab_comp */
/*-------------------------------------------------------------------*/
/* Sort Recovery Table by val. Always return entry type NONE behind */
/* any other entries */
/*-------------------------------------------------------------------*/
int cdsk_rcvtab_comp(const void *a, const void *b)
{
const SPCTAB *x = a, *y = b; /* Entries to be sorted */
unsigned int v1, v2; /* Value for entry */
if (x->typ == SPCTAB_NONE) return 1;
if (y->typ == SPCTAB_NONE) return -1;
if (x->typ == SPCTAB_L2TAB) v1 = x->val * 256;
else v1 = x->val;
if (y->typ == SPCTAB_L2TAB) v2 = y->val * 256;
else v2 = y->val;
if (v1 < v2) return -1;
if (v1 > v2) return 1;
if (x->typ == SPCTAB_L2TAB) return -1;
else return 1;
} /* end function cdsk_rcvtab_comp */
/*-------------------------------------------------------------------*/
/* Validate a track image */
/*-------------------------------------------------------------------*/
int cdsk_valid_trk (int trk, BYTE *buf, int heads, int len, int trksz, BYTE *msg)
{
int cyl; /* Cylinder */
int head; /* Head */
char cchh[4],cchh2[4]; /* Cyl, head big-endian */
int r; /* Record number */
int sz; /* Track size */
int kl,dl; /* Key/Data lengths */
/* Check for fba dasd */
if (heads == -1)
return cdsk_valid_blk (trk, buf, heads, len, trksz, msg);
/* cylinder and head calculations */
cyl = trk / heads;
head = trk % heads;
cchh[0] = cyl >> 8;
cchh[1] = cyl & 0xFF;
cchh[2] = head >> 8;
cchh[3] = head & 0xFF;
/* validate home address */
if (buf[0] !=0 || memcmp (&buf[1], cchh, 4) != 0)
{
if (msg)
sprintf (msg, "track %d HA validation error: "
"%2.2x%2.2x%2.2x%2.2x%2.2x",
trk, buf[0], buf[1], buf[2], buf[3], buf[4]);
return -1;
}
/* validate record 0 */
memcpy (cchh2, &buf[5], 4); cchh2[0] &= 0x7f; /* fix for ovflow */
if (memcmp (cchh, cchh2, 4) != 0 || buf[9] != 0 ||
buf[10] != 0 || buf[11] != 0 || buf[12] != 8)
{
if (msg)
sprintf (msg, "track %d R0 validation error: "
"%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x",
trk, buf[5], buf[6], buf[7], buf[8], buf[9],
buf[10], buf[11], buf[12]);
return -1;
}
/* validate records 1 thru n */
for (r = 1, sz = 21; sz + 8 <= trksz; sz += 8 + kl + dl, r++)
{
if (memcmp (&buf[sz], eighthexFF, 8) == 0) break;
kl = buf[sz+5];
dl = buf[sz+6] * 256 + buf[sz+7];
/* fix for track overflow bit */
memcpy (cchh2, &buf[sz], 4); cchh2[0] &= 0x7f;
/* fix for funny formatted vm disks */
if (r == 1) memcpy (cchh, cchh2, 4);
if (memcmp (cchh, cchh2, 4) != 0 || buf[sz+4] == 0 ||
sz + 8 + kl + dl >= len)
{
if (msg)
sprintf (msg, "track %d R%d validation error: "
"%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x",
trk, r, buf[sz], buf[sz+1], buf[sz+2], buf[sz+3],
buf[sz+4], buf[sz+5], buf[sz+6], buf[sz+7]);
return -1;
}
}
if (sz + 8 > trksz)
{
if (msg)
sprintf (msg, "track %d R%d validation error, no EOT: "
"%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x",
trk, r, buf[sz], buf[sz+1], buf[sz+2], buf[sz+3],
buf[sz+4], buf[sz+5], buf[sz+6], buf[sz+7]);
return -1;
}
sz += 8;
if (sz != len && msg)
sprintf (msg, "track %d size mismatch, expected %d found %d",
trk, len, sz);
return sz;
} /* end function cdsk_valid_trk */
/*-------------------------------------------------------------------*/
/* Validate a block group image */
/*-------------------------------------------------------------------*/
int cdsk_valid_blk (int trk, BYTE *buf, int heads, int len, int trksz, BYTE *msg)
{
if (len == trksz)
return len;
if (msg)
sprintf (msg, "block %d length %d validation error: "
"%2.2x%2.2x%2.2x%2.2x%2.2x",
trk, len, buf[0], buf[1], buf[2], buf[3], buf[4]);
return -1;
} /* end function cdsk_valid_blk */
/*-------------------------------------------------------------------*/
/* Recover a track image */
/*-------------------------------------------------------------------*/
int cdsk_recover_trk (int trk, BYTE *buf, int heads, int maxlen, int trksz)
{
int rc; /* Return code */
int i; /* Index */
unsigned int len; /* Uncompress length */
BYTE buf2[65536]; /* Uncompress/Decompress buf */
/* Special case for no compression */
if ((buf[0] & ~CCKD_COMPRESS_STRESSED) == CCKD_COMPRESS_NONE)
return cdsk_valid_trk (trk, buf, heads, maxlen, maxlen, NULL);
/* Build the track header */
memcpy (&buf2, buf, CKDDASD_TRKHDR_SIZE);
buf2[0] = 0;
for (i = CKDDASD_TRKHDR_SIZE + 8; i < maxlen; i++)
{
if ((buf[0] & ~CCKD_COMPRESS_STRESSED) == CCKD_COMPRESS_ZLIB)
{
len = 65536;
rc = uncompress (&buf2[CKDDASD_TRKHDR_SIZE], (uLongf *)&len,
&buf[CKDDASD_TRKHDR_SIZE], i);
//fprintf (stderr,"len=%d rc=%d\n",i,rc);
if (rc == Z_OK)
{
rc = cdsk_valid_trk (trk, (BYTE *)&buf2, heads,
len + CKDDASD_TRKHDR_SIZE, trksz, NULL);
if (rc == len + CKDDASD_TRKHDR_SIZE)
return CKDDASD_TRKHDR_SIZE + i;
}
}
#ifdef CCKD_BZIP2
else if ((buf[0] & ~CCKD_COMPRESS_STRESSED) == CCKD_COMPRESS_BZIP2)
{
len = 65536;
rc = BZ2_bzBuffToBuffDecompress ( &buf2[CKDDASD_TRKHDR_SIZE], &len,
&buf[CKDDASD_TRKHDR_SIZE], i, 0, 0);
//fprintf (stderr,"len=%d rc=%d\n",i,rc);
if (rc == BZ_OK)
{
rc = cdsk_valid_trk (trk, (BYTE *)&buf2, heads,
len + CKDDASD_TRKHDR_SIZE, trksz, NULL);
if (rc == len + CKDDASD_TRKHDR_SIZE)
return CKDDASD_TRKHDR_SIZE + i;
}
}
#endif
else break;
}
return -1;
}
/*-------------------------------------------------------------------*/
/* build gap table */
/*-------------------------------------------------------------------*/
int cdsk_build_gap (SPCTAB *spc, int *n, SPCTAB *gap)
{
int i, gaps, s;
s = *n; /* size of space table */
/* sort the space table by offset */
qsort ((void *)spc, s, sizeof(SPCTAB), cdsk_spctab_comp);
/* remove null entries from the end */
for ( ; spc[s-1].typ == SPCTAB_NONE; s--);
/* build the gap table */
for (i = gaps = 0; i < s - 1; i++)
{
if (spc[i].pos + spc[i].siz < spc[i+1].pos)
{
gap[gaps].pos = spc[i].pos + spc[i].siz;
gap[gaps++].siz = spc[i+1].pos - (spc[i].pos + spc[i].siz);
}
}
*n = s;
return gaps;
}
/*-------------------------------------------------------------------*/
/* build gap table (but ignore short gaps) */
/*-------------------------------------------------------------------*/
int cdsk_build_gap_long (SPCTAB *spc, int *n, SPCTAB *gap)
{
int i, gaps, s, siz;
s = *n; /* size of space table */
/* sort the space table by offset */
qsort ((void *)spc, s, sizeof(SPCTAB), cdsk_spctab_comp);
/* remove null entries from the end */
for ( ; spc[s-1].typ == SPCTAB_NONE; s--);
/* build the gap table */
for (i = gaps = 0; i < s - 1; i++)
{
if (spc[i].pos + spc[i].siz < spc[i+1].pos)
{
siz = spc[i+1].pos - (spc[i].pos + spc[i].siz);
if (siz >= CCKD_FREEBLK_SIZE)
{
gap[gaps].pos = spc[i].pos + spc[i].siz;
gap[gaps++].siz = siz;
}
}
}
*n = s;
return gaps;
}