Files
gottfriedleibniz d79382bbfe Run codespell on README, build, and source files. (#824)
* chore: run codespell on source/header files

Fixes most reported typos discovered by codespell (while trying to be
unopinionated about American vs. British English).

* chore: run codespell on build/readme files
2026-04-10 11:01:50 -07:00

902 lines
37 KiB
C

/* DASDCONV64.C (C) Copyright Roger Bowler, 1999-2012 */
/* Hercules DASD Utilities: DASD image converter */
/* */
/* Released under "The Q Public License Version 1" */
/* (http://www.hercules-390.org/herclic.html) as modifications to */
/* Hercules. */
/*-------------------------------------------------------------------*/
/* This program converts a CKD disk image from HDR-30 format */
/* to 64-bit AWSCKD format used by Hercules. */
/* */
/* The program is invoked from the shell prompt using the command: */
/* */
/* dasdconv64 [options] infile outfile */
/* */
/* options -r means overwrite existing outfile */
/* -q means suppress progress messages */
/* -lfs creates one large output file (if supported) */
/* infile is the name of the HDR-30 format CKD image file */
/* ("-" means that the CKD image is read from stdin) */
/* If this module was compiled with HAVE_ZLIB option */
/* activated, then the input file may be compressed */
/* or uncompressed. Otherwise it must be uncompressed. */
/* outfile is the name of the AWSCKD image file to be created. */
/* If the image exceeds 2GB then multiple files will */
/* be created, with names suffixed _1, _2, etc. */
/* (except if the underlying file system supports files */
/* larger than 2GB and the -lfs option is specified). */
/* This program will not overwrite an existing file. */
/*-------------------------------------------------------------------*/
#include "hstdinc.h"
#include "hercules.h"
#include "dasdblks.h"
#include "opcode.h"
#include "ccwarn.h"
#define UTILITY_NAME "dasdconv64"
#define UTILITY_DESC "DASD CKD64 image conversion"
/*-------------------------------------------------------------------*/
/* Definition of HDR-30 CKD image headers */
/*-------------------------------------------------------------------*/
typedef struct _H30CKD_TRKHDR { /* Track header */
HWORD devcode; /* Device type code */
BYTE resv02[14]; /* Reserved */
HWORD cyl; /* Cylinder number */
HWORD head; /* Head number */
BYTE resv14[28]; /* Reserved */
} H30CKD_TRKHDR;
typedef struct _H30CKD_RECHDR { /* Record header */
FWORD resv00; /* Reserved */
HWORD cyl; /* Cylinder number */
HWORD head; /* Head number */
BYTE rec; /* Record number */
BYTE klen; /* Key length */
HWORD dlen; /* Data length */
} H30CKD_RECHDR;
#define H30CKD_TRKHDR_SIZE ((S64)sizeof(H30CKD_TRKHDR))
#define H30CKD_RECHDR_SIZE ((S64)sizeof(H30CKD_RECHDR))
#define EXIT(rc) delayed_exit(rc) /* (use this macro to exit) */
/*-------------------------------------------------------------------*/
/* Static data areas */
/*-------------------------------------------------------------------*/
BYTE twelvehex00[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
BYTE gz_magic_id[] = {0x1F, 0x8B};
BYTE ckd_ident[] = {0x43, 0x4B, 0x44, 0x5F}; /* CKD_ in ASCII */
/*-------------------------------------------------------------------*/
/* Definition of file descriptor for gzip and non-gzip builds */
/*-------------------------------------------------------------------*/
#if defined( HAVE_ZLIB )
#define IFD gzFile
#define IFREAD gzread
#define IFCLOS gzclose
#else /*!defined( HAVE_ZLIB )*/
#define IFD int
#define IFREAD read
#define IFCLOS close
#endif /*!defined( HAVE_ZLIB )*/
static void delayed_exit (int exit_code);
static void argexit ( int code, char *pgm );
static void read_input_data (IFD ifd, char *ifname, BYTE *buf, int reqlen, U64 offset);
static int find_input_record (BYTE *buf, BYTE **ppbuf, int *plen,
BYTE *pkl, BYTE **pkp, U16 *pdl, BYTE **pdp,
U32 *pcc, U32 *phh, BYTE *prn);
static IFD open_input_image (char *ifname, U16 *devt, U32 *vcyls,
U32 *itrkl, BYTE **itrkb, BYTE *volser);
static void convert_ckd_file (IFD ifd, char *ifname, int itrklen, BYTE *itrkbuf,
int repl, int quiet,
char *ofname, int fseqn, U16 devtype, U32 heads,
U32 trksize, BYTE *obuf, U32 start, U32 end,
U32 volcyls, BYTE *volser);
static void convert_ckd (int lfs, IFD ifd, char *ifname, int itrklen,
BYTE *itrkbuf, int repl, int quiet,
char *ofname, U16 devtype, U32 heads,
U32 maxdlen, U32 volcyls, BYTE *volser);
/*-------------------------------------------------------------------*/
/* DASDCONV64 program main entry point */
/*-------------------------------------------------------------------*/
int main ( int argc, char *argv[] )
{
char *pgm; /* less any extension (.ext) */
IFD ifd; /* Input file descriptor */
int repl = 0; /* 1=replace existing file */
int quiet = 0; /* 1=suppress progress msgs */
BYTE *itrkbuf; /* -> Input track buffer */
U32 itrklen; /* Input track length */
U32 volcyls; /* Total cylinders on volume */
U32 heads = 0; /* Number of tracks/cylinder */
U32 maxdlen = 0; /* Maximum R1 data length */
U16 devtype; /* Device type */
char ifname[256]; /* Input file name */
char ofname[256]; /* Output file name */
BYTE volser[7]; /* Volume serial (ASCIIZ) */
int lfs = 0; /* 1 = Build large file */
INITIALIZE_UTILITY( UTILITY_NAME, UTILITY_DESC, &pgm );
/* Process the options in the argument list */
for (; argc > 1; argc--, argv++)
{
if (strcmp(argv[1], "-") == 0) break;
if (argv[1][0] != '-') break;
if (strcmp(argv[1], "-r") == 0)
repl = 1;
else if (strcmp(argv[1], "-q") == 0)
quiet = 1;
else
if (sizeof(off_t) > 4 && strcmp(argv[1], "-lfs") == 0)
lfs = 1;
else
argexit(5, pgm);
}
if (argc != 3)
argexit(5, pgm);
/* The first argument is the input file name */
if (argv[1] == NULL || strlen(argv[1]) == 0
|| strlen(argv[1]) > sizeof(ifname)-1)
argexit(1, pgm);
STRLCPY( ifname, argv[1] );
/* The second argument is the output file name */
if (argv[2] == NULL || strlen(argv[2]) == 0
|| strlen(argv[2]) > sizeof(ofname)-1)
argexit(2, pgm);
STRLCPY( ofname, argv[2] );
/* Read the first track of the input file, and determine
the device type and size from the track header */
ifd = open_input_image (ifname, &devtype, &volcyls,
&itrklen, &itrkbuf, volser);
/* Use the device type to determine track characteristics */
switch (devtype) {
case 0x2314: heads = 20; maxdlen = 7294; break;
case 0x3330: heads = 19; maxdlen = 13030; break;
case 0x3340: heads = 12; maxdlen = 8368; break;
case 0x3350: heads = 30; maxdlen = 19069; break;
case 0x3375: heads = 12; maxdlen = 35616; break;
case 0x3380: heads = 15; maxdlen = 47476; break;
case 0x3390: heads = 15; maxdlen = 56664; break;
case 0x9345: heads = 15; maxdlen = 46456; break;
default:
// "Unknown device type %04X"
FWRMSG( stderr, HHC02416, "E", devtype );
EXIT(3);
} /* end switch(devtype) */
/* Create the device */
convert_ckd (lfs, ifd, ifname, itrklen, itrkbuf, repl, quiet,
ofname, devtype, heads, maxdlen, volcyls, volser);
/* Release the input buffer and close the input file */
free (itrkbuf);
IFCLOS (ifd);
/* Display completion message */
// "DASD operation completed"
WRMSG( HHC02423, "I" );
return 0;
} /* end function main */
/*-------------------------------------------------------------------*/
/* Subroutine to exit the program */
/*-------------------------------------------------------------------*/
static void delayed_exit (int exit_code)
{
/* Delay exiting is to give the system
* time to display the error message. */
USLEEP(100000);
exit(exit_code);
}
/*-------------------------------------------------------------------*/
/* Subroutine to display command syntax and exit */
/*-------------------------------------------------------------------*/
static void
argexit ( int code, char *pgm )
{
// "Usage: %s ...
if (sizeof(off_t) > 4)
WRMSG( HHC02410, "I", pgm,
"\nHHC02410I -lfs build one large output file" );
else
WRMSG( HHC02410, "I", pgm, "" );
EXIT(code);
} /* end function argexit */
/*-------------------------------------------------------------------*/
/* Subroutine to read data from input file */
/* Input: */
/* ifd Input file descriptor */
/* ifname Input file name */
/* buf Address of input buffer */
/* reqlen Number of bytes requested */
/* offset Current offset in file (for error message only) */
/*-------------------------------------------------------------------*/
static void
read_input_data (IFD ifd, char *ifname, BYTE *buf, int reqlen,
U64 offset)
{
int rc; /* Return code */
int len = 0; /* Number of bytes read */
UNREFERENCED(ifname);
UNREFERENCED(offset);
while (len < reqlen)
{
rc = IFREAD (ifd, buf + len, reqlen - len);
if (rc == 0) break;
if (rc < 0)
{
// "Error in function %s: %s"
#if defined( HAVE_ZLIB )
FWRMSG( stderr, HHC02412, "E", "gzread()", strerror( errno ));
#else
FWRMSG( stderr, HHC02412, "E", "read()", strerror( errno ));
#endif
EXIT(3);
}
len += rc;
} /* end while */
if (len < reqlen)
{
// "Error in function %s: %s"
#if defined( HAVE_ZLIB )
FWRMSG( stderr, HHC02412, "E", "gzread()", "unexpected end of file" );
#else
FWRMSG( stderr, HHC02412, "E", "read()", "unexpected end of file" );
#endif
EXIT(3);
}
} /* end function read_input_data */
/*-------------------------------------------------------------------*/
/* Subroutine to locate next record in HDR-30 CKD track image */
/* Input: */
/* buf Pointer to start of track image buffer */
/* Input+Output: */
/* ppbuf Pointer to current position in track image buffer */
/* plen Length remaining in track image buffer */
/* Output: */
/* pkl Key length */
/* pkp Address of record key */
/* pdl Data length */
/* pdp Address of record data */
/* pcc Cylinder number */
/* phh Head number */
/* prn Record number */
/* Return value: */
/* 0=OK, 1=End of track, >1=Track format error detected */
/*-------------------------------------------------------------------*/
static int
find_input_record (BYTE *buf, BYTE **ppbuf, int *plen,
BYTE *pkl, BYTE **pkp, U16 *pdl, BYTE **pdp,
U32 *pcc, U32 *phh, BYTE *prn)
{
H30CKD_RECHDR *hrec; /* Input record header */
U16 dlen; /* Data length */
BYTE klen; /* Key length */
int n; /* Integer work area */
UNREFERENCED(buf);
/* End of track if not enough bytes remain in buffer */
if (*plen < H30CKD_RECHDR_SIZE) return 1;
/* Point to record header */
hrec = (H30CKD_RECHDR*)(*ppbuf);
/* End of track if record header is all zero */
if (memcmp(*ppbuf, twelvehex00, 12) == 0) return 1;
/* Extract the key length and data length */
klen = hrec->klen;
FETCH_HW (dlen, hrec->dlen);
/* Check that the reserved bytes are all zero */
if (memcmp(hrec->resv00, twelvehex00, sizeof(hrec->resv00)) != 0)
return 2;
/* Check that the key and data do not overflow the buffer */
if (*plen < H30CKD_RECHDR_SIZE + klen + dlen)
return 3;
/* Return the cylinder, head, and record number */
FETCH_HW (*pcc, hrec->cyl);
FETCH_HW (*phh, hrec->head);
*prn = hrec->rec;
/* Point past the record header to the key */
*plen -= H30CKD_RECHDR_SIZE;
*ppbuf += H30CKD_RECHDR_SIZE;
/* Return the key length and key pointer */
*pkl = klen;
*pkp = *ppbuf;
/* Point past the key to the data */
*plen -= klen;
*ppbuf += klen;
/* Return the data length and data pointer */
*pdl = dlen;
*pdp = *ppbuf;
/* Point past the data to the next record header */
*plen -= dlen;
*ppbuf += dlen;
/* Ensure next header starts on a fullword boundary */
if ((klen + dlen) & 3)
{
n = 4 - ((klen + dlen) % 4);
*plen -= n;
*ppbuf += n;
}
return 0;
} /* end function find_input_record */
/*-------------------------------------------------------------------*/
/* Subroutine to open input HDR-30 CKD image file */
/* Input: */
/* ifname Input HDR-30 CKD image file name */
/* Output: */
/* devt Device type */
/* vcyls Number of primary cylinders on volume */
/* itrkl Input HDR-30 CKD image track length */
/* itrkb -> Track image buffer (containing 1st track image) */
/* volser Volume serial number (6 bytes ASCII + X'00') */
/* Return value: */
/* Input file descriptor */
/*-------------------------------------------------------------------*/
static IFD
open_input_image (char *ifname, U16 *devt, U32 *vcyls,
U32 *itrkl, BYTE **itrkb, BYTE *volser)
{
int rc; /* Return code */
H30CKD_TRKHDR h30trkhdr; /* Input track header */
IFD ifd; /* Input file descriptor */
int len; /* Length of input */
U16 code; /* Device type code */
U16 dt = 0; /* Device type */
U32 cyls; /* Device size (pri+alt cyls)*/
U32 alts; /* Number of alternate cyls */
BYTE *itrkbuf; /* -> Input track buffer */
U32 itrklen = 0; /* Input track length */
BYTE *pbuf; /* Current byte in input buf */
BYTE klen; /* Key length */
U16 dlen; /* Data length */
BYTE *kptr; /* -> Key in input buffer */
BYTE *dptr; /* -> Data in input buffer */
U32 cyl; /* Cylinder number */
U32 head; /* Head number */
BYTE rec; /* Record number */
char pathname[MAX_PATH]; /* file path in host format */
hostpath(pathname, (char *)ifname, sizeof(pathname));
/* Open the HDR-30 CKD image file */
#if defined( HAVE_ZLIB )
if (strcmp(ifname, "-") == 0)
ifd = gzdopen (STDIN_FILENO, "rb");
else
ifd = gzopen (pathname, "rb");
if (ifd == NULL)
{
// "Error in function %s: %s"
FWRMSG( stderr, HHC02412, "E", "gzopen()", strerror( errno ));
EXIT(3);
}
#else /*!defined( HAVE_ZLIB )*/
if (strcmp(ifname, "-") == 0)
ifd = STDIN_FILENO;
else
{
ifd = HOPEN (pathname, O_RDONLY | O_BINARY);
if (ifd < 0)
{
// "Error in function %s: %s"
FWRMSG( stderr, HHC02412, "E", "open()", strerror( errno ));
EXIT(3);
}
}
#endif /*!defined( HAVE_ZLIB )*/
/* Read the first track header */
read_input_data (ifd, ifname, (BYTE*)&h30trkhdr,
H30CKD_TRKHDR_SIZE, 0);
#if !defined( HAVE_ZLIB )
/* Reject input if compressed and we lack gzip support */
if (memcmp(h30trkhdr.devcode, gz_magic_id, sizeof(gz_magic_id)) == 0)
{
// "Dasdconv64 is compiled without compress support and input is compressed"
FWRMSG( stderr, HHC02413, "E" );
EXIT(3);
}
#endif /*!defined( HAVE_ZLIB )*/
/* Reject input if it is already in CKD or CCKD format */
if (memcmp((BYTE*)&h30trkhdr, ckd_ident, sizeof(ckd_ident)) == 0)
{
// "Input file is already in CKD format, use dasdcopy64"
WRMSG( HHC02414, "I" );
EXIT(3);
}
/* Extract the device type code from the track header */
FETCH_HW (code, h30trkhdr.devcode);
/* Determine the input device type and size from the device code */
switch (code) {
case 0x01: dt=0x3330; cyls=411; alts=7; break; /* 3330 */
case 0x02: dt=0x3330; cyls=815; alts=7; break; /* 3330-11 */
case 0x03: dt=0x3340; cyls=351; alts=1; break; /* 3340-35 */
case 0x04: dt=0x3340; cyls=701; alts=1; break; /* 3340-70 */
case 0x05: dt=0x3350; cyls=562; alts=7; break; /* 3350 */
case 0x06: dt=0x3375; cyls=962; alts=3; break; /* 3375 */
case 0x08: dt=0x3380; cyls=888; alts=3; break; /* 3380-A,D,J*/
case 0x09: dt=0x3380; cyls=1774; alts=4; break; /* 3380-E */
case 0x0A: dt=0x3380; cyls=2660; alts=5; break; /* 3380-K */
case 0x0B: dt=0x3390; cyls=1117; alts=4; break; /* 3390-1 */
case 0x0C: dt=0x3390; cyls=2230; alts=4; break; /* 3390-2 */
case 0x0D: dt=0x3390; cyls=3343; alts=4; break; /* 3390-3 */
case 0x12: dt=0x2314; cyls=203; alts=3; break; /* 2314 */
case 0x13: dt=0x3390; cyls=10038; alts=21; break; /* 3390-9 */
case 0x14: dt=0x9345; cyls=1454; alts=14; break; /* 9345-1 */
case 0x15: dt=0x9345; cyls=2170; alts=14; break; /* 9345-2 */
default:
// "Unknown device type %04X at offset 00000000 in input file"
FWRMSG( stderr, HHC02415, "E", code );
EXIT(3);
} /* end switch(code) */
/* Use the device type to determine the input image track size */
switch (dt) {
case 0x2314: itrklen = 0x2000; break;
case 0x3330: itrklen = 0x3400; break;
case 0x3340: itrklen = 0x2400; break;
case 0x3350: itrklen = 0x4C00; break;
case 0x3375: itrklen = 0x9000; break;
case 0x3380: itrklen = 0xBC00; break;
case 0x3390: itrklen = 0xE400; break;
case 0x9345: itrklen = 0xBC00; break;
default:
// "Unknown device type %04X"
FWRMSG( stderr, HHC02416, "E", dt );
EXIT(3);
} /* end switch(dt) */
/* Obtain the input track buffer */
itrkbuf = malloc (itrklen);
if (itrkbuf == NULL)
{
char buf[40];
MSGBUF( buf, "malloc(%u)", itrklen);
// "Error in function %s: %s"
FWRMSG( stderr, HHC02412, "E", buf, strerror( errno ));
EXIT(3);
}
/* Copy the first track header to the input track buffer */
memcpy (itrkbuf, &h30trkhdr, H30CKD_TRKHDR_SIZE);
/* Read the remainder of the first track into the buffer */
read_input_data (ifd, ifname,
itrkbuf + H30CKD_TRKHDR_SIZE,
itrklen - H30CKD_TRKHDR_SIZE,
H30CKD_TRKHDR_SIZE);
/* Initialize the volume serial number */
strlcpy( (char*) volser, "(NONE)", VOLSER_LEN+1 );
/* Search for volume label in record 3 of first track */
pbuf = itrkbuf + H30CKD_TRKHDR_SIZE;
len = itrklen - H30CKD_TRKHDR_SIZE;
while (1)
{
/* Find next input record */
rc = find_input_record (itrkbuf, &pbuf, &len,
&klen, &kptr, &dlen, &dptr,
&cyl, &head, &rec);
/* Give up if error or end of track */
if (rc != 0) break;
/* Process when record 3 is found */
if (cyl == 0 && head == 0 && rec == 3)
{
/* Extract volser if it is a volume label */
if (klen == 4 && memcmp( kptr, VOL1_KEY, VOL1_KEYLEN ) == 0
&& dlen == 80 && memcmp( dptr, VOL1_KEY, VOL1_KEYLEN ) == 0)
make_asciiz( (char*) volser, VOLSER_LEN+1, dptr + VOL1_KEYLEN, VOLSER_LEN );
break;
}
} /* end while */
/* Set output variables and return the input file descriptor */
*devt = dt;
*vcyls = cyls - alts;
*itrkl = itrklen;
*itrkb = itrkbuf;
return ifd;
} /* end function open_input_image */
/*-------------------------------------------------------------------*/
/* Subroutine to create an AWSCKD DASD image file */
/* Input: */
/* ifd Input HDR-30 image file descriptor */
/* ifname Input file name */
/* itrklen Length of input track buffer */
/* itrkbuf Address of input track buffer */
/* repl 1=replace existing file, 0=do not replace */
/* ofname Output AWSCKD file name */
/* fseqn Sequence number of this file (1=first) */
/* devtype Device type */
/* heads Number of heads per cylinder */
/* trksize AWSCKD image track length */
/* obuf Address of output AWSCKD track image buffer */
/* start Starting cylinder number for this file */
/* end Ending cylinder number for this file */
/* volcyls Total number of cylinders on volume */
/* volser Volume serial number */
/*-------------------------------------------------------------------*/
static void
convert_ckd_file (IFD ifd, char *ifname, int itrklen, BYTE *itrkbuf,
int repl, int quiet,
char *ofname, int fseqn, U16 devtype, U32 heads,
U32 trksize, BYTE *obuf, U32 start, U32 end,
U32 volcyls, BYTE *volser)
{
int rc; /* Return code */
int ofd; /* Output file descriptor */
CKD_DEVHDR devhdr; /* Output device header */
CKD_TRKHDR *trkhdr; /* -> Output track header */
CKD_RECHDR *rechdr; /* -> Output record header */
U32 cyl; /* Cylinder number */
U32 head; /* Head number */
BYTE fileseq; /* CKD header sequence number*/
int highcyl; /* CKD header high cyl number*/
BYTE *opos; /* -> Byte in output buffer */
BYTE klen; /* Key length */
U16 dlen; /* Data length */
BYTE rec; /* Record number */
BYTE *iptr; /* -> Byte in input buffer */
BYTE *kptr; /* -> Key in input buffer */
BYTE *dptr; /* -> Data in input buffer */
int ilen; /* Bytes left in input buffer*/
H30CKD_TRKHDR *ith; /* -> Input track header */
U32 ihc, ihh; /* Input trk header cyl,head */
U64 offset; /* Current input file offset */
char pathname[MAX_PATH]; /* file path in host format */
UNREFERENCED(volser);
/* Set file sequence number to zero if this is the only file */
if (fseqn == 1 && end + 1 == volcyls)
fileseq = 0;
else
fileseq = (BYTE) fseqn;
/* Set high cylinder number to zero if this is the last file */
if (end + 1 == volcyls)
highcyl = 0;
else
highcyl = end;
/* Create the AWSCKD image file */
hostpath(pathname, (char *)ofname, sizeof(pathname));
ofd = HOPEN (pathname,
O_WRONLY | O_CREAT | O_BINARY | (repl ? 0 : O_EXCL),
S_IRUSR | S_IWUSR | S_IRGRP);
if (ofd < 0)
{
// "Error in function %s: %s"
FWRMSG( stderr, HHC02412, "E", "open()", strerror( errno ));
EXIT(8);
}
/* Create the device header */
memset( &devhdr, 0, CKD_DEVHDR_SIZE );
memcpy( devhdr.dh_devid, dh_devid_str( CKD_P064_TYP ), 8 );
STORE_LE_FW( devhdr.dh_heads, heads );
STORE_LE_FW( devhdr.dh_trksize, trksize );
devhdr.dh_devtyp = devtype & 0xFF;
devhdr.dh_fileseq = fileseq & 0xFF;
STORE_LE_HW( devhdr.dh_highcyl, (U16) highcyl );
/* Write the device header */
rc = write (ofd, &devhdr, CKD_DEVHDR_SIZE);
if (rc < CKD_DEVHDR_SIZE)
{
// "Error in function %s: %s"
FWRMSG( stderr, HHC02412, "E", "write()", errno ? strerror(errno) : "incomplete" );
EXIT(1);
}
/* Write each cylinder */
for (cyl = start; cyl <= end; cyl++)
{
/* Display progress message every 10 cylinders */
if ((cyl % 10) == 0)
{
EXTGUIMSG( "CYL=%u\n", cyl );
if (!quiet)
fprintf (stderr, "Writing cylinder %u\r", cyl);
}
for (head = 0; head < heads; head++)
{
/* Calculate the current offset in the file */
offset = (((U64)cyl*heads)+head)*itrklen;
/* Read the input track image (except cyl 0 head 0
already read by the open_input_image procedure) */
if (cyl > 0 || head > 0)
{
read_input_data (ifd, ifname,
itrkbuf, itrklen,
offset);
} /* end if(cyl>0||head>0) */
/* Validate the track header */
ith = (H30CKD_TRKHDR*)itrkbuf;
FETCH_HW (ihc, ith->cyl);
FETCH_HW (ihh, ith->head);
if (ihc != cyl || ihh != head)
{
// "Invalid track header at offset 0x%16.16"PRIX64
FWRMSG( stderr, HHC02417, "E", offset );
// "Expected CCHH %04X%04X, found CCHH %04X%04X"
FWRMSG( stderr, HHC02418, "E", cyl, head, ihc, ihh );
EXIT(8);
}
/* Clear the output track image to zeroes */
memset (obuf, 0, trksize);
/* Build the output track header */
trkhdr = (CKD_TRKHDR*)obuf;
trkhdr->bin = 0;
STORE_HW (trkhdr->cyl, (U16) cyl);
STORE_HW (trkhdr->head, (U16) head);
opos = obuf + CKD_TRKHDR_SIZE;
/* Copy each record from the input buffer */
iptr = itrkbuf + H30CKD_TRKHDR_SIZE;
ilen = itrklen - H30CKD_TRKHDR_SIZE;
while (1)
{
/* Locate the next input record */
rc = find_input_record (itrkbuf, &iptr, &ilen,
&klen, &kptr, &dlen, &dptr,
&ihc, &ihh, &rec);
/* Exit at end of track */
if (rc == 1) break;
/* Error if invalid record header detected */
if (rc > 1)
{
// "Invalid record header (rc %d) at offset %04X in trk at CCHH %04X%04X at offset 0x%16.16"PRIX64" in file %s"
FWRMSG( stderr, HHC02419, "E",
rc, (unsigned int)(iptr-itrkbuf), cyl, head,
offset, ifname );
EXIT(9);
}
/* Build AWSCKD record header in output buffer */
rechdr = (CKD_RECHDR*)opos;
opos += CKD_RECHDR_SIZE;
STORE_HW (rechdr->cyl, (U16) ihc);
STORE_HW (rechdr->head, (U16) ihh);
rechdr->rec = rec;
rechdr->klen = klen;
STORE_HW (rechdr->dlen, dlen);
/* Copy key and data to output buffer */
if (klen != 0)
{
memcpy (opos, kptr, klen);
opos += klen;
}
if (dlen != 0)
{
memcpy (opos, dptr, dlen);
opos += dlen;
}
} /* end while */
/* Build the end of track marker */
memcpy( opos, &CKD_ENDTRK, CKD_ENDTRK_SIZE );
/* Write the track to the file */
rc = write (ofd, obuf, trksize);
if (rc < 0 || (U32)rc < trksize)
{
// "Error in function %s: %s"
FWRMSG( stderr, HHC02412, "E", "write()",
errno ? strerror(errno) : "incomplete" );
EXIT(1);
}
} /* end for(head) */
} /* end for(cyl) */
/* Close the AWSCKD image file */
rc = close (ofd);
if (rc < 0)
{
// "Error in function %s: %s"
FWRMSG( stderr, HHC02412, "E", "close()", strerror( errno ));
EXIT(10);
}
/* Display completion message */
// "%u cylinders successfully written to file %s"
WRMSG( HHC02420, "I", cyl - start, ofname );
} /* end function convert_ckd_file */
/*-------------------------------------------------------------------*/
/* Subroutine to create an AWSCKD DASD image */
/* Input: */
/* lfs Build one large output file */
/* ifd Input HDR-30 image file descriptor */
/* ifname Input file name */
/* itrklen Length of input track buffer */
/* itrkbuf Address of input track buffer */
/* repl 1=replace existing file, 0=do not replace */
/* ofname Output AWSCKD image file name */
/* devtype Device type */
/* heads Number of heads per cylinder */
/* maxdlen Maximum R1 record data length */
/* volcyls Total number of cylinders on volume */
/* volser Volume serial number */
/* */
/* If the total number of cylinders exceeds the capacity of a 2GB */
/* file, then multiple CKD image files will be created, with the */
/* suffix _1, _2, etc suffixed to the specified file name. */
/* Otherwise a single file is created without a suffix. */
/*-------------------------------------------------------------------*/
static void
convert_ckd (int lfs, IFD ifd, char *ifname, int itrklen,
BYTE *itrkbuf, int repl, int quiet,
char *ofname, U16 devtype, U32 heads,
U32 maxdlen, U32 volcyls, BYTE *volser)
{
int i; /* Array subscript */
char *s; /* String pointer */
int fileseq; /* File sequence number */
char sfname[260]; /* Suffixed name of this file*/
char *suffix; /* -> Suffix character */
U32 endcyl; /* Last cylinder of this file*/
U32 cyl; /* Cylinder number */
U32 cylsize; /* Cylinder size in bytes */
BYTE *obuf; /* -> Output track buffer */
U32 mincyls; /* Minimum cylinder count */
U32 maxcyls; /* Maximum cylinder count */
U32 maxcpif; /* Maximum number of cylinders
in each CKD image file */
U32 trksize; /* AWSCKD image track length */
/* Compute the AWSCKD image track length */
trksize = CKD_TRKHDR_SIZE
+ CKD_R0_SIZE + CKD_R0_DLEN
+ CKD_RECHDR_SIZE + maxdlen
+ CKD_ENDTRK_SIZE;
trksize = ROUND_UP(trksize,512);
/* Compute minimum and maximum number of cylinders */
cylsize = trksize * heads;
mincyls = 1;
if (!lfs)
{
maxcpif = 0x80000000 / cylsize;
maxcyls = maxcpif * CKD_MAXFILES;
}
else
maxcpif = maxcyls = volcyls;
if (maxcyls > 65536) maxcyls = 65536;
/* Check for valid number of cylinders */
if (volcyls < mincyls || volcyls > maxcyls)
{
// "Cylinder count %u is outside range %u-%u"
FWRMSG( stderr, HHC02421, "E",
volcyls, mincyls, maxcyls );
EXIT(4);
}
/* Obtain track data buffer */
obuf = malloc(trksize);
if (obuf == NULL)
{
char buf[40];
MSGBUF( buf, "malloc(%u)", trksize);
// "Error in function %s: %s"
FWRMSG( stderr, HHC02412, "E", buf, strerror( errno ));
EXIT(6);
}
/* Display progress message */
// "Converting %04X volume %s: %u cyls, %u trks/cyl, %u bytes/trk"
WRMSG( HHC02422, "I", devtype, volser, volcyls, heads, trksize );
EXTGUIMSG( "CYLS=%u\n", volcyls );
/* Copy the unsuffixed AWSCKD image file name */
STRLCPY( sfname, ofname );
suffix = NULL;
/* Create the suffixed file name if volume will exceed 2GB */
if (volcyls > maxcpif)
{
/* Look for last slash marking end of directory name */
s = strrchr (ofname, '/');
if (s == NULL) s = ofname;
/* Insert suffix before first dot in file name, or
append suffix to file name if there is no dot */
s = strchr (s, '.');
if (s != NULL)
{
i = (int) (s - ofname);
strlcpy( sfname + i, "_1", sizeof( sfname ) - i );
STRLCAT( sfname, ofname + i );
suffix = sfname + i + 1;
}
else
{
STRLCAT( sfname, "_1" );
suffix = sfname + strlen(sfname) - 1;
}
}
/* Create the AWSCKD image files */
for (cyl = 0, fileseq = 1; cyl < volcyls;
cyl += maxcpif, fileseq++)
{
/* Insert the file sequence number in the file name */
if (suffix) *suffix = (char)('0' + fileseq);
/* Calculate the ending cylinder for this file */
if (cyl + maxcpif < volcyls)
endcyl = cyl + maxcpif - 1;
else
endcyl = volcyls - 1;
/* Create an AWSCKD image file */
convert_ckd_file (ifd, ifname, itrklen, itrkbuf, repl, quiet,
sfname, fileseq, devtype, heads, trksize,
obuf, cyl, endcyl, volcyls, volser);
}
/* Release the output track buffer */
free (obuf);
} /* end function convert_ckd */