mirror of
https://github.com/SDL-Hercules-390/hyperion.git
synced 2026-04-16 08:55:23 +02:00
940 lines
37 KiB
C
940 lines
37 KiB
C
/* DASDCONV.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 the AWSCKD format used by Hercules. */
|
|
/* */
|
|
/* The program is invoked from the shell prompt using the command: */
|
|
/* */
|
|
/* dasdconv [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_LIBZ 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"
|
|
|
|
#define UTILITY_NAME "dasdconv"
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* 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 ((ssize_t)sizeof(H30CKD_TRKHDR))
|
|
#define H30CKD_RECHDR_SIZE ((ssize_t)sizeof(H30CKD_RECHDR))
|
|
#define EXIT(rc) delayed_exit(rc) /* (use this macro to exit) */
|
|
/*-------------------------------------------------------------------*/
|
|
/* Static data areas */
|
|
/*-------------------------------------------------------------------*/
|
|
BYTE eighthexFF[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
|
BYTE twelvehex00[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
BYTE ebcdicvol1[] = {0xE5, 0xD6, 0xD3, 0xF1};
|
|
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_LIBZ)
|
|
#define IFD gzFile
|
|
#define IFREAD gzread
|
|
#define IFCLOS gzclose
|
|
#else /*!defined(HAVE_LIBZ)*/
|
|
#define IFD int
|
|
#define IFREAD read
|
|
#define IFCLOS close
|
|
#endif /*!defined(HAVE_LIBZ)*/
|
|
|
|
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, U32 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);
|
|
/*-------------------------------------------------------------------*/
|
|
/* DASDCONV program main entry point */
|
|
/*-------------------------------------------------------------------*/
|
|
int main ( int argc, char *argv[] )
|
|
{
|
|
char *pgmname; /* prog name in host format */
|
|
char *pgm; /* less any extension (.ext) */
|
|
char msgbuf[512]; /* message build work area */
|
|
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 */
|
|
char *strtok_str = NULL;
|
|
|
|
/* Set program name */
|
|
if ( argc > 0 )
|
|
{
|
|
if ( strlen(argv[0]) == 0 )
|
|
{
|
|
pgmname = strdup( UTILITY_NAME );
|
|
}
|
|
else
|
|
{
|
|
char path[MAX_PATH];
|
|
#if defined( _MSVC_ )
|
|
GetModuleFileName( NULL, path, MAX_PATH );
|
|
#else
|
|
strncpy( path, argv[0], sizeof( path ) );
|
|
#endif
|
|
pgmname = strdup(basename(path));
|
|
#if !defined( _MSVC_ )
|
|
strncpy( path, argv[0], sizeof(path) );
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pgmname = strdup( UTILITY_NAME );
|
|
}
|
|
|
|
pgm = strtok_r( strdup(pgmname), ".", &strtok_str);
|
|
INITIALIZE_UTILITY( pgm );
|
|
|
|
/* Display the program identification message */
|
|
MSGBUF( msgbuf, MSG_C( HHC02499, "I", pgm, "DASD CKD image conversion" ) );
|
|
display_version (stderr, msgbuf+10, FALSE);
|
|
|
|
/* 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);
|
|
strcpy (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);
|
|
strcpy (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:
|
|
fprintf (stderr, MSG(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 */
|
|
fprintf (stderr, MSG(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 )
|
|
{
|
|
if (sizeof(off_t) > 4)
|
|
fprintf( stderr, MSG(HHC02410, "I", pgm,
|
|
"\n -lfs build one large output file"));
|
|
else
|
|
fprintf( stderr, MSG(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,
|
|
U32 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)
|
|
{
|
|
#if defined(HAVE_LIBZ)
|
|
fprintf (stderr, MSG(HHC02412, "E", "gzread()", strerror(errno)));
|
|
#else
|
|
fprintf (stderr, MSG(HHC02412, "E", "read()", strerror(errno)));
|
|
#endif
|
|
EXIT(3);
|
|
}
|
|
len += rc;
|
|
} /* end while */
|
|
|
|
if (len < reqlen)
|
|
{
|
|
#if defined(HAVE_LIBZ)
|
|
fprintf (stderr, MSG(HHC02412, "E", "gzread()", "unexpected end of file"));
|
|
#else
|
|
fprintf (stderr, MSG(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;
|
|
}
|
|
|
|
/* Issue progress message */
|
|
#if 0
|
|
fprintf (stderr,
|
|
"+%4.4X cyl=%4.4X head=%4.4X rec=%2.2X"
|
|
" kl=%2.2X dl=%4.4X trkbal=%d\n",
|
|
(BYTE*)hrec-buf, *pcc, *phh, *prn, klen, dlen, *plen);
|
|
#endif
|
|
|
|
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_LIBZ)
|
|
if (strcmp(ifname, "-") == 0)
|
|
ifd = gzdopen (STDIN_FILENO, "rb");
|
|
else
|
|
ifd = gzopen (pathname, "rb");
|
|
|
|
if (ifd == NULL)
|
|
{
|
|
fprintf (stderr, MSG(HHC02412, "E", "gzopen()", strerror(errno)));
|
|
EXIT(3);
|
|
}
|
|
#else /*!defined(HAVE_LIBZ)*/
|
|
if (strcmp(ifname, "-") == 0)
|
|
ifd = STDIN_FILENO;
|
|
else
|
|
{
|
|
ifd = HOPEN (pathname, O_RDONLY | O_BINARY);
|
|
|
|
if (ifd < 0)
|
|
{
|
|
fprintf (stderr, MSG(HHC02412, "E", "open()", strerror(errno)));
|
|
EXIT(3);
|
|
}
|
|
}
|
|
#endif /*!defined(HAVE_LIBZ)*/
|
|
|
|
/* Read the first track header */
|
|
read_input_data (ifd, ifname, (BYTE*)&h30trkhdr,
|
|
H30CKD_TRKHDR_SIZE, 0);
|
|
|
|
#if !defined(HAVE_LIBZ)
|
|
/* Reject input if compressed and we lack gzip support */
|
|
if (memcmp(h30trkhdr.devcode, gz_magic_id, sizeof(gz_magic_id)) == 0)
|
|
{
|
|
fprintf (stderr, MSG(HHC02413, "E"));
|
|
EXIT(3);
|
|
}
|
|
#endif /*!defined(HAVE_LIBZ)*/
|
|
|
|
/* Reject input if it is already in CKD or CCKD format */
|
|
if (memcmp((BYTE*)&h30trkhdr, ckd_ident, sizeof(ckd_ident)) == 0)
|
|
{
|
|
fprintf (stderr, MSG(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:
|
|
fprintf (stderr, MSG(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:
|
|
fprintf (stderr, MSG(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);
|
|
fprintf (stderr, MSG(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 */
|
|
strcpy ((char *)volser, "(NONE)");
|
|
|
|
/* 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, ebcdicvol1, 4) == 0
|
|
&& dlen == 80 && memcmp(dptr, ebcdicvol1, 4) == 0)
|
|
make_asciiz ((char *)volser, 7, dptr+4, 6);
|
|
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 */
|
|
CKDDASD_DEVHDR devhdr; /* Output device header */
|
|
CKDDASD_TRKHDR *trkhdr; /* -> Output track header */
|
|
CKDDASD_RECHDR *rechdr; /* -> Output record header */
|
|
U32 cyl; /* Cylinder number */
|
|
U32 head; /* Head number */
|
|
int 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 */
|
|
U32 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 = 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)
|
|
{
|
|
fprintf (stderr, MSG(HHC02412, "E", "open()", strerror(errno)));
|
|
EXIT(8);
|
|
}
|
|
|
|
/* Create the device header */
|
|
memset(&devhdr, 0, CKDDASD_DEVHDR_SIZE);
|
|
memcpy(devhdr.devid, "CKD_P370", 8);
|
|
devhdr.heads[3] = (heads >> 24) & 0xFF;
|
|
devhdr.heads[2] = (heads >> 16) & 0xFF;
|
|
devhdr.heads[1] = (heads >> 8) & 0xFF;
|
|
devhdr.heads[0] = heads & 0xFF;
|
|
devhdr.trksize[3] = (trksize >> 24) & 0xFF;
|
|
devhdr.trksize[2] = (trksize >> 16) & 0xFF;
|
|
devhdr.trksize[1] = (trksize >> 8) & 0xFF;
|
|
devhdr.trksize[0] = trksize & 0xFF;
|
|
devhdr.devtype = devtype & 0xFF;
|
|
devhdr.fileseq = fileseq;
|
|
devhdr.highcyl[1] = (highcyl >> 8) & 0xFF;
|
|
devhdr.highcyl[0] = highcyl & 0xFF;
|
|
|
|
/* Write the device header */
|
|
rc = write (ofd, &devhdr, CKDDASD_DEVHDR_SIZE);
|
|
if (rc < CKDDASD_DEVHDR_SIZE)
|
|
{
|
|
fprintf (stderr, MSG(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)
|
|
{
|
|
#ifdef EXTERNALGUI
|
|
if (extgui)
|
|
fprintf (stderr, "CYL=%u\n", cyl);
|
|
else
|
|
#endif /*EXTERNALGUI*/
|
|
if (quiet == 0)
|
|
fprintf (stderr, "Writing cylinder %u\r", cyl);
|
|
}
|
|
|
|
for (head = 0; head < heads; head++)
|
|
{
|
|
/* Calculate the current offset in the file */
|
|
offset = ((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)
|
|
{
|
|
fprintf (stderr, MSG(HHC02417, "E", offset));
|
|
fprintf (stderr, MSG(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 = (CKDDASD_TRKHDR*)obuf;
|
|
trkhdr->bin = 0;
|
|
STORE_HW (trkhdr->cyl, cyl);
|
|
STORE_HW (trkhdr->head, head);
|
|
opos = obuf + CKDDASD_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)
|
|
{
|
|
fprintf (stderr, MSG(HHC02419, "E",
|
|
rc, (unsigned int)(iptr-itrkbuf), cyl, head,
|
|
offset, ifname));
|
|
EXIT(9);
|
|
}
|
|
|
|
/* Build AWSCKD record header in output buffer */
|
|
rechdr = (CKDDASD_RECHDR*)opos;
|
|
opos += CKDDASD_RECHDR_SIZE;
|
|
STORE_HW (rechdr->cyl, ihc);
|
|
STORE_HW (rechdr->head, 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, eighthexFF, 8);
|
|
|
|
/* Write the track to the file */
|
|
rc = write (ofd, obuf, trksize);
|
|
if (rc < 0 || (U32)rc < trksize)
|
|
{
|
|
fprintf (stderr, MSG(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)
|
|
{
|
|
fprintf (stderr, MSG(HHC02412, "E", "close()", strerror(errno)));
|
|
EXIT(10);
|
|
}
|
|
|
|
/* Display completion message */
|
|
fprintf (stderr, MSG(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 */
|
|
int rec0len = 8; /* Length of R0 data */
|
|
U32 trksize; /* AWSCKD image track length */
|
|
|
|
/* Compute the AWSCKD image track length */
|
|
trksize = sizeof(CKDDASD_TRKHDR)
|
|
+ sizeof(CKDDASD_RECHDR) + rec0len
|
|
+ sizeof(CKDDASD_RECHDR) + maxdlen
|
|
+ sizeof(eighthexFF);
|
|
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)
|
|
{
|
|
fprintf (stderr, MSG(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);
|
|
fprintf (stderr, MSG(HHC02412, "E", buf, strerror(errno)));
|
|
EXIT(6);
|
|
}
|
|
|
|
/* Display progress message */
|
|
fprintf (stderr, MSG(HHC02422, "I",
|
|
devtype, volser, volcyls, heads, trksize));
|
|
#ifdef EXTERNALGUI
|
|
if (extgui)
|
|
fprintf (stderr, "CYLS=%u\n", volcyls);
|
|
#endif /*EXTERNALGUI*/
|
|
|
|
/* Copy the unsuffixed AWSCKD image file name */
|
|
strcpy (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 = s - ofname;
|
|
strcpy (sfname + i, "_1");
|
|
strlcat (sfname, ofname + i, sizeof(sfname));
|
|
suffix = sfname + i + 1;
|
|
}
|
|
else
|
|
{
|
|
strlcat (sfname, "_1", sizeof(sfname));
|
|
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 = '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 */
|
|
|
|
|