Files
org-hyperion-cules/dasdload.c
2014-10-27 05:21:50 -07:00

4505 lines
177 KiB
C

/* DASDLOAD.C (c) Copyright Roger Bowler, 1999-2012 */
/* (c) Copyright TurboHercules, SAS 2010-2011 */
/* Hercules DASD Utilities: DASD image loader */
/* */
/* Released under "The Q Public License Version 1" */
/* (http://www.hercules-390.org/herclic.html) as modifications to */
/* Hercules. */
/*-------------------------------------------------------------------*/
/* This program creates a virtual DASD volume from a list of */
/* datasets previously unloaded using the TSO XMIT command. */
/*-------------------------------------------------------------------*/
/*-------------------------------------------------------------------*/
/* Additional credits: */
/* Corrections to CVOL initialization logic by Jay Maynard */
/* IEBCOPY native dataset support by Ronen Tzur */
/* Standardized Messages by P. Gorlinsky */
/*-------------------------------------------------------------------*/
#include "hstdinc.h"
#include "hercules.h"
#include "dasdblks.h"
#define UTILITY_NAME "dasdload"
/*-------------------------------------------------------------------*/
/* Internal table sizes */
/*-------------------------------------------------------------------*/
#define MAXDBLK 10000 /* Maximum number of directory
blocks per dataset */
#define MAXTTR 50000 /* Maximum number of TTRs
per dataset */
#define MAXDSCB 1000 /* Maximum number of DSCBs */
/*-------------------------------------------------------------------*/
/* Internal macro definitions */
/*-------------------------------------------------------------------*/
#define CASERET(s) case s: return (#s)
#define XMINF info_msg
#define XMINFF info_msg
#define XMERR printf
#define XMERRF printf
#define R0_DATALEN 8
#define IPL1_KEYLEN 4
#define IPL1_DATALEN 24
#define IPL2_KEYLEN 4
#define IPL2_DATALEN 144
#define VOL1_KEYLEN 4
#define VOL1_DATALEN 80
#define EBCDIC_END "\xC5\xD5\xC4"
#define EBCDIC_TXT "\xE3\xE7\xE3"
/*-------------------------------------------------------------------*/
/* Definition of LOGREC header record */
/*-------------------------------------------------------------------*/
typedef struct _DIPHDR {
HWORD recid; /* Record identifier (0xFFFF)*/
HWORD bcyl; /* Extent begin cylinder */
HWORD btrk; /* Extent begin track */
HWORD ecyl; /* Extent end cylinder */
HWORD etrk; /* Extent end track */
BYTE resv; /* Unused */
BYTE restart[7]; /* Restart area BBCCHHR */
HWORD trkbal; /* Bytes remaining on track */
HWORD trklen; /* Total bytes on track */
BYTE reused[7]; /* Last reused BBCCHHR */
HWORD lasthead; /* Last track on cylinder */
HWORD trklen90; /* 90% of track length */
BYTE devcode; /* Device type code */
BYTE cchh90[4]; /* 90% full track CCHH */
BYTE switches; /* Switches */
BYTE endid; /* Check byte (0xFF) */
} DIPHDR;
/*-------------------------------------------------------------------*/
/* Definition of internal extent descriptor array entry */
/*-------------------------------------------------------------------*/
typedef struct _EXTDESC {
U16 bcyl; /* Begin cylinder */
U16 btrk; /* Begin track */
U16 ecyl; /* End cylinder */
U16 etrk; /* End track */
U16 ntrk; /* Number of tracks */
} EXTDESC;
/*-------------------------------------------------------------------*/
/* Definition of internal TTR conversion table array entry */
/*-------------------------------------------------------------------*/
typedef struct _TTRCONV {
BYTE origttr[3]; /* TTR in original dataset */
BYTE outpttr[3]; /* TTR in output dataset */
} TTRCONV;
/*-------------------------------------------------------------------*/
/* Definitions for dataset initialization methods */
/*-------------------------------------------------------------------*/
#define METHOD_EMPTY 0
#define METHOD_XMIT 1
#define METHOD_DIP 2
#define METHOD_CVOL 3
#define METHOD_VTOC 4
#define METHOD_VS 5
#define METHOD_SEQ 6
/*-------------------------------------------------------------------*/
/* Static data areas */
/*-------------------------------------------------------------------*/
static 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 cvol_low_key[] = {0, 0, 0, 0, 0, 0, 0, 1};
BYTE iplpsw[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
BYTE iplccw1[8] = {0x06, 0x00, 0x3A, 0x98, 0x60, 0x00, 0x00, 0x60};
BYTE iplccw2[8] = {0x08, 0x00, 0x3A, 0x98, 0x00, 0x00, 0x00, 0x00};
BYTE ipl2data[] = {0x07, 0x00, 0x3A, 0xB8, 0x40, 0x00, 0x00, 0x06,
0x31, 0x00, 0x3A, 0xBE, 0x40, 0x00, 0x00, 0x05,
0x08, 0x00, 0x3A, 0xA0, 0x00, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x7f, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*BBCCHH*/
0x00, 0x00, 0x00, 0x00, 0x04}; /*CCHHR*/
BYTE noiplpsw[8] = {0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F};
BYTE noiplccw1[8] = {0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
BYTE noiplccw2[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
/* Information message level: 0=None, 1=File name, 2=File information,
3=Member information, 4=Text units, record headers, 5=Dump data */
int infolvl = 1;
/*-------------------------------------------------------------------*/
/* Subroutine to display command syntax and exit */
/*-------------------------------------------------------------------*/
static void
argexit ( int code, char *pgm )
{
char usage[512];
char buflfs[64];
#ifdef CCKD_COMPRESS_ZLIB
char *bufz = " -z compress using zlib [default]\n";
#else
char *bufz = "";
#endif
#ifdef CCKD_COMPRESS_BZIP2
char *bufbz = " -bz2 compress using bzip2\n";
#else
char *bufbz = "";
#endif
strncpy( buflfs,
(sizeof(off_t) > 4) ?
" -lfs create single large output file\n" : "",
sizeof( buflfs));
MSGBUF( usage ,MSG( HHC02496, "I", pgm, bufz, bufbz, buflfs ) );
fprintf( stderr, "%s", usage );
exit(code);
} /* end function argexit */
/*-------------------------------------------------------------------*/
/* Subroutine to display an informational message */
/*-------------------------------------------------------------------*/
static void
info_msg (int lvl, char *msg, ...)
{
va_list vl;
if (infolvl >= lvl)
{
va_start(vl, msg);
vprintf (msg, vl);
}
} /* end function info_msg */
/*-------------------------------------------------------------------*/
/* Subroutine to load a S/390 integer value from a buffer */
/*-------------------------------------------------------------------*/
static int
make_int (BYTE *src, int srclen)
{
int result = 0; /* Result accumulator */
int i; /* Array subscript */
for (i=0; i < srclen; i++)
{
result <<= 8;
result |= src[i];
}
return result;
} /* end function make_int */
/*-------------------------------------------------------------------*/
/* Subroutine to return the name of a dataset organization */
/*-------------------------------------------------------------------*/
static char *
dsorg_name (BYTE *dsorg)
{
static char name[8]; /* Name of dsorg */
if (dsorg[0] & DSORG_IS)
strcpy (name, "IS");
else if (dsorg[0] & DSORG_PS)
strcpy (name, "PS");
else if (dsorg[0] & DSORG_DA)
strcpy (name, "DA");
else if (dsorg[0] & DSORG_PO)
strcpy (name, "PO");
if (dsorg[0] & DSORG_U) strlcat (name, "U", sizeof(name));
return name;
} /* end function dsorg_name */
/*-------------------------------------------------------------------*/
/* Subroutine to return the name of a record format */
/*-------------------------------------------------------------------*/
static char *
recfm_name (BYTE *recfm)
{
static char name[8]; /* Name of record format */
switch (recfm[0] & RECFM_FORMAT) {
case RECFM_FORMAT_V:
strcpy (name, "V"); break;
case RECFM_FORMAT_F:
strcpy (name, "F"); break;
case RECFM_FORMAT_U:
strcpy (name, "U"); break;
default:
strcpy (name,"??");
} /* end switch */
if (recfm[0] & RECFM_TRKOFLOW) strlcat (name, "T", sizeof(name));
if (recfm[0] & RECFM_BLOCKED) strlcat (name, "B", sizeof(name));
if (recfm[0] & RECFM_SPANNED) strlcat (name, "S", sizeof(name));
switch (recfm[0] & RECFM_CTLCHAR) {
case RECFM_CTLCHAR_A:
strcpy (name, "A"); break;
case RECFM_CTLCHAR_M:
strcpy (name, "M"); break;
} /* end switch */
return name;
} /* end function recfm_name */
/*-------------------------------------------------------------------*/
/* Subroutine to return the name of a DASD device from the UCB type */
/*-------------------------------------------------------------------*/
static char *
dasd_name (FWORD ucbtype)
{
if (ucbtype[2] != 0x20) return "????";
switch (ucbtype[3]) {
case 0x01: return "2311";
case 0x02: return "2301";
case 0x03: return "2303";
case 0x04: if (ucbtype[1] == 0x00) return "2302";
else return "9345";
case 0x05: return "2321";
case 0x06: return "2305-1";
case 0x07: return "2305-2";
case 0x08: return "2314";
case 0x09: return "3330";
case 0x0A: return "3340";
case 0x0B: return "3350";
case 0x0C: return "3375";
case 0x0D: return "3330-11";
case 0x0E: return "3380";
case 0x0F: return "3390";
} /* end switch(key) */
return "????";
} /* end function dasd_name */
/*-------------------------------------------------------------------*/
/* Subroutine to return the UCBTYPE of a DASD device */
/*-------------------------------------------------------------------*/
static U32
ucbtype_code (U16 devtype)
{
switch (devtype) {
case 0x2311: return 0x30002001;
case 0x2301: return 0x30402002;
case 0x2303: return 0x30002003;
case 0x2302: return 0x30002004;
case 0x2321: return 0x30002005;
case 0x2305: return 0x30002006;
case 0x2314: return 0x30C02008;
case 0x3330: return 0x30502009;
case 0x3340: return 0x3050200A;
case 0x3350: return 0x3050200B;
case 0x3375: return 0x3050200C;
case 0x3380: return 0x3050200E;
case 0x3390: return 0x3050200F;
case 0x9345: return 0x30502004;
} /* end switch(key) */
return 0;
} /* end function ucbtype_code */
/*-------------------------------------------------------------------*/
/* Subroutine to calculate relative track address */
/* Input: */
/* cyl Cylinder number */
/* head Head number */
/* heads Number of heads per cylinder */
/* numext Number of extents */
/* xarray Array containing 1-16 extent descriptions */
/* Output: */
/* The return value is the relative track number, */
/* or -1 if an error occurred. */
/*-------------------------------------------------------------------*/
static int
calculate_ttr (int cyl, int head, int heads, int numext,
EXTDESC xarray[])
{
int i; /* Array subscript */
int track; /* Relative track number */
/* Search the extent descriptor array */
for (i = 0, track = 0; i < numext; track += xarray[i++].ntrk)
{
if (cyl < xarray[i].bcyl || cyl > xarray[i].ecyl)
continue;
if (cyl == xarray[i].bcyl && head < xarray[i].btrk)
continue;
if (cyl == xarray[i].ecyl && head > xarray[i].etrk)
continue;
track += (cyl - xarray[i].bcyl) * heads
- xarray[i].btrk + head;
break;
} /* end for(i) */
/* Error if track was not found in extent table */
if (i == numext)
{
XMERRF ( MSG( HHC02505, "E", cyl, head ) );
return -1;
}
/* Return relative track number */
return track;
} /* end function calculate_ttr */
/*-------------------------------------------------------------------*/
/* Subroutine to read IPL text from an EBCDIC object file */
/* Input: */
/* iplfnm Name of EBCDIC card image object file */
/* iplbuf Address of buffer in which to build IPL text record */
/* buflen Length of buffer */
/* Output: */
/* The return value is the length of the IPL text built */
/* in the buffer if successful, or -1 if error */
/* Note: */
/* Only TXT records are processed; ESD and RLD records are */
/* ignored because the IPL text is non-relocatable and is */
/* assumed to have zero origin. An END card must be present. */
/*-------------------------------------------------------------------*/
static int
read_ipl_text (char *iplfnm, BYTE *iplbuf, int buflen)
{
int rc; /* Return code */
int ipllen = 0; /* Length of IPL text */
int txtlen; /* Byte count from TXT card */
int txtadr; /* Address from TXT card */
int tfd; /* Object file descriptor */
BYTE objrec[80]; /* Object card image */
char pathname[MAX_PATH]; /* iplfnm in host path format*/
/* Open the object file */
hostpath(pathname, iplfnm, sizeof(pathname));
tfd = HOPEN (pathname, O_RDONLY|O_BINARY);
if (tfd < 0)
{
XMERRF ( MSG( HHC02506, "E", "open", iplfnm, strerror( errno ) ) );
return -1;
}
/* Read the object file */
while (1)
{
/* Read a card image from the object file */
rc = read (tfd, objrec, 80);
if (rc < 80)
{
XMERRF ( MSG( HHC02506, "E", "read", iplfnm, strerror( errno ) ) );
close (tfd);
return -1;
}
/* Column 1 of each object card must contain X'02' */
if (objrec[0] != 0x02)
{
XMERRF ( MSG( HHC02507, "E", iplfnm ) );
close (tfd);
return -1;
}
/* Exit if END card has been read */
if (memcmp(objrec+1, EBCDIC_END, 3) == 0)
break;
/* Ignore any cards which are not TXT cards */
if (memcmp(objrec+1, EBCDIC_TXT, 3) != 0)
continue;
/* Load the address from TXT card columns 6-8 */
txtadr = (objrec[5] << 16) | (objrec[6] << 8) | objrec[7];
/* Load the byte count from TXT card columns 11-12 */
txtlen = (objrec[10] << 8) | objrec[11];
XMINFF (5, MSG( HHC02522, "I", txtadr, txtlen ) );
/* Check that the byte count is valid */
if (txtlen > 56)
{
XMERRF ( MSG( HHC02508, "E", iplfnm, txtlen ) );
close (tfd);
return -1;
}
/* Check that the text falls within the buffer */
if (txtadr + txtlen > buflen)
{
XMERRF ( MSG( HHC02509, "E", iplfnm, buflen ) );
close (tfd);
return -1;
}
/* Copy the IPL text to the buffer */
memcpy (iplbuf + txtadr, objrec+16, txtlen);
/* Update the total size of the IPL text */
if (txtadr + txtlen > ipllen)
ipllen = txtadr + txtlen;
} /* end while */
return ipllen;
} /* end function read_ipl_text */
/*-------------------------------------------------------------------*/
/* Subroutine to initialize the output track buffer */
/* Input: */
/* trklen Track length of virtual output device */
/* trkbuf Pointer to track buffer */
/* cyl Cylinder number on output device */
/* head Head number on output device */
/* Output: */
/* usedv Number of bytes written to track of virtual device */
/*-------------------------------------------------------------------*/
static void
init_track (int trklen, BYTE *trkbuf, int cyl, int head, int *usedv)
{
CKDDASD_TRKHDR *trkhdr; /* -> Track header */
CKDDASD_RECHDR *rechdr; /* -> Record header */
/* Clear the track buffer to zeroes */
memset (trkbuf, 0, trklen);
/* Build the home address in the track buffer */
trkhdr = (CKDDASD_TRKHDR*)trkbuf;
trkhdr->bin = 0;
trkhdr->cyl[0] = (cyl >> 8) & 0xFF;
trkhdr->cyl[1] = cyl & 0xFF;
trkhdr->head[0] = (head >> 8) & 0xFF;
trkhdr->head[1] = head & 0xFF;
/* Build a standard record zero in the track buffer */
rechdr = (CKDDASD_RECHDR*)(trkbuf + CKDDASD_TRKHDR_SIZE);
rechdr->cyl[0] = (cyl >> 8) & 0xFF;
rechdr->cyl[1] = cyl & 0xFF;
rechdr->head[0] = (head >> 8) & 0xFF;
rechdr->head[1] = head & 0xFF;
rechdr->rec = 0;
rechdr->klen = 0;
rechdr->dlen[0] = (R0_DATALEN >> 8) & 0xFF;
rechdr->dlen[1] = R0_DATALEN & 0xFF;
/* Set number of bytes used in track buffer */
*usedv = CKDDASD_TRKHDR_SIZE + CKDDASD_RECHDR_SIZE + R0_DATALEN;
/* Build end of track marker at end of buffer */
memcpy (trkbuf + *usedv, eighthexFF, 8);
} /* end function init_track */
/*-------------------------------------------------------------------*/
/* Subroutine to write track buffer to output file */
/* Input: */
/* cif -> CKD image file descriptor */
/* ofname Output file name */
/* heads Number of tracks per cylinder on output device */
/* trklen Track length of virtual output device */
/* Input/output: */
/* usedv Number of bytes written to track of virtual device */
/* reltrk Relative track number on output device */
/* cyl Cylinder number on output device */
/* head Head number on output device */
/* Output: */
/* The return value is 0 if successful, -1 if error occurred. */
/*-------------------------------------------------------------------*/
static int
write_track (CIFBLK *cif, char *ofname, int heads, int trklen,
int *usedv, int *reltrk, int *cyl, int *head)
{
int rc; /* Return code */
UNREFERENCED(ofname);
UNREFERENCED(trklen);
/* Don't overwrite HA */
if (*usedv == 0)
*usedv = CKDDASD_TRKHDR_SIZE;
/* Build end of track marker at end of buffer */
memcpy (cif->trkbuf + *usedv, eighthexFF, 8);
cif->trkmodif = 1;
/* Reset values for next track */
(*reltrk)++;
(*head)++;
if (*head >= heads)
{
(*cyl)++;
*head = 0;
}
*usedv = 0;
/* Read the next track */
if (*cyl < (int)cif->devblk.ckdcyls)
{
rc = read_track (cif, *cyl, *head);
if (rc < 0) return -1;
}
return 0;
} /* end function write_track */
/*-------------------------------------------------------------------*/
/* Subroutine to add a data block to the output track buffer */
/* Input: */
/* cif -> CKD image file descriptor */
/* ofname Output file name */
/* blk Pointer to data block */
/* keylen Key length */
/* datalen Data length */
/* devtype Output device type */
/* heads Number of tracks per cylinder on output device */
/* trklen Track length of virtual output device */
/* maxtrk Maximum number of tracks to be written */
/* Input/output: */
/* usedv Number of bytes written to track of virtual device */
/* usedr Number of bytes written to track, calculated */
/* according to the formula for a real device */
/* trkbal Number of bytes remaining on track, calculated */
/* according to the formula for a real device */
/* reltrk Relative track number on output device */
/* cyl Cylinder number on output device */
/* head Head number on output device */
/* rec Record number on output device */
/* Output: */
/* The return value is 0 if successful, -1 if error occurred. */
/*-------------------------------------------------------------------*/
static int
write_block (CIFBLK *cif, char *ofname, DATABLK *blk, int keylen,
int datalen, U16 devtype, int heads, int trklen,
int maxtrk, int *usedv, int *usedr,
int *trkbal, int *reltrk, int *cyl, int *head, int *rec)
{
int rc; /* Return code */
int cc; /* Capacity calculation code */
CKDDASD_RECHDR *rechdr; /* -> Record header */
UNREFERENCED(devtype);
/* Determine whether record will fit on current track */
cc = capacity_calc (cif, *usedr, keylen, datalen,
usedr, trkbal, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL);
if (cc < 0) return -1;
/* Move to next track if record will not fit */
if (cc > 0 && *usedr > 0)
{
/* Write current track to output file */
rc = write_track (cif, ofname, heads, trklen,
usedv, reltrk, cyl, head);
if (rc < 0) return -1;
/* Clear bytes used and record number for new track */
*usedr = 0;
*rec = 0;
/* Determine whether record will fit on new track */
cc = capacity_calc (cif, *usedr, keylen, datalen,
usedr, trkbal, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL);
if (cc < 0) return -1;
} /* end if */
/* Error if record will not even fit on an empty track */
if (cc > 0)
{
XMERRF ( MSG( HHC02510, "E", blk->cyl[0], blk->cyl[1],
blk->head[0], blk->head[1], blk->rec ) );
return -1;
}
/* Determine whether end of extent has been reached */
if (*reltrk >= maxtrk)
{
XMERRF ( MSG( HHC02511, "E", *reltrk, maxtrk ) );
return -1;
}
/* Build home address and record 0 if new track */
if (*usedv == 0)
{
init_track (trklen, cif->trkbuf, *cyl, *head, usedv);
}
/* Double check that record will not exceed virtual track size */
if (*usedv + CKDDASD_RECHDR_SIZE + keylen + datalen + 8 > trklen)
{
XMERRF ( MSG( HHC02512, "E", blk->cyl[0], blk->cyl[1],
blk->head[0], blk->head[1], blk->rec ) );
return -1;
}
/* Add data block to virtual track buffer */
(*rec)++;
rechdr = (CKDDASD_RECHDR*)(cif->trkbuf + *usedv);
rechdr->cyl[0] = (*cyl >> 8) & 0xFF;
rechdr->cyl[1] = *cyl & 0xFF;
rechdr->head[0] = (*head >> 8) & 0xFF;
rechdr->head[1] = *head & 0xFF;
rechdr->rec = *rec;
rechdr->klen = keylen;
rechdr->dlen[0] = (datalen >> 8) & 0xFF;
rechdr->dlen[1] = datalen & 0xFF;
*usedv += CKDDASD_RECHDR_SIZE;
memcpy (cif->trkbuf + *usedv, blk->kdarea, keylen + datalen);
*usedv += keylen + datalen;
cif->trkmodif = 1;
return 0;
} /* end function write_block */
/*-------------------------------------------------------------------*/
/* Subroutine to write track zero */
/* Input: */
/* cif -> CKD image file descriptor */
/* ofname Output file name */
/* volser Volume serial number (ASCIIZ) */
/* devtype Output device type */
/* heads Number of tracks per cylinder on output device */
/* trklen Track length of virtual output device */
/* iplfnm Name of file containing IPL text object deck */
/* flagECmode 1 set EC mode bit in wait PSW */
/* 0 don't set EC mode bit in wait PSW */
/* flagMachinecheck 1 set machine-check-enabled flag */
/* in wait PSW */
/* 0 don't set machine-check-enabled flag */
/* in wait PSW */
/* Output: */
/* reltrk Next relative track number on output device */
/* outcyl Cylinder number of next track on output device */
/* outhead Head number of next track on output device */
/* The return value is 0 if successful, -1 if error occurred. */
/*-------------------------------------------------------------------*/
static int
write_track_zero (CIFBLK *cif, char *ofname, char *volser, U16 devtype,
int heads, int trklen, char *iplfnm,
int *reltrk, int *outcyl, int *outhead,
int flagECmode, int flagMachinecheck)
{
int rc; /* Return code */
int outusedv = 0; /* Output bytes used on track
of virtual device */
int outusedr = 0; /* Output bytes used on track
of real device */
int outtrkbr = 0; /* Output bytes remaining on
track of real device */
int outtrk = 0; /* Output relative track */
int outrec = 0; /* Output record number */
int keylen; /* Key length */
int datalen; /* Data length */
int maxtrks = 1; /* Maximum track count */
DATABLK *datablk; /* -> data block */
BYTE buf[32768]; /* Buffer for data block */
/* For 2311 the IPL text will not fit on track 0 record 4,
so adjust the IPL2 so that it loads from track 1 record 1 */
if (devtype == 0x2311)
{
memcpy (ipl2data + 32, "\x00\x00\x00\x00\x00\x01", 6);
memcpy (ipl2data + 38, "\x00\x00\x00\x01\x01", 5);
maxtrks = 2;
}
/* Read track 0 */
rc = read_track (cif, 0, 0);
if (rc < 0) return -1;
/* Initialize the track buffer */
*outcyl = 0; *outhead = 0;
init_track (trklen, cif->trkbuf, *outcyl, *outhead, &outusedv);
cif->trkmodif = 1;
/* Build the IPL1 record */
memset (buf, 0, sizeof(buf));
datablk = (DATABLK*)buf;
convert_to_ebcdic (datablk->kdarea, 4, "IPL1");
/* Build IPL PSW and CCWs in IPL1 record */
if (iplfnm != NULL)
{
/* Copy model IPL PSW and CCWs for IPLable volume */
memcpy (datablk->kdarea+4, iplpsw, 8);
memcpy (datablk->kdarea+12, iplccw1, 8);
memcpy (datablk->kdarea+20, iplccw2, 8);
}
else
{
/* Copy model IPL PSW and CCWs for non-IPLable volume */
memcpy (datablk->kdarea+4, noiplpsw, 8);
memcpy (datablk->kdarea+12, noiplccw1, 8);
memcpy (datablk->kdarea+20, noiplccw2, 8);
/* Set EC mode flag in wait PSW if requested */
if (flagECmode)
{
*(datablk->kdarea+5) = 0x08 | *(datablk->kdarea+5);
}
/* Set machine-check-enabled mask in PSW if requested */
if (flagMachinecheck)
{
*(datablk->kdarea+5) = 0x04 | *(datablk->kdarea+5);
}
}
keylen = IPL1_KEYLEN;
datalen = IPL1_DATALEN;
rc = write_block (cif, ofname, datablk, keylen, datalen,
devtype, heads, trklen, maxtrks,
&outusedv, &outusedr, &outtrkbr,
&outtrk, outcyl, outhead, &outrec);
if (rc < 0) return -1;
/* Build the IPL2 record */
memset (buf, 0, sizeof(buf));
datablk = (DATABLK*)buf;
convert_to_ebcdic (datablk->kdarea, 4, "IPL2");
if (iplfnm != NULL)
{
memcpy (datablk->kdarea+4, ipl2data, sizeof(ipl2data));
}
keylen = IPL2_KEYLEN;
datalen = IPL2_DATALEN;
rc = write_block (cif, ofname, datablk, keylen, datalen,
devtype, heads, trklen, maxtrks,
&outusedv, &outusedr, &outtrkbr,
&outtrk, outcyl, outhead, &outrec);
if (rc < 0) return -1;
/* Build the VOL1 record */
memset (buf, 0x40, sizeof(buf));
datablk = (DATABLK*)buf;
convert_to_ebcdic (datablk->kdarea, 4, "VOL1");
convert_to_ebcdic (datablk->kdarea+4, 4, "VOL1");
convert_to_ebcdic (datablk->kdarea+8, 6, volser);
memset(datablk->kdarea+15, 0, 5);
convert_to_ebcdic (datablk->kdarea+45, 8, "HERCULES");
keylen = VOL1_KEYLEN;
datalen = VOL1_DATALEN;
rc = write_block (cif, ofname, datablk, keylen, datalen,
devtype, heads, trklen, maxtrks,
&outusedv, &outusedr, &outtrkbr,
&outtrk, outcyl, outhead, &outrec);
if (rc < 0) return -1;
/* Build the IPL text from the object file */
if (iplfnm != NULL)
{
memset (buf, 0, sizeof(buf));
datalen = read_ipl_text (iplfnm, buf+12, sizeof(buf)-12);
if (datalen < 0) return -1;
datablk = (DATABLK*)buf;
keylen = 0;
rc = write_block (cif, ofname, datablk, keylen, datalen,
devtype, heads, trklen, maxtrks,
&outusedv, &outusedr, &outtrkbr,
&outtrk, outcyl, outhead, &outrec);
if (rc < 0) return -1;
}
/* Write track zero to the output file */
rc = write_track (cif, ofname, heads, trklen,
&outusedv, reltrk, outcyl, outhead);
if (rc < 0) return -1;
return 0;
} /* end function write_track_zero */
/*-------------------------------------------------------------------*/
/* Subroutine to update a data block in the output file */
/* Input: */
/* cif -> CKD image file descriptor */
/* ofname Output file name */
/* blk Pointer to data block structure */
/* cyl Cylinder number */
/* head Head number */
/* rec Record number */
/* keylen Key length */
/* datalen Data length */
/* heads Number of tracks per cylinder on output device */
/* trklen Track length of virtual output device */
/* Output: */
/* The return value is 0 if successful, -1 if error occurred. */
/*-------------------------------------------------------------------*/
static int
update_block (CIFBLK *cif, char *ofname, DATABLK *blk, int cyl,
int head, int rec, int keylen, int datalen, int heads, int trklen)
{
int rc; /* Return code */
int curcyl; /* Original cylinder */
int curhead; /* Original head */
int klen; /* Record key length */
int dlen; /* Record data length */
int skiplen; /* Number of bytes to skip */
int offset; /* Offset into trkbuf */
CKDDASD_TRKHDR trkhdr; /* Track header */
CKDDASD_RECHDR rechdr; /* Record header */
UNREFERENCED(heads);
UNREFERENCED(trklen);
/* Save the current position in the output file */
curcyl = cif->curcyl;
curhead = cif->curhead;
/* Read the requested track */
rc = read_track (cif, cyl, head);
if (rc < 0)
{
XMERRF ( MSG( HHC02513, "E", ofname, cyl, cyl, head, head ) );
return -1;
}
/* Copy the track header */
memcpy (&trkhdr, cif->trkbuf, CKDDASD_TRKHDR_SIZE);
offset = CKDDASD_TRKHDR_SIZE;
/* Validate the track header */
if (trkhdr.bin != 0
|| trkhdr.cyl[0] != (cyl >> 8)
|| trkhdr.cyl[1] != (cyl & 0xFF)
|| trkhdr.head[0] != (head >> 8)
|| trkhdr.head[1] != (head & 0xFF))
{
XMERRF ( MSG( HHC02514, "E", ofname, cyl, cyl, head, head,
trkhdr.bin, trkhdr.cyl[0], trkhdr.cyl[1],
trkhdr.head[0], trkhdr.head[1] ) );
return -1;
}
/* Search for the record to be updated */
while (1)
{
/* Copy the next record header */
memcpy (&rechdr, cif->trkbuf + offset, CKDDASD_RECHDR_SIZE);
offset += CKDDASD_RECHDR_SIZE;
/* Check for end of track */
if (memcmp(&rechdr, eighthexFF, 8) == 0)
{
XMERRF ( MSG( HHC02515, "E", ofname, cyl, cyl, head, head, rec, rec ) );
return -1;
}
/* Extract record key length and data length */
klen = rechdr.klen;
dlen = (rechdr.dlen[0] << 8) | rechdr.dlen[1];
/* Exit loop if matching record number */
if (rechdr.rec == rec)
break;
/* Skip the key and data areas */
skiplen = klen + dlen;
offset += skiplen;
} /* end while */
/* Check for attempt to change key length or data length */
if (keylen != klen || datalen != dlen)
{
XMERRF ( MSG( HHC02516, "E", ofname, cyl, cyl, head, head, rec, rec ) );
return -1;
}
/* Copy the updated block to the trkbuf */
memcpy (cif->trkbuf + offset, blk->kdarea, keylen + datalen);
cif->trkmodif = 1;
/* Restore original track */
rc = read_track (cif, curcyl, curhead);
if (rc < 0)
{
XMERRF ( MSG( HHC02513, "E", ofname, curcyl, curcyl, curhead, curhead ) );
return -1;
}
XMINFF (4, MSG( HHC02523, "I", cyl, cyl, head, head, rec, rec, keylen, datalen ) );
return 0;
} /* end function update_block */
/*-------------------------------------------------------------------*/
/* Subroutine to build a format 1 DSCB */
/* Input: */
/* dscbtab Array of pointers to DSCB data blocks */
/* dscbnum Number of entries in dscbtab array */
/* dsname Dataset name (ASCIIZ) */
/* volser Volume serial number (ASCIIZ) */
/* dsorg 1st byte of dataset organization bits */
/* recfm 1st byte of record format bits */
/* lrecl Logical record length */
/* blksz Block size */
/* keyln Key length */
/* dirblu Bytes used in last directory block */
/* lasttrk Relative track number of last-used track of dataset */
/* lastrec Record number of last-used block of dataset */
/* trkbal Bytes remaining on last-used track */
/* units Allocation units (C=CYL, T=TRK) */
/* spsec Secondary allocation quantity */
/* bcyl Extent begin cylinder number */
/* bhead Extent begin head number */
/* ecyl Extent end cylinder number */
/* ehead Extent end head number */
/* Output: */
/* The return value is 0 if successful, or -1 if error */
/* */
/* This subroutine allocates a DATABLK structure, builds a DSCB */
/* within the structure, and adds the structure to the DSCB array. */
/*-------------------------------------------------------------------*/
static int
build_format1_dscb (DATABLK *dscbtab[], int dscbnum,
char *dsname, char *volser,
BYTE dsorg, BYTE recfm, int lrecl, int blksz,
int keyln, int dirblu, int lasttrk, int lastrec,
int trkbal, BYTE units, int spsec,
int bcyl, int bhead, int ecyl, int ehead)
{
DATABLK *datablk; /* -> Data block structure */
FORMAT1_DSCB *f1dscb; /* -> DSCB within data block */
int blklen; /* Size of data block */
struct tm *tmptr; /* -> Date and time structure*/
time_t timeval; /* Current time value */
/* Obtain the current time */
time(&timeval);
tmptr = localtime(&timeval);
/* Allocate storage for a DATABLK structure */
blklen = 12 + sizeof(FORMAT1_DSCB);
datablk = (DATABLK*)malloc(blklen);
if (datablk == NULL)
{
XMERRF ( MSG( HHC02517, "E", "Format 1 ", strerror(errno) ) );
return -1;
}
/* Check that there is room in the DSCB pointer array */
if (dscbnum >= MAXDSCB)
{
XMERRF ( MSG( HHC02518, "E", MAXDSCB ) );
return -1;
}
/* Clear the data block and save its address in the DSCB array */
memset (datablk, 0, blklen);
dscbtab[dscbnum] = datablk;
/* Point to the DSCB within the data block */
f1dscb = (FORMAT1_DSCB*)(datablk->kdarea);
/* Build the format 1 DSCB */
convert_to_ebcdic (f1dscb->ds1dsnam, 44, dsname);
f1dscb->ds1fmtid = 0xF1;
convert_to_ebcdic (f1dscb->ds1dssn, 6, volser);
f1dscb->ds1volsq[0] = 0;
f1dscb->ds1volsq[1] = 1;
f1dscb->ds1credt[0] = tmptr->tm_year;
f1dscb->ds1credt[1] = (tmptr->tm_yday >> 8) & 0xFF;
f1dscb->ds1credt[2] = tmptr->tm_yday & 0xFF;
f1dscb->ds1expdt[0] = 0;
f1dscb->ds1expdt[1] = 0;
f1dscb->ds1expdt[2] = 0;
f1dscb->ds1noepv = 1;
f1dscb->ds1bodbd = dirblu;
convert_to_ebcdic (f1dscb->ds1syscd, 13, "HERCULES");
f1dscb->ds1dsorg[0] = dsorg;
f1dscb->ds1dsorg[1] = 0;
f1dscb->ds1recfm = recfm;
f1dscb->ds1optcd = 0;
f1dscb->ds1blkl[0] = (blksz >> 8) & 0xFF;
f1dscb->ds1blkl[1] = blksz & 0xFF;
f1dscb->ds1lrecl[0] = (lrecl >> 8) & 0xFF;
f1dscb->ds1lrecl[1] = lrecl & 0xFF;
f1dscb->ds1keyl = keyln;
f1dscb->ds1rkp[0] = 0;
f1dscb->ds1rkp[1] = 0;
f1dscb->ds1dsind = DS1DSIND_LASTVOL;
if ((blksz & 0x07) == 0)
f1dscb->ds1dsind |= DS1DSIND_BLKSIZ8;
f1dscb->ds1scalo[0] =
(units == 'C' ? DS1SCALO_UNITS_CYL : DS1SCALO_UNITS_TRK);
f1dscb->ds1scalo[1] = (spsec >> 16) & 0xFF;
f1dscb->ds1scalo[2] = (spsec >> 8) & 0xFF;
f1dscb->ds1scalo[3] = spsec & 0xFF;
f1dscb->ds1lstar[0] = (lasttrk >> 8) & 0xFF;
f1dscb->ds1lstar[1] = lasttrk & 0xFF;
f1dscb->ds1lstar[2] = lastrec;
f1dscb->ds1trbal[0] = (trkbal >> 8) & 0xFF;
f1dscb->ds1trbal[1] = trkbal & 0xFF;
f1dscb->ds1ext1.xttype =
(units == 'C' ? XTTYPE_CYLBOUND : XTTYPE_DATA);
f1dscb->ds1ext1.xtseqn = 0;
f1dscb->ds1ext1.xtbcyl[0] = (bcyl >> 8) & 0xFF;
f1dscb->ds1ext1.xtbcyl[1] = bcyl & 0xFF;
f1dscb->ds1ext1.xtbtrk[0] = (bhead >> 8) & 0xFF;
f1dscb->ds1ext1.xtbtrk[1] = bhead & 0xFF;
f1dscb->ds1ext1.xtecyl[0] = (ecyl >> 8) & 0xFF;
f1dscb->ds1ext1.xtecyl[1] = ecyl & 0xFF;
f1dscb->ds1ext1.xtetrk[0] = (ehead >> 8) & 0xFF;
f1dscb->ds1ext1.xtetrk[1] = ehead & 0xFF;
return 0;
} /* end function build_format1_dscb */
/*-------------------------------------------------------------------*/
/* Subroutine to build a format 4 DSCB */
/* Input: */
/* dscbtab Array of pointers to DSCB data blocks */
/* dscbnum Number of entries in dscbtab array */
/* devtype Output device type */
/* Output: */
/* The return value is 0 if successful, or -1 if error */
/* */
/* This subroutine allocates a DATABLK structure, builds a DSCB */
/* within the structure, and adds the structure to the DSCB array. */
/* */
/* Note: The VTOC extent descriptor, the highest F1 DSCB address, */
/* and the number of unused DSCBs, are set to zeroes here and must */
/* be updated later when the VTOC size and location are known. */
/* The device size in cylinders is set to the normal size for the */
/* device type and must be updated when the actual total number of */
/* cylinders written to the volume is known. */
/*-------------------------------------------------------------------*/
static int
build_format4_dscb (DATABLK *dscbtab[], int dscbnum, CIFBLK *cif)
{
DATABLK *datablk; /* -> Data block structure */
FORMAT4_DSCB *f4dscb; /* -> DSCB within data block */
int blklen; /* Size of data block */
int numdscb; /* Number of DSCBs per track */
int numdblk; /* Number of dir blks/track */
int physlen; /* Physical track length */
int numcyls; /* Device size in cylinders */
int numheads; /* Number of heads/cylinder */
int kbconst; /* Keyed block constant */
int lbconst; /* Last keyed block constant */
int nkconst; /* Non-keyed block constant */
BYTE devflag; /* Device flags for VTOC */
int tolfact; /* Device tolerance */
/* Calculate the physical track length, block overheads, device
size, and the number of DSCBs and directory blocks per track */
capacity_calc (cif, 0, 44, 96, NULL, NULL, &physlen, &kbconst,
&lbconst, &nkconst, &devflag, &tolfact, NULL,
&numdscb, &numheads, &numcyls);
capacity_calc (cif, 0, 8, 256, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
&numdblk, NULL, NULL);
/* Allocate storage for a DATABLK structure */
blklen = 12 + sizeof(FORMAT4_DSCB);
datablk = (DATABLK*)malloc(blklen);
if (datablk == NULL)
{
XMERRF ( MSG( HHC02517, "E", "Format 4 ", strerror(errno) ) );
return -1;
}
/* Check that there is room in the DSCB pointer array */
if (dscbnum >= MAXDSCB)
{
XMERRF ( MSG( HHC02518, "E", MAXDSCB ) );
return -1;
}
/* Clear the data block and save its address in the DSCB array */
memset (datablk, 0, blklen);
dscbtab[dscbnum] = datablk;
/* Point to the DSCB within the data block */
f4dscb = (FORMAT4_DSCB*)(datablk->kdarea);
/* Build the format 4 DSCB */
memset (f4dscb->ds4keyid, 0x04, 44);
f4dscb->ds4fmtid = 0xF4;
f4dscb->ds4hcchh[0] = (numcyls >> 8) & 0xFF;
f4dscb->ds4hcchh[1] = numcyls & 0xFF;
f4dscb->ds4hcchh[2] = 0;
f4dscb->ds4hcchh[3] = 0;
f4dscb->ds4noatk[0] = 0;
f4dscb->ds4noatk[1] = 0;
f4dscb->ds4vtoci = DS4VTOCI_DOS;
f4dscb->ds4noext = 1;
f4dscb->ds4devsz[0] = (numcyls >> 8) & 0xFF;
f4dscb->ds4devsz[1] = numcyls & 0xFF;
f4dscb->ds4devsz[2] = (numheads >> 8) & 0xFF;
f4dscb->ds4devsz[3] = numheads & 0xFF;
f4dscb->ds4devtk[0] = (physlen >> 8) & 0xFF;
f4dscb->ds4devtk[1] = physlen & 0xFF;
f4dscb->ds4devi = kbconst;
f4dscb->ds4devl = lbconst;
f4dscb->ds4devk = nkconst;
f4dscb->ds4devfg = devflag;
f4dscb->ds4devtl[0] = (tolfact >> 8) & 0xFF;
f4dscb->ds4devtl[1] = tolfact & 0xFF;
f4dscb->ds4devdt = numdscb;
f4dscb->ds4devdb = numdblk;
return 0;
} /* end function build_format4_dscb */
/*-------------------------------------------------------------------*/
/* Subroutine to build a format 5 DSCB */
/* Input: */
/* dscbtab Array of pointers to DSCB data blocks */
/* dscbnum Number of entries in dscbtab array */
/* Output: */
/* The return value is 0 if successful, or -1 if error */
/* */
/* This subroutine allocates a DATABLK structure, builds a DSCB */
/* within the structure, and adds the structure to the DSCB array. */
/* */
/* Note: The format 5 DSCB is built with no free space extents. */
/* The DOS bit which is set in ds4vtoci forces the operating system */
/* VTOC conversion routine to calculate the free space and update */
/* the format 5 DSCB the first time the volume is accessed. */
/*-------------------------------------------------------------------*/
static int
build_format5_dscb (DATABLK *dscbtab[], int dscbnum)
{
DATABLK *datablk; /* -> Data block structure */
FORMAT5_DSCB *f5dscb; /* -> DSCB within data block */
int blklen; /* Size of data block */
/* Allocate storage for a DATABLK structure */
blklen = 12 + sizeof(FORMAT5_DSCB);
datablk = (DATABLK*)malloc(blklen);
if (datablk == NULL)
{
XMERRF ( MSG( HHC02517, "E", "Format 5 ", strerror(errno) ) );
return -1;
}
/* Check that there is room in the DSCB pointer array */
if (dscbnum >= MAXDSCB)
{
XMERRF ( MSG( HHC02518, "E", MAXDSCB ) );
return -1;
}
/* Clear the data block and save its address in the DSCB array */
memset (datablk, 0, blklen);
dscbtab[dscbnum] = datablk;
/* Point to the DSCB within the data block */
f5dscb = (FORMAT5_DSCB*)(datablk->kdarea);
/* Build the format 5 DSCB */
memset (f5dscb->ds5keyid, 0x05, 4);
f5dscb->ds5fmtid = 0xF5;
return 0;
} /* end function build_format5_dscb */
/*-------------------------------------------------------------------*/
/* Subroutine to write the VTOC */
/* Input: */
/* dscbtab Array of pointers to DSCB data blocks */
/* numdscb Number of DSCBs including format 4 and format 5 */
/* cif -> CKD image file descriptor */
/* ofname Output file name */
/* devtype Output device type */
/* reqcyls Requested device size in cylinders, or zero */
/* heads Number of tracks per cylinder on output device */
/* trklen Track length of virtual output device */
/* vtoctrk Starting relative track number for VTOC, or zero */
/* vtocext Number of tracks in VTOC, or zero */
/* Input/output: */
/* nxtcyl Starting cylinder number for next dataset */
/* nxthead Starting head number for next dataset */
/* Output: */
/* volvtoc VTOC starting CCHHR (5 bytes) */
/* The return value is 0 if successful, or -1 if error */
/* */
/* If vtoctrk and vtocext are non-zero, then the VTOC is written */
/* into the space previously reserved at the indicated location. */
/* Otherwise, the VTOC is written at the next available dataset */
/* location, using as many tracks as are necessary, and nextcyl */
/* and nexthead are updated to point past the end of the VTOC. */
/*-------------------------------------------------------------------*/
static int
write_vtoc (DATABLK *dscbtab[], int numdscb, CIFBLK *cif, char *ofname,
U16 devtype, int reqcyls, int heads, int trklen,
int vtoctrk, int vtocext,
int *nxtcyl, int *nxthead, BYTE volvtoc[])
{
int rc; /* Return code */
int i; /* Array subscript */
DATABLK *datablk; /* -> Data block structure */
FORMAT1_DSCB *f1dscb; /* -> Format 1 DSCB */
FORMAT4_DSCB *f4dscb; /* -> Format 4 DSCB */
int dscbpertrk; /* Number of DSCBs per track */
int mintrks; /* Minimum VTOC size (tracks)*/
int numtrks; /* Actual VTOC size (tracks) */
int highcyl; /* Last used cylinder number */
int highhead; /* Last used head number */
int highrec; /* Last used record number */
int numf0dscb; /* Number of unused DSCBs */
int abstrk; /* Absolute track number */
int endcyl; /* VTOC end cylinder number */
int endhead; /* VTOC end head number */
int numcyls; /* Volume size in cylinders */
int outusedv = 0; /* Bytes used in track buffer*/
int outusedr = 0; /* Bytes used on real track */
int outtrkbr; /* Bytes left on real track */
int outcyl; /* Output cylinder number */
int outhead; /* Output head number */
int outtrk = 0; /* Relative track number */
int outrec = 0; /* Output record number */
int prealloc = 0; /* 1=VTOC is preallocated */
BYTE blankblk[152]; /* Data block for blank DSCB */
int curcyl; /* Current cylinder in file */
int curhead; /* Current head in file */
char dsnama[45]; /* Dataset name (ASCIIZ) */
/* Determine if the VTOC is preallocated */
prealloc = (vtoctrk != 0 && vtocext != 0);
/* Point to the format 4 DSCB within the first data block */
f4dscb = (FORMAT4_DSCB*)(dscbtab[0]->kdarea);
/* Calculate the minimum number of tracks required for the VTOC */
dscbpertrk = f4dscb->ds4devdt;
mintrks = (numdscb + dscbpertrk - 1) / dscbpertrk;
/* Save the current position in the output file */
curcyl = cif->curcyl;
curhead = cif->curhead;
/* Obtain the VTOC starting location and size */
if (prealloc)
{
/* Use preallocated VTOC location */
outcyl = vtoctrk / heads;
outhead = vtoctrk % heads;
numtrks = vtocext;
}
else
{
/* Use next available dataset location for VTOC */
outcyl = *nxtcyl;
outhead = *nxthead;
numtrks = mintrks;
}
/* Check that VTOC extent size is sufficient */
if (numtrks < mintrks)
{
XMERRF ( MSG( HHC02519, "E", mintrks, mintrks == 1 ? "" : "s" ) );
return -1;
}
/* Read the first track of the VTOC */
rc = read_track (cif, outcyl, outhead);
if (rc < 0)
{
XMERRF ( MSG( HHC02530, "E", "VTOC", outcyl, outcyl, outhead, outhead ) );
return -1;
}
/* Calculate the CCHHR of the last format 1 DSCB */
abstrk = (outcyl * heads) + outhead;
abstrk += mintrks - 1;
highcyl = abstrk / heads;
highhead = abstrk % heads;
highrec = ((numdscb - 1) % dscbpertrk) + 1;
/* Update the last format 1 CCHHR in the format 4 DSCB */
f4dscb->ds4hpchr[0] = (highcyl >> 8) & 0xFF;
f4dscb->ds4hpchr[1] = highcyl & 0xFF;
f4dscb->ds4hpchr[2] = (highhead >> 8) & 0xFF;
f4dscb->ds4hpchr[3] = highhead & 0xFF;
f4dscb->ds4hpchr[4] = highrec;
/* Build the VTOC start CCHHR */
volvtoc[0] = (outcyl >> 8) & 0xFF;
volvtoc[1] = outcyl & 0xFF;
volvtoc[2] = (outhead >> 8) & 0xFF;
volvtoc[3] = outhead & 0xFF;
volvtoc[4] = 1;
XMINFF (1, MSG( HHC02524, "I", outcyl, outcyl, outhead, outhead, numtrks, numtrks == 1 ? "" : "s" ) );
/* Calculate the number of format 0 DSCBs required to
fill out the unused space at the end of the VTOC */
numf0dscb = (numtrks * dscbpertrk) - numdscb;
/* Update the format 0 DSCB count in the format 4 DSCB */
f4dscb->ds4dsrec[0] = (numf0dscb >> 8) & 0xFF;
f4dscb->ds4dsrec[1] = numf0dscb & 0xFF;
/* Calculate the CCHH of the last track of the VTOC */
abstrk = (outcyl * heads) + outhead;
abstrk += numtrks - 1;
endcyl = abstrk / heads;
endhead = abstrk % heads;
/* Update the VTOC extent descriptor in the format 4 DSCB */
f4dscb->ds4vtoce.xttype =
(endhead == heads - 1 ? XTTYPE_CYLBOUND : XTTYPE_DATA);
f4dscb->ds4vtoce.xtseqn = 0;
f4dscb->ds4vtoce.xtbcyl[0] = (outcyl >> 8) & 0xFF;
f4dscb->ds4vtoce.xtbcyl[1] = outcyl & 0xFF;
f4dscb->ds4vtoce.xtbtrk[0] = (outhead >> 8) & 0xFF;
f4dscb->ds4vtoce.xtbtrk[1] = outhead & 0xFF;
f4dscb->ds4vtoce.xtecyl[0] = (endcyl >> 8) & 0xFF;
f4dscb->ds4vtoce.xtecyl[1] = endcyl & 0xFF;
f4dscb->ds4vtoce.xtetrk[0] = (endhead >> 8) & 0xFF;
f4dscb->ds4vtoce.xtetrk[1] = endhead & 0xFF;
/* Calculate the mimimum volume size */
if (prealloc)
{
/* The VTOC was preallocated, so the minimum volume
size equals the next available cylinder number */
numcyls = *nxtcyl;
if (*nxthead != 0) numcyls++;
}
else
{
/* The VTOC will be written into the available space,
so the minimum volume size is one more than the
ending cylinder number of the VTOC */
numcyls = endcyl + 1;
}
/* If the minimum volume size is less than the requested
size then use the requested size as the actual size */
if (numcyls < reqcyls) numcyls = reqcyls;
/* Update the volume size in cylinders in the format 4 DSCB */
f4dscb->ds4devsz[0] = (numcyls >> 8) & 0xFF;
f4dscb->ds4devsz[1] = numcyls & 0xFF;
/* Format the track buffer */
init_track (trklen, cif->trkbuf, outcyl, outhead, &outusedv);
cif->trkmodif = 1;
/* Write the format 4, format 5, and format 1 DSCBs to the VTOC */
for (i = 0; i < numdscb; i++)
{
/* Load the data block pointer from the DSCB table */
datablk = dscbtab[i];
/* Extract the dataset name from the format 1 DSCB */
memset (dsnama, 0, sizeof(dsnama));
f1dscb = (FORMAT1_DSCB*)(datablk->kdarea);
if (f1dscb->ds1fmtid == 0xF1)
{
make_asciiz (dsnama, sizeof(dsnama), f1dscb->ds1dsnam,
sizeof(f1dscb->ds1dsnam));
}
/* Add next DSCB to the track buffer */
rc = write_block (cif, ofname, datablk, 44, 96,
devtype, heads, trklen, numtrks,
&outusedv, &outusedr, &outtrkbr,
&outtrk, &outcyl, &outhead, &outrec);
if (rc < 0) return -1;
XMINFF (4, MSG( HHC02525, "I", datablk->kdarea[0] == 0x04 ? 4 :
datablk->kdarea[0] == 0x05 ? 5 : 1,
outcyl, outhead, outrec, outtrk, outrec, dsnama ) );
if (infolvl >= 5) data_dump (datablk, 152);
} /* end for(i) */
/* Fill the remainder of the VTOC with format 0 DSCBs */
for (i = 0; i < numf0dscb; i++)
{
/* Add a format 0 DSCB to the track buffer */
memset (blankblk, 0, sizeof(blankblk));
datablk = (DATABLK*)blankblk;
rc = write_block (cif, ofname, datablk, 44, 96,
devtype, heads, trklen, numtrks,
&outusedv, &outusedr, &outtrkbr,
&outtrk, &outcyl, &outhead, &outrec);
if (rc < 0) return -1;
XMINFF (4, MSG( HHC02525, "I", 0, outcyl, outhead, outrec, outtrk, outrec, "" ) );
} /* end for(i) */
/* Write data remaining in last track buffer */
rc = write_track (cif, ofname, heads, trklen,
&outusedv, &outtrk, &outcyl, &outhead);
if (rc < 0) return -1;
/* Restore original file position if VTOC was preallocated */
if (prealloc)
{
/* Read the original track again */
rc = read_track (cif, curcyl, curhead);
if (rc < 0)
{
XMERRF ( MSG( HHC02530, "E", "track", curcyl, curcyl, curhead, curhead ) );
return -1;
}
}
else
{
/* Update next cyl and head if VTOC not preallocated */
*nxtcyl = outcyl;
*nxthead = outhead;
}
return 0;
} /* end function write_vtoc */
/*-------------------------------------------------------------------*/
/* Subroutine to return the name of a text unit */
/*-------------------------------------------------------------------*/
static char *
tu_name (U16 key)
{
switch (key) {
CASERET(INMDDNAM);
CASERET(INMDSNAM);
CASERET(INMMEMBR);
CASERET(INMDIR );
CASERET(INMEXPDT);
CASERET(INMTERM );
CASERET(INMBLKSZ);
CASERET(INMDSORG);
CASERET(INMLRECL);
CASERET(INMRECFM);
CASERET(INMTNODE);
CASERET(INMTUID );
CASERET(INMFNODE);
CASERET(INMFUID );
CASERET(INMLREF );
CASERET(INMLCHG );
CASERET(INMCREAT);
CASERET(INMFVERS);
CASERET(INMFTIME);
CASERET(INMTTIME);
CASERET(INMFACK );
CASERET(INMERRCD);
CASERET(INMUTILN);
CASERET(INMUSERP);
CASERET(INMRECCT);
CASERET(INMSIZE );
CASERET(INMFFM );
CASERET(INMNUMF );
CASERET(INMTYPE );
} /* end switch(key) */
return "????????";
} /* end function tu_name */
/*-------------------------------------------------------------------*/
/* Subroutine to extract next text unit from buffer */
/* Input: */
/* xbuf Pointer to start of buffer */
/* bufpos Position of next text unit within buffer */
/* bufrem Number of bytes remaining in buffer */
/* pkey Pointer to field to receive text unit key */
/* pnum Pointer to field to receive number of data items */
/* maxnum Maximum number of data items expected */
/* plen Pointer to array to receive data item lengths */
/* pdata Pointer to array to receive data item pointers */
/* Output: */
/* The function return value is the total length of the */
/* text unit, or -1 if error. */
/* */
/* Text units are listed if infolvl is 4 or greater. */
/*-------------------------------------------------------------------*/
static int
next_tu (BYTE *xbuf, int bufpos, int bufrem, U16 *pkey, U16 *pnum,
U16 maxnum, U16 plen[], BYTE *pdata[])
{
int i, j; /* Array subscripts */
U16 key, num; /* Text unit header */
int field; /* Field number */
int offset; /* Offset into text unit */
U16 len; /* Field length */
char *name; /* Text unit name */
BYTE c, chars[9]; /* Character work areas */
char hex[17]; /* Character work areas */
set_codepage(NULL);
/* Error if remaining length is insufficient for header */
if (bufrem < 4)
{
XMERR ( MSG( HHC02531, "E" ) );
return -1;
}
/* Load the key and field count from the first 4 bytes */
key = (xbuf[bufpos] << 8) | xbuf[bufpos+1];
num = (xbuf[bufpos+2] << 8) | xbuf[bufpos+3];
/* Obtain the text unit name */
name = tu_name(key);
/* Print the text unit name and field count */
XMINFF (4, MSG_C( HHC02526, "I", bufpos, name, key, num ) );
/* Error if number of fields exceeds maximum */
if (num > maxnum)
{
XMINF (4, "\n");
XMERR ( MSG( HHC02532, "E" ) );
return -1;
}
/* Point to first field */
offset = 4;
bufrem -= 4;
/* Process each field in text unit */
for (field = 0; field < num; field++)
{
/* Error if remaining length is insufficient for length */
if (bufrem < 2)
{
XMINF (4, "\n");
XMERR ( MSG( HHC02531, "E" ) );
return -1;
}
/* Load field length from next 2 bytes */
len = (xbuf[bufpos+offset] << 8) | xbuf[bufpos+offset+1];
offset += 2;
bufrem -= 2;
/* Error if remaining length is insufficient for data */
if (bufrem < len)
{
XMINF (4, "\n");
XMERR ( MSG( HHC02531, "E" ) );
return -1;
}
/* Print field length and data */
if (field > 0) XMINF (4, "\n\t\t\t\t ");
XMINFF (4, "%04X ", len);
memset (hex, 0, sizeof(hex));
memset (chars, 0, sizeof(chars));
for (i = 0, j = 0; i < len; i++, j++)
{
if (i > 0 && (i & 0x07) == 0)
{
XMINFF (4, "%-16.16s %-8.8s\n\t\t\t\t ",
hex, chars);
memset (hex, 0, sizeof(hex));
memset (chars, 0, sizeof(chars));
j = 0;
}
sprintf(hex+2*j, "%2.2X", xbuf[bufpos+offset+i]);
c = guest_to_host(xbuf[bufpos+offset+i]);
if (!isprint(c)) c = '.';
chars[j] = c;
} /* end for(i) */
XMINFF (4, "%-16.16s %-8.8s", hex, chars);
/* Save field length and pointer in array */
plen[field] = len;
pdata[field] = xbuf + bufpos + offset;
/* Get offset of next field in text unit */
offset += len;
bufrem -= len;
} /* end for */
/* Print newline at end of text unit */
XMINF (4, "\n");
/* Return key, number of fields, and total length */
*pkey = key;
*pnum = num;
return offset;
} /* end function next_tu */
/*-------------------------------------------------------------------*/
/* Subroutine to assemble a logical record from segments */
/* Input: */
/* xfd Input file descriptor */
/* xfname Input file name */
/* xbuf Pointer to buffer to receive logical record */
/* Output: */
/* ctl Zero=data record, non-zero=control record */
/* The return value is the logical record length, */
/* or -1 if an error occurred. */
/*-------------------------------------------------------------------*/
static int
read_xmit_rec (int xfd, char *xfname, BYTE *xbuf, BYTE *ctl)
{
int rc; /* Return code */
int xreclen = 0; /* Cumulative record length */
int segnum; /* Segment counter */
int seglen; /* Segment data length */
BYTE ctlind = 0x00; /* 0x20=Control record */
BYTE seghdr[2]; /* Segment length and flags */
for (segnum = 0; ; segnum++)
{
/* Read the segment length and flags */
rc = read (xfd, seghdr, 2);
if (rc < 2)
{
XMERRF ( MSG( HHC02533, "E", xfname, rc < 0 ? strerror(errno) : "Unexpected end of file" ) );
return -1;
}
/* Check for valid segment header */
if (seghdr[0] < 2 || (seghdr[1] & 0x1F) != 0)
{
XMERRF ( MSG( HHC02534, "E", xfname, seghdr[0], seghdr[1] ) );
return -1;
}
/* Check flags for first segment */
if (segnum == 0)
{
/* Check that first segment indicator is set */
if ((seghdr[1] & 0x80) == 0)
{
XMERRF ( MSG( HHC02535, "E", xfname ) );
return -1;
}
/* Save the control record indicator */
ctlind = (seghdr[1] & 0x20);
}
/* Check flags for subsequent segments */
if (segnum > 0)
{
/* Check that first segment indicator is not set */
if (seghdr[1] & 0x80)
{
XMERRF ( MSG( HHC02536, "E", xfname ) );
return -1;
}
/* Check if ctlrec indicator matches first segment */
if ((seghdr[1] & 0x20) != ctlind)
{
XMERRF ( MSG( HHC02537, "E", xfname ) );
return -1;
}
}
/* Read segment data into buffer */
seglen = seghdr[0] - 2;
rc = read (xfd, xbuf + xreclen, seglen);
if (rc < seglen)
{
XMERRF ( MSG( HHC02533, "E", xfname, rc < 0 ? strerror(errno) : "Unexpected end of file" ) );
return -1;
}
/* Accumulate total record length */
xreclen += seglen;
/* Exit if last segment of record */
if (seghdr[1] & 0x40)
break;
} /* end for(segnum) */
/* Return record length and control indicator */
*ctl = ctlind;
return xreclen;
} /* end function read_xmit_rec */
/*-------------------------------------------------------------------*/
/* Subroutine to assemble a logical record from DSORG=VS file */
/* Input: */
/* xfd Input file descriptor */
/* xfname Input file name */
/* xbuf Pointer to buffer to receive logical record */
/* recnum Relative number for the record to be read */
/* Output: */
/* The return value is the logical record length, */
/* or -1 if an error occurred. */
/*-------------------------------------------------------------------*/
static int
read_vs_rec (int xfd, char *xfname, BYTE *xbuf, int recnum)
{
int rc; /* Return code */
int xreclen; /* Cumulative record length */
DATABLK *datablk; /* Data block */
if (recnum == 0) {
xreclen = read(xfd, xbuf, 56); /* read COPYR1 plus some extras */
if (xreclen < 56)
{
XMERRF ( MSG( HHC02533, "E", xfname, xreclen < 0 ? strerror(errno) : "Unexpected end of file" ) );
return -1;
}
}
else if (recnum == 1) {
xreclen = read(xfd, xbuf, sizeof(COPYR2)); /* read COPYR2 */
if (xreclen < (int)sizeof(COPYR2))
{
XMERRF ( MSG( HHC02533, "E", xfname, xreclen < 0 ? strerror(errno) : "Unexpected end of file" ) );
return -1;
}
}
else {
rc = read(xfd, xbuf, 12); /* read header of DATABLK */
if (rc == 0) /* read nothing? */
return 0;
if (rc < 12)
{
XMERRF ( MSG( HHC02533, "E", xfname, rc < 0 ? strerror(errno) : "Unexpected end of file" ) );
return -1;
}
datablk = (DATABLK *)xbuf;
xreclen = ((datablk->dlen[0] << 8) | datablk->dlen[1])
+ datablk->klen;
rc = read(xfd, xbuf + 12, xreclen); /* read kdarea of DATABLK */
if (rc < xreclen)
{
XMERRF ( MSG( HHC02533, "E", xfname, rc < 0 ? strerror(errno) : "Unexpected end of file" ) );
return -1;
}
xreclen += 12; /* also count the header */
}
/* Return record length */
return xreclen;
} /* end function read_vs_rec */
/*-------------------------------------------------------------------*/
/* Subroutine to process an INMR02 control record */
/* Input: */
/* xbuf Pointer to buffer containing control record */
/* xreclen Length of control record */
/* filen Pointer to field to receive file sequence number */
/* dsorg Pointer to byte to receive dataset organization */
/* recfm Pointer to byte to receive record format */
/* lrecl Pointer to integer to receive logical record length */
/* blksz Pointer to integer to receive block size */
/* keyln Pointer to integer to receive key length */
/* dirnm Pointer to integer to number of directory blocks */
/* Output: */
/* If the record contains the text unit INMUTILN=IEBCOPY */
/* then the dataset attributes are returned and the function */
/* return value is 1. Otherwise the return value is 0 */
/* and the dataset attributes remain unchanged. */
/* If an error occurs then the return value is -1. */
/* */
/* File information is listed if infolvl is 2 or greater. */
/*-------------------------------------------------------------------*/
static int
process_inmr02 (BYTE *xbuf, int xreclen, int *filen,
BYTE *dsorg, BYTE *recfm, U16 *lrecl, U16 *blksz,
U16 *keyln, U16 *dirnm)
{
int rc; /* Return code */
int i; /* Array subscript */
int len; /* String length */
U32 filenum; /* File number */
int bufpos; /* Position of TU in buffer */
int bufrem; /* Bytes remaining in buffer */
char tuutiln[9]; /* Utility name */
BYTE tukeyln; /* Key length */
HWORD tudsorg; /* Data set organization */
HWORD turecfm; /* Record format */
U16 tulrecl; /* Logical record length */
U16 tublksz; /* Block size */
int tudirct; /* Number of directory blocks*/
U16 tukey; /* Text unit key */
U16 tunum; /* Number of text unit fields*/
char tudsnam[45]; /* Data set name */
#define MAXNUM 20 /* Maximum number of fields */
U16 fieldlen[MAXNUM]; /* Array of field lengths */
BYTE *fieldptr[MAXNUM]; /* Array of field pointers */
/* Extract the file number which follows the record name */
filenum = (xbuf[6] << 24) | (xbuf[7] << 16)
| (xbuf[8] << 8) | xbuf[9];
XMINFF (4, MSG( HHC02527, "I", filenum ) );
/* Point to the first text unit */
bufpos = 10;
bufrem = xreclen-10;
/* Clear values to be loaded from text units */
memset (tudsnam, 0, sizeof(tudsnam));
memset (tuutiln, 0, sizeof(tuutiln));
memset (tudsorg, 0, sizeof(tudsorg));
memset (turecfm, 0, sizeof(turecfm));
tulrecl = 0;
tublksz = 0;
tukeyln = 0;
tudirct = 0;
/* Process each text unit */
while (bufrem > 0)
{
/* Extract the next text unit */
rc = next_tu (xbuf, bufpos, bufrem, &tukey, &tunum,
MAXNUM, fieldlen, fieldptr);
if (rc < 0)
{
XMERRF ( MSG( HHC02538, "E", bufpos + 2 ) );
return -1;
}
bufpos += rc;
bufrem -= rc;
/* Save the values from selected text units */
switch (tukey) {
case INMUTILN:
make_asciiz (tuutiln, sizeof(tuutiln),
fieldptr[0], fieldlen[0]);
break;
case INMDSORG:
if (fieldlen[0] > sizeof(tudsorg))
fieldlen[0] = sizeof(tudsorg);
memcpy (tudsorg, fieldptr[0], fieldlen[0]);
break;
case INMRECFM:
if (fieldlen[0] > sizeof(turecfm))
fieldlen[0] = sizeof(turecfm);
memcpy (turecfm, fieldptr[0], fieldlen[0]);
break;
case INMLRECL:
tulrecl = make_int (fieldptr[0], fieldlen[0]);
break;
case INMBLKSZ:
tublksz = make_int (fieldptr[0], fieldlen[0]);
break;
case INMTYPE:
tukeyln = make_int (fieldptr[0], fieldlen[0]);
break;
case INMDIR:
tudirct = make_int (fieldptr[0], fieldlen[0]);
break;
case INMDSNAM:
memset (tudsnam, 0, sizeof(tudsnam));
for (i = 0; i < tunum; i++)
{
#if defined( _MSVC_ )
len = (int)strlen(tudsnam);
#else
len = strlen(tudsnam);
#endif
if (i > 0 && len < (int)(sizeof(tudsnam) - 1))
tudsnam[len++] = '.';
make_asciiz (tudsnam + len, sizeof(tudsnam) - len,
fieldptr[i], fieldlen[i]);
} /* end for(i) */
} /* end switch(tukey) */
} /* end while(bufrem) */
/* Return the dataset values if this is the IEBCOPY record */
if (strcmp(tuutiln, "IEBCOPY") == 0)
{
XMINFF (2, MSG( HHC02528, "I", filenum, tudsnam ) );
XMINFF (2, MSG( HHC02529, "I", dsorg_name(tudsorg), recfm_name(turecfm),
tulrecl, tublksz, tukeyln, tudirct ) );
*filen = filenum;
*dsorg = tudsorg[0];
*recfm = turecfm[0];
*lrecl = tulrecl;
*blksz = tublksz;
*keyln = tukeyln;
*dirnm = tudirct;
}
return 0;
} /* end function process_inmr02 */
/*-------------------------------------------------------------------*/
/* Subroutine to process a control record other than INMR02 */
/* Input: */
/* xbuf Pointer to buffer containing control record */
/* xreclen Length of control record */
/* Output: */
/* The return value is 0 if successful, or -1 if error. */
/*-------------------------------------------------------------------*/
static int
process_inmrxx (BYTE *xbuf, int xreclen)
{
int rc; /* Return code */
int bufpos; /* Position of TU in buffer */
int bufrem; /* Bytes remaining in buffer */
U16 tukey; /* Text unit key */
U16 tunum; /* Number of text unit fields*/
#define MAXNUM 20 /* Maximum number of fields */
U16 fieldlen[MAXNUM]; /* Array of field lengths */
BYTE *fieldptr[MAXNUM]; /* Array of field pointers */
/* Point to the first text unit */
bufpos = 6;
bufrem = xreclen-6;
/* Process each text unit */
while (bufrem > 0)
{
/* Extract the next text unit */
rc = next_tu (xbuf, bufpos, bufrem, &tukey, &tunum,
MAXNUM, fieldlen, fieldptr);
if (rc < 0)
{
XMERRF ( MSG( HHC02538, "E", bufpos + 2 ) );
return -1;
}
bufpos += rc;
bufrem -= rc;
} /* end while(bufrem) */
return 0;
} /* end function process_inmrxx */
/*-------------------------------------------------------------------*/
/* Subroutine to process a COPYR1 header record */
/* Input: */
/* xbuf Pointer to buffer containing header record */
/* xreclen Length of header record */
/* Output: */
/* The return value is the number of tracks per cylinder, */
/* or -1 if an error occurred. */
/*-------------------------------------------------------------------*/
static int
process_copyr1 (BYTE *xbuf, int xreclen)
{
COPYR1 *copyr1 = (COPYR1*)xbuf; /* -> COPYR1 header record */
U16 blksize; /* Block size */
U16 lrecl; /* Logical record length */
BYTE keylen; /* Key length */
U16 cyls; /* Number of cylinders */
U16 heads; /* Number of tracks/cylinder */
/* Check COPYR1 record for correct length */
if (xreclen != sizeof(COPYR1)
&& xreclen != sizeof(COPYR1) - 4)
{
XMERR ( MSG( HHC02539, "E", 1 ) );
return -1;
}
/* Check that COPYR1 header identifier is correct */
if (memcmp(copyr1->hdrid, COPYR1_HDRID, 3) != 0)
{
XMERR ( MSG( HHC02540, "E", 1 ) );
return -1;
}
/* Check that the dataset is an old format unload */
if ((copyr1->uldfmt & COPYR1_ULD_FORMAT)
!= COPYR1_ULD_FORMAT_OLD)
{
XMERR ( MSG( HHC02541, "E", 1 ) );
return -1;
}
blksize = (copyr1->ds1blkl[0] << 8) | copyr1->ds1blkl[1];
lrecl = (copyr1->ds1lrecl[0] << 8) | copyr1->ds1lrecl[1];
keylen = copyr1->ds1keyl;
/* Display original dataset information */
XMINFF (2, MSG( HHC02550, "I",
dsorg_name(copyr1->ds1dsorg),
recfm_name(&copyr1->ds1recfm),
lrecl, blksize, keylen ) );
XMINFF (2, MSG( HHC02551, "I",
copyr1->ucbtype[0], copyr1->ucbtype[1],
copyr1->ucbtype[2], copyr1->ucbtype[3],
dasd_name(copyr1->ucbtype) ) );
cyls = (copyr1->cyls[0] << 8) | copyr1->cyls[1];
heads = (copyr1->heads[0] << 8) | copyr1->heads[1];
XMINFF (2, MSG( HHC02552, "I", cyls, cyls, heads, heads ) );
return heads;
} /* end function process_copyr1 */
/*-------------------------------------------------------------------*/
/* Subroutine to process a COPYR2 header record */
/* Input: */
/* xbuf Pointer to buffer containing header record */
/* xreclen Length of header record */
/* xarray Pointer to array to receive 1-16 extent descriptions */
/* Output: */
/* The return value is the number of extents, */
/* or -1 if an error occurred. */
/* */
/* Extent information is listed if infolvl is 4 or greater. */
/*-------------------------------------------------------------------*/
static int
process_copyr2 (BYTE *xbuf, int xreclen, EXTDESC xarray[])
{
COPYR2 *copyr2 = (COPYR2*)xbuf; /* -> COPYR2 header record */
int numext; /* Number of extents */
int i; /* Array subscript */
/* Check COPYR2 record for correct length */
if (xreclen != sizeof(COPYR2))
{
XMERR ( MSG( HHC02539, "E", 2 ) );
return -1;
}
/* Get number of extents from DEB basic section */
numext = copyr2->debbasic[0];
if (numext < 1 || numext > 16)
{
XMERRF ( MSG( HHC02542, "E", numext ) );
return -1;
}
/* Copy each extent descriptor into the array */
for (i = 0; i < numext; i++)
{
xarray[i].bcyl = (copyr2->debxtent[i][6] << 8)
| copyr2->debxtent[i][7];
xarray[i].btrk = (copyr2->debxtent[i][8] << 8)
| copyr2->debxtent[i][9];
xarray[i].ecyl = (copyr2->debxtent[i][10] << 8)
| copyr2->debxtent[i][11];
xarray[i].etrk = (copyr2->debxtent[i][12] << 8)
| copyr2->debxtent[i][13];
xarray[i].ntrk = (copyr2->debxtent[i][14] << 8)
| copyr2->debxtent[i][15];
XMINFF (4, MSG( HHC02553, "I", i, xarray[i].bcyl, xarray[i].btrk,
xarray[i].ecyl, xarray[i].etrk,
xarray[i].ntrk, xarray[i].ntrk ) );
} /* end for(i) */
/* Return number of extents */
return numext;
} /* end function process_copyr2 */
/*-------------------------------------------------------------------*/
/* Subroutine to process a directory block record */
/* Input: */
/* xbuf Pointer to directory block */
/* blklen Length of directory block */
/* cyl Cylinder number of directory block in output file */
/* head Head number of directory block in output file */
/* rec Record number of directory block in output file */
/* dirblka Array of pointers to directory blocks */
/* dirblkn Number of directory blocks in dirblka array */
/* Output: */
/* dirblu Number of bytes used in directory block */
/* The return value is 0 if successful, 1 if end of directory, */
/* or -1 if an error occurred. */
/* */
/* A pointer to the directory block is saved in the dirblka array. */
/* The copy of the data block is updated with the cylinder, head, */
/* and record number of the directory block in the output file. */
/* Directory information is listed if infolvl is 3 or greater. */
/*-------------------------------------------------------------------*/
static int
process_dirblk (DATABLK *xbuf, int blklen, int cyl, int head, int rec,
DATABLK *dirblka[], int dirblkn, int *dirblu)
{
int size; /* Size of directory entry */
int i, j; /* Array subscripts */
int k; /* Userdata halfword count */
DATABLK *blkp; /* -> Copy of directory block*/
BYTE *dirptr; /* -> Next byte within block */
int dirrem; /* Number of bytes remaining */
PDSDIR *dirent; /* -> Directory entry */
char memname[9]; /* Member name (ASCIIZ) */
BYTE c, chars[25]; /* Character work areas */
char hex[49]; /* Character work areas */
set_codepage(NULL);
/* Check for end of directory */
if (blklen == 12 && memcmp(xbuf, twelvehex00, 12) == 0)
{
XMINF (3, MSG( HHC02554, "I" ) );
return 1;
}
/* Check directory block record for correct length */
if (blklen != 276)
{
XMERR ( MSG( HHC02543, "E" ) );
return -1;
}
/* Obtain storage for a copy of the directory block */
blkp = (DATABLK*)malloc(blklen);
if (blkp == NULL)
{
XMERRF ( MSG( HHC02544, "E", "a directory block", "malloc()", strerror( errno ) ) );
return -1;
}
/* Copy the directory block */
memcpy (blkp, xbuf, blklen);
/* Check that there is room in the directory block pointer array */
if (dirblkn >= MAXDBLK)
{
XMERRF ( MSG( HHC02545, "E", MAXDBLK ) );
return -1;
}
/* Add the directory block to the pointer array */
dirblka[dirblkn] = blkp;
/* Update the CCHHR in the copy of the directory block */
blkp->cyl[0] = (cyl >> 8) & 0xFF;
blkp->cyl[1] = cyl & 0xFF;
blkp->head[0] = (head >> 8) & 0xFF;
blkp->head[1] = head & 0xFF;
blkp->rec = rec;
/* Load number of bytes in directory block */
dirptr = xbuf->kdarea + 8;
dirrem = (dirptr[0] << 8) | dirptr[1];
if (dirrem < 2 || dirrem > 256)
{
XMERR (MSG( HHC02546, "E" ) );
return -1;
}
/* Return number of bytes used in directory block */
*dirblu = dirrem;
/* Point to first directory entry */
dirptr += 2;
dirrem -= 2;
/* Process each directory entry */
while (dirrem > 0)
{
/* Point to next directory entry */
dirent = (PDSDIR*)dirptr;
/* Test for end of directory */
if (memcmp(dirent->pds2name, eighthexFF, 8) == 0)
break;
/* Extract the member name */
make_asciiz (memname, sizeof(memname), dirent->pds2name, 8);
/* Display the directory entry */
XMINFF (3, MSG_C( HHC02555, "I", (dirent->pds2indc & PDS2INDC_ALIAS) ? " Alias" : "Member",
memname, dirent->pds2ttrp[0],
dirent->pds2ttrp[1], dirent->pds2ttrp[2] ) );
/* Load the user data halfword count */
k = dirent->pds2indc & PDS2INDC_LUSR;
/* Print the user data */
if (k > 0) XMINF (3, "Userdata=");
memset (hex, 0, sizeof(hex));
memset (chars, 0, sizeof(chars));
for (i = 0, j = 0; i < k*2; i++, j++)
{
if (i == 8 || i == 32 || i == 56)
{
if (i == 8)
XMINFF (3, "%-16.16s %-8.8s\n ", hex, chars);
else
XMINFF (3, "%-16.16s %-16.16s %16.16s %-24.24s\n ",
hex, hex+16, hex+32, chars);
memset (hex, 0, sizeof(hex));
memset (chars, 0, sizeof(chars));
j = 0;
}
sprintf(hex+2*j, "%2.2X", dirent->pds2usrd[i]);
c = guest_to_host(dirent->pds2usrd[i]);
if (!isprint(c)) c = '.';
chars[j] = c;
} /* end for(i) */
if (i <= 8)
XMINFF (3, "%-16.16s %-8.8s\n", hex, chars);
else
XMINFF (3, "%-16.16s %-16.16s %-16.16s %-24.24s\n",
hex, hex+16, hex+32, chars);
/* Point to next directory entry */
size = 12 + k*2;
dirptr += size;
dirrem -= size;
}
return 0;
} /* end function process_dirblk */
/*-------------------------------------------------------------------*/
/* Subroutine to replace a TTR in a PDS directory */
/* Input: */
/* memname Member name (ASCIIZ) */
/* ttrptr Pointer to TTR to be replaced */
/* ttrtab Pointer to TTR conversion table */
/* numttr Number of entries in TTR conversion table */
/* Output: */
/* The TTR is replaced using the TTR conversion table. */
/* */
/* Return value is 0 if successful, or -1 if TTR not in table. */
/* Directory information is listed if infolvl is 3 or greater. */
/*-------------------------------------------------------------------*/
static int
replace_ttr (char *memname, BYTE *ttrptr, TTRCONV *ttrtab, int numttr)
{
int i; /* Array subscript */
/* Search for the TTR in the conversion table */
for (i = 0; i < numttr; i++)
{
if (memcmp(ttrptr, ttrtab[i].origttr, 3) == 0)
{
XMINFF (4, MSG( HHC02556, "I", memname, ttrptr[0], ttrptr[1], ttrptr[2],
ttrtab[i].outpttr[0], ttrtab[i].outpttr[1],
ttrtab[i].outpttr[2] ) );
memcpy (ttrptr, ttrtab[i].outpttr, 3);
return 0;
}
}
/* Return error if TTR not found in conversion table */
XMERRF ( MSG( HHC02557, "I", memname, ttrptr[0], ttrptr[1], ttrptr[2] ) );
return -1;
} /* end function replace_ttr */
/*-------------------------------------------------------------------*/
/* Subroutine to update a note list record */
/* Input: */
/* cif -> CKD image file descriptor */
/* ofname Output file name */
/* heads Number of tracks per cylinder on output device */
/* trklen Track length of virtual output device */
/* dsstart Relative track number of start of dataset */
/* memname Member name (ASCIIZ) */
/* ttrn Pointer to TTRN of note list record */
/* ttrtab Pointer to TTR conversion table */
/* numttr Number of entries in TTR conversion table */
/* Output: */
/* Each original TTR in the note list record is replaced by the */
/* corresponding output TTR from the TTR conversion table. */
/* */
/* Return value is 0 if successful, or -1 if error. */
/*-------------------------------------------------------------------*/
static int
update_note_list (CIFBLK *cif, char *ofname, int heads, int trklen,
int dsstart, char *memname, BYTE *ttrn,
TTRCONV *ttrtab, int numttr)
{
int rc; /* Return code */
int i; /* Loop counter */
int trk; /* Relative track number */
int cyl; /* Cylinder number */
int head; /* Head number */
int rec; /* Record number */
int klen; /* Record key length */
int dlen; /* Record data length */
int numnl; /* Number of note list TTRs */
int nllen; /* Note list length */
BYTE *ttrptr; /* -> Note list TTR */
int curcyl; /* Current cylinder */
int curhead; /* Current head */
int offset; /* Offset into track buffer */
int skiplen; /* Number of bytes to skip */
CKDDASD_TRKHDR trkhdr; /* Track header */
CKDDASD_RECHDR rechdr; /* Record header */
BYTE notelist[1024]; /* Note list */
UNREFERENCED(trklen);
/* Load the TTR of the note list record */
trk = (ttrn[0] << 8) | ttrn[1];
rec = ttrn[2];
/* Load number of note list TTRs and calculate note list length */
numnl = ttrn[3];
nllen = numnl * 4;
/* Calculate the CCHHR of the note list record */
cyl = (dsstart + trk) / heads;
head = (dsstart + trk) % heads;
XMINFF (4, MSG( HHC02558, "I", memname, trk, rec, cyl, head, rec ) );
/* Save the current position in the output file */
curcyl = cif->curcyl;
curhead = cif->curhead;
/* Seek to start of track header */
rc = read_track (cif, cyl, head);
if (rc < 0)
{
XMERRF ( MSG( HHC02547, "E", ofname, cyl, cyl, head, head ) );
return -1;
}
/* Copy the track header */
memcpy (&trkhdr, cif->trkbuf, CKDDASD_TRKHDR_SIZE);
offset = CKDDASD_TRKHDR_SIZE;
/* Validate the track header */
if (trkhdr.bin != 0
|| trkhdr.cyl[0] != (cyl >> 8)
|| trkhdr.cyl[1] != (cyl & 0xFF)
|| trkhdr.head[0] != (head >> 8)
|| trkhdr.head[1] != (head & 0xFF))
{
XMERRF ( MSG( HHC02548, "E", ofname, cyl, cyl, head, head,
trkhdr.bin, trkhdr.cyl[0], trkhdr.cyl[1],
trkhdr.head[0], trkhdr.head[1] ) );
return -1;
}
/* Search for the note list record */
while (1)
{
/* Copy the next record header */
memcpy (&rechdr, cif->trkbuf + offset, CKDDASD_RECHDR_SIZE);
offset += CKDDASD_RECHDR_SIZE;
/* Check for end of track */
if (memcmp(&rechdr, eighthexFF, 8) == 0)
{
XMERRF ( MSG( HHC02549, "E", ofname, cyl, cyl, head, head, rec, rec ) );
return -1;
}
/* Extract record key length and data length */
klen = rechdr.klen;
dlen = (rechdr.dlen[0] << 8) | rechdr.dlen[1];
/* Exit loop if matching record number */
if (rechdr.rec == rec)
break;
/* Skip the key and data areas */
skiplen = klen + dlen;
offset += skiplen;
} /* end while */
/* Check that the data length is sufficient */
if (dlen < nllen)
{
XMERRF ( MSG( HHC02559, "E", memname, cyl, cyl, head, head, rec, rec, dlen, numnl ) );
return -1;
}
/* Skip the key area if present */
offset += klen;
/* Copy the note list from the data area */
memcpy (&notelist, cif->trkbuf + offset, nllen);
/* Replace the TTRs in the note list record */
ttrptr = notelist;
for (i = 0; i < numnl; i++)
{
rc = replace_ttr (memname, ttrptr, ttrtab, numttr);
if (rc < 0) return -1;
ttrptr += 4;
} /* end for(i) */
/* Copy the updated note list to the buffer */
memcpy (cif->trkbuf + offset, &notelist, nllen);
cif->trkmodif = 1;
/* Restore original file position */
rc = read_track (cif, curcyl, curhead);
if (rc < 0)
{
XMERRF ( MSG( HHC02547, "E", ofname, curcyl, curcyl, curhead, curhead ) );
return -1;
}
XMINFF (4, MSG( HHC02523, "I", cyl, cyl, head, head, rec, rec, klen, dlen ) );
return 0;
} /* end function update_note_list */
/*-------------------------------------------------------------------*/
/* Subroutine to update a directory block record */
/* Input: */
/* cif -> CKD image file descriptor */
/* ofname Output file name */
/* heads Number of tracks per cylinder on output device */
/* trklen Track length of virtual output device */
/* dsstart Relative track number of start of dataset */
/* xbuf Pointer to directory block */
/* ttrtab Pointer to TTR conversion table */
/* numttr Number of entries in TTR conversion table */
/* Output: */
/* Each original TTR in the directory block is replaced by the */
/* corresponding output TTR from the TTR conversion table. */
/* For any module which has note list entries, the note list */
/* TTRs in the note list record are also updated. */
/* */
/* Return value is 0 if successful, or -1 if any directory entry */
/* contains a TTR which is not found in the TTR conversion table. */
/*-------------------------------------------------------------------*/
static int
update_dirblk (CIFBLK *cif, char *ofname, int heads, int trklen,
int dsstart, DATABLK *xbuf,
TTRCONV *ttrtab, int numttr)
{
int rc; /* Return code */
int size; /* Size of directory entry */
int k; /* Userdata halfword count */
BYTE *dirptr; /* -> Next byte within block */
int dirrem; /* Number of bytes remaining */
PDSDIR *dirent; /* -> Directory entry */
BYTE *ttrptr; /* -> User TTR */
int n; /* Number of user TTRs */
int i; /* Loop counter */
char memname[9]; /* Member name (ASCIIZ) */
/* Load number of bytes in directory block */
dirptr = xbuf->kdarea + 8;
dirrem = (dirptr[0] << 8) | dirptr[1];
if (dirrem < 2 || dirrem > 256)
{
XMERR ( MSG( HHC02546, "E" ) );
return -1;
}
/* Point to first directory entry */
dirptr += 2;
dirrem -= 2;
/* Process each directory entry */
while (dirrem > 0)
{
/* Point to next directory entry */
dirent = (PDSDIR*)dirptr;
/* Test for end of directory */
if (memcmp(dirent->pds2name, eighthexFF, 8) == 0)
break;
/* Extract the member name */
make_asciiz (memname, sizeof(memname), dirent->pds2name, 8);
/* Replace the member TTR */
rc = replace_ttr (memname, dirent->pds2ttrp, ttrtab, numttr);
if (rc < 0) return -1;
/* Load the number of user TTRs */
n = (dirent->pds2indc & PDS2INDC_NTTR) >> PDS2INDC_NTTR_SHIFT;
/* Replace the user TTRs */
ttrptr = dirent->pds2usrd;
for (i = 0; i < n; i++)
{
rc = replace_ttr (memname, ttrptr, ttrtab, numttr);
if (rc < 0) return -1;
ttrptr += 4;
} /* end for(i) */
/* Update the note list record if note list TTRs exist */
if ((dirent->pds2indc & PDS2INDC_ALIAS) == 0
&& n >= 2 && dirent->pds2usrd[7] != 0)
{
rc = update_note_list (cif, ofname, heads, trklen,
dsstart, memname, dirent->pds2usrd+4,
ttrtab, numttr);
if (rc < 0) return -1;
}
/* Load the user data halfword count */
k = dirent->pds2indc & PDS2INDC_LUSR;
/* Point to next directory entry */
size = 12 + k*2;
dirptr += size;
dirrem -= size;
}
return 0;
} /* end function update_dirblk */
/*-------------------------------------------------------------------*/
/* Subroutine to read an IEBCOPY file and write to DASD image file */
/* Input: */
/* xfname XMIT input file name */
/* ofname DASD image file name */
/* cif -> CKD image file descriptor */
/* devtype Output device type */
/* heads Output device number of tracks per cylinder */
/* trklen Output device virtual track length */
/* outcyl Output starting cylinder number */
/* outhead Output starting head number */
/* maxtrks Maximum extent size in tracks */
/* method METHOD_XMIT or METHOD_VS */
/* Output: */
/* odsorg Dataset organization */
/* orecfm Record format */
/* olrecl Logical record length */
/* oblksz Block size */
/* okeyln Key length */
/* dirblu Bytes used in last directory block */
/* lastrec Record number of last block written */
/* trkbal Number of bytes remaining on last track */
/* numtrks Number of tracks written */
/* nxtcyl Starting cylinder number for next dataset */
/* nxthead Starting head number for next dataset */
/*-------------------------------------------------------------------*/
static int
process_iebcopy_file (char *xfname, char *ofname, CIFBLK *cif,
U16 devtype, int heads, int trklen,
int outcyl, int outhead, int maxtrks,
BYTE method,
BYTE *odsorg, BYTE *orecfm,
int *olrecl, int *oblksz, int *okeyln,
int *dirblu, int *lastrec, int *trkbal,
int *numtrks, int *nxtcyl, int *nxthead)
{
int rc = 0; /* Return code */
int i; /* Array subscript */
int xfd; /* XMIT file descriptor */
int dsstart; /* Relative track number of
start of output dataset */
BYTE *xbuf; /* -> Logical record buffer */
int xreclen; /* Logical record length */
BYTE xctl; /* 0x20=Control record */
char xrecname[8]; /* XMIT control record name */
int datarecn = 0; /* Data record counter */
int datafiln = 0; /* Data file counter */
int copyfiln = 0; /* Seq num of file to copy */
BYTE dsorg=0; /* Dataset organization */
BYTE recfm=0; /* Dataset record format */
U16 lrecl=0; /* Dataset record length */
U16 blksz=0; /* Dataset block size */
U16 keyln=0; /* Dataset key length */
U16 dirnm; /* Number of directory blocks*/
int enddir = 0; /* 1=End of directory found */
BYTE *blkptr; /* -> Data block in record */
DATABLK *datablk; /* -> Data block */
int blktrk; /* Data block relative track */
int blkcyl; /* Data block cylinder number*/
int blkhead; /* Data block head number */
int blkrec; /* Data block record number */
int keylen; /* Key length of data block */
int datalen; /* Data length of data block */
int blklen; /* Total length of data block*/
int origheads = 0; /* Number of tracks/cylinder
on original dataset */
int numext = 0; /* Number of extents */
EXTDESC xarray[16]; /* Extent descriptor array */
DATABLK **dirblka; /* -> Directory block array */
int dirblkn = 0; /* #of directory blocks read */
int outusedv = 0; /* Output bytes used on track
of virtual device */
int outusedr = 0; /* Output bytes used on track
of real device */
int outtrkbr = 0; /* Output bytes remaining on
track of real device */
int outtrk = 0; /* Output relative track */
int outrec = 0; /* Output record number */
TTRCONV *ttrtab; /* -> TTR conversion table */
int numttr = 0; /* TTR table array index */
COPYR1 *copyr1; /* -> header record 1 */
char pathname[MAX_PATH]; /* xfname in host path format*/
/* Open the input file */
hostpath(pathname, xfname, sizeof(pathname));
xfd = HOPEN (pathname, O_RDONLY|O_BINARY);
if (xfd < 0)
{
XMERRF ( MSG( HHC02468, "E", xfname, "open()", strerror( errno ) ) );
return -1;
}
/* Obtain the input logical record buffer */
xbuf = malloc (65536);
if (xbuf == NULL)
{
XMERRF ( MSG( HHC02544, "E", "input buffer", "malloc()", strerror(errno) ) );
close (xfd);
return -1;
}
/* Obtain storage for the directory block array */
dirblka = (DATABLK**)malloc (sizeof(DATABLK*) * MAXDBLK);
if (dirblka == NULL)
{
XMERRF ( MSG( HHC02544, "E", "directory block array", "malloc()", strerror(errno) ) );
free (xbuf);
close (xfd);
return -1;
}
/* Obtain storage for the TTR conversion table */
ttrtab = (TTRCONV*)malloc (sizeof(TTRCONV) * MAXTTR);
if (ttrtab == NULL)
{
XMERRF ( MSG( HHC02544, "E", "TTR table", "malloc()", strerror(errno) ) );
free (xbuf);
free (dirblka);
close (xfd);
return -1;
}
/* Calculate the relative track number of the dataset */
dsstart = (outcyl * heads) + outhead;
/* Display the file information message */
XMINFF (1, MSG( HHC02560, "I", xfname ) );
/* Read each logical record */
while (1)
{
xctl=0;
if (method == METHOD_XMIT)
rc = read_xmit_rec (xfd, xfname, xbuf, &xctl);
else if (method == METHOD_VS) {
rc = read_vs_rec (xfd, xfname, xbuf, datarecn);
if (rc == 0) /* end-of-file */
break;
} else
rc = -1;
if (rc < 0) return -1;
xreclen = rc;
/* Process control records */
if (method == METHOD_XMIT && xctl)
{
/* Extract the control record name */
make_asciiz (xrecname, sizeof(xrecname), xbuf, 6);
XMINFF (4, MSG( HHC02561, "I", xrecname, xreclen ) );
/* Exit if control record is a trailer record */
if (strcmp(xrecname, "INMR06") == 0)
break;
/* Process control record according to type */
if (strcmp(xrecname, "INMR02") == 0)
{
rc = process_inmr02 (xbuf, xreclen, &copyfiln,
&dsorg, &recfm, &lrecl, &blksz,
&keyln, &dirnm);
if (rc < 0) return -1;
}
else
{
rc = process_inmrxx (xbuf, xreclen);
if (rc < 0) return -1;
}
/* Reset the data counter if data control record */
if (strcmp(xrecname, "INMR03") == 0)
{
datafiln++;
datarecn = 0;
XMINFF (4, MSG( HHC02562, "I", datafiln, datafiln == copyfiln ? "(selected)" : "(not selected)" ) );
}
/* Loop to get next record */
continue;
} /* end if(xctl) */
/* Process data records */
datarecn++;
XMINFF (4, MSG( HHC02563, "I", xreclen ) );
if (infolvl >= 5) data_dump (xbuf, xreclen);
/* If this is not the IEBCOPY file then ignore data record */
if (method == METHOD_XMIT && datafiln != copyfiln)
{
continue;
}
/* Process IEBCOPY header record 1 */
if (datarecn == 1)
{
origheads = process_copyr1 (xbuf, xreclen);
if (origheads < 0) exit(1);
if (method == METHOD_VS) {
copyr1 = (COPYR1 *)xbuf;
dsorg = copyr1->ds1dsorg[0];
recfm = copyr1->ds1recfm;
lrecl = (copyr1->ds1lrecl[0] << 8) | copyr1->ds1lrecl[1];
blksz = (copyr1->ds1blkl[0] << 8) | copyr1->ds1blkl[1];
keyln = copyr1->ds1keyl;
}
continue;
}
/* Process IEBCOPY header record 2 */
if (datarecn == 2)
{
numext = process_copyr2 (xbuf, xreclen, xarray);
if (numext < 0) exit(1);
continue;
}
/* Process each data block in data record */
blkptr = xbuf;
while (xreclen > 0)
{
/* Compute the length of the block */
datablk = (DATABLK*)blkptr;
blkcyl = (datablk->cyl[0] << 8)
| datablk->cyl[1];
blkhead = (datablk->head[0] << 8)
| datablk->head[1];
blkrec = datablk->rec;
keylen = datablk->klen;
datalen = (datablk->dlen[0] << 8)
| datablk->dlen[1];
blklen = 12 + keylen + datalen;
/* Calculate the TTR in the original dataset */
blktrk = (enddir == 0) ? 0 :
calculate_ttr (blkcyl, blkhead,
origheads, numext, xarray);
/* Write the data block to the output file */
rc = write_block (cif, ofname, datablk, keylen, datalen,
devtype, heads, trklen, maxtrks,
&outusedv, &outusedr, &outtrkbr,
&outtrk, &outcyl, &outhead, &outrec);
if (rc < 0)
{
XMERRF ( MSG( HHC02570, "E", ofname, "write_block()",
blkcyl, blkhead, blkrec,
blktrk, blkrec, keylen, datalen ) );
return -1;
}
XMINFF (4, MSG( HHC02564, "I", blkcyl, blkhead, blkrec, blktrk, blkrec, keylen, datalen,
outcyl, outhead, outrec, outtrk, outrec ) );
/* Process directory block or member block */
if (enddir == 0)
{
rc = process_dirblk (datablk, blklen,
outcyl, outhead, outrec,
dirblka, dirblkn, dirblu);
if (rc < 0) return -1;
enddir = rc;
/* Count the number of directory blocks read */
if (enddir == 0) dirblkn++;
}
else /* Not a directory block */
{
/* Check that TTR conversion table is not full */
if (numttr >= MAXTTR)
{
XMERRF ( MSG( HHC02571, "E", MAXTTR ) );
return -1;
}
/* Add an entry to the TTR conversion table */
ttrtab[numttr].origttr[0] = (blktrk >> 8) & 0xFF;
ttrtab[numttr].origttr[1] = blktrk & 0xFF;
ttrtab[numttr].origttr[2] = blkrec;
ttrtab[numttr].outpttr[0] = (outtrk >> 8) & 0xFF;
ttrtab[numttr].outpttr[1] = outtrk & 0xFF;
ttrtab[numttr].outpttr[2] = outrec;
numttr++;
}
/* Point to next data block in data record */
xreclen -= blklen;
blkptr += blklen;
} /* end while(xreclen) */
} /* end while(1) */
/* Check for unsupported xmit utility */
if (method == METHOD_XMIT && copyfiln == 0)
{
XMERRF ( MSG( HHC02572, "W", xfname ) );
}
/* Return the last record number and track balance */
*lastrec = outrec;
*trkbal = outtrkbr;
/* Write any data remaining in track buffer */
rc = write_track (cif, ofname, heads, trklen,
&outusedv, &outtrk, &outcyl, &outhead);
if (rc < 0) return -1;
/* Update the directory and rewrite to output file */
for (i = 0; i < dirblkn; i++)
{
/* Obtain the directory block pointer from the array */
datablk = dirblka[i];
/* Update TTR pointers in this directory block */
rc = update_dirblk (cif, ofname, heads, trklen, dsstart,
datablk, ttrtab, numttr);
if (rc < 0) return -1;
/* Rewrite the updated directory block */
blkcyl = (datablk->cyl[0] << 8) | datablk->cyl[1];
blkhead = (datablk->head[0] << 8) | datablk->head[1];
blkrec = datablk->rec;
keylen = datablk->klen;
datalen = (datablk->dlen[0] << 8) | datablk->dlen[1];
rc = update_block (cif, ofname, datablk, blkcyl, blkhead,
blkrec, keylen, datalen, heads, trklen);
if (rc < 0) return -1;
} /* end for(i) */
/* Close input file and release buffers */
close (xfd);
for (i = 0; i < dirblkn; i++)
free (dirblka[i]);
free (dirblka);
free (xbuf);
free (ttrtab);
/* Return the dataset attributes */
*odsorg = dsorg;
*orecfm = recfm;
*olrecl = lrecl;
*oblksz = blksz;
*okeyln = keyln;
/* Return number of tracks and starting address of next dataset */
*numtrks = outtrk;
*nxtcyl = outcyl;
*nxthead = outhead;
return 0;
} /* end function process_iebcopy_file */
/*-------------------------------------------------------------------*/
/* Subroutine to initialize a SYSCTLG dataset as an OS CVOL */
/* Input: */
/* ofname DASD image file name */
/* cif -> CKD image file descriptor */
/* volser Volume serial number */
/* devtype Output device type */
/* heads Output device number of tracks per cylinder */
/* trklen Output device virtual track length */
/* outcyl Output starting cylinder number */
/* outhead Output starting head number */
/* extsize Extent size in tracks */
/* Output: */
/* lastrec Record number of last block written */
/* trkbal Number of bytes remaining on last track */
/* numtrks Number of tracks written */
/* nxtcyl Starting cylinder number for next dataset */
/* nxthead Starting head number for next dataset */
/* Note: */
/* This subroutine builds a minimal SYSCTLG containing only */
/* the entries required on an OS/360 IPL volume. */
/*-------------------------------------------------------------------*/
static int
cvol_initialize (char *ofname, CIFBLK *cif, char *volser,
U16 devtype, int heads, int trklen,
int outcyl, int outhead, int extsize,
int *lastrec, int *trkbal,
int *numtrks, int *nxtcyl, int *nxthead)
{
int rc; /* Return code */
int i; /* Array subscript */
int keylen; /* Key length of data block */
int datalen; /* Data length of data block */
int outusedv = 0; /* Output bytes used on track
of virtual device */
int outusedr = 0; /* Output bytes used on track
of real device */
int outtrkbr = 0; /* Output bytes remaining on
track of real device */
int outtrk = 0; /* Output relative track */
int outrec = 0; /* Output record number */
int blkptrk; /* Number of blocks per track*/
int totblks; /* Number of blocks in CVOL */
int bytes; /* Bytes used in this block */
U32 ucbtype; /* UCB device type */
PDSDIR *catent; /* -> Catalog entry */
DATABLK datablk; /* Data block */
#define NUM_SYS1_DATASETS 8 /* Number of SYS1 datasets */
static char *sys1name[NUM_SYS1_DATASETS] =
{"DUMP", "IMAGELIB", "LINKLIB", "NUCLEUS",
"PARMLIB", "PROCLIB", "SAMPLIB", "SYSJOBQE"};
/* Set the key length and data length for SYSCTLG dataset */
keylen = 8;
datalen = 256;
/* Obtain the number of blocks which will fit on a track */
capacity_calc (cif, 0, keylen, datalen, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, &blkptrk, NULL, NULL);
/* Calculate the total number of blocks in the catalog */
totblks = extsize * blkptrk;
/* Get the UCB device type */
ucbtype = ucbtype_code (devtype);
/*-----------------------------------*/
/* Initialize the volume index block */
/*-----------------------------------*/
memset (datablk.kdarea, 0, keylen + datalen);
/* The key field contains all X'FF' */
memcpy (datablk.kdarea, eighthexFF, 8);
/* The first entry begins after the 2 byte count field */
bytes = 2;
catent = (PDSDIR*)(datablk.kdarea + keylen + bytes);
/* Build the volume index control entry (VICE) */
/* The VICE name is X'0000000000000001' */
memcpy (catent->pds2name, cvol_low_key, 8);
/* Set TTR to highest block in volume index, i.e. X'000001' */
catent->pds2ttrp[0] = 0;
catent->pds2ttrp[1] = 0;
catent->pds2ttrp[2] = 1;
/* Indicator byte X'05' means 5 user halfwords follow, and
uniquely identifies this catalog entry as a VICE */
catent->pds2indc = 5;
/* Set the TTR of the last block of the catalog */
catent->pds2usrd[0] = ((extsize - 1) >> 8) & 0xFF;
catent->pds2usrd[1] = (extsize - 1) & 0xFF;
catent->pds2usrd[2] = blkptrk;
catent->pds2usrd[3] = 0;
/* Set the TTR of the first unused block (X'000003') */
catent->pds2usrd[4] = 0;
catent->pds2usrd[5] = 0;
catent->pds2usrd[6] = 3;
/* Remainder of user data is 0 */
catent->pds2usrd[7] = 0;
catent->pds2usrd[8] = 0;
catent->pds2usrd[9] = 0;
/* Increment bytes used by the length of the VICE */
bytes += 22;
catent = (PDSDIR*)(datablk.kdarea + keylen + bytes);
/* Build the index pointer for SYS1 */
convert_to_ebcdic (catent->pds2name, 8, "SYS1");
/* Set TTR of the SYS1 index block, i.e. X'000002' */
catent->pds2ttrp[0] = 0;
catent->pds2ttrp[1] = 0;
catent->pds2ttrp[2] = 2;
/* Indicator byte X'00' means no user halfwords follow, and
uniquely identifies this catalog entry as an index pointer */
catent->pds2indc = 0;
/* Increment bytes used by the length of the index pointer */
bytes += 12;
catent = (PDSDIR*)(datablk.kdarea + keylen + bytes);
/* Set the last entry in block marker */
memcpy (catent->pds2name, eighthexFF, 8);
/* Increment bytes used by the last entry marker */
bytes += 12;
catent = (PDSDIR*)(datablk.kdarea + keylen + bytes);
/* Set the number of bytes used in this block */
datablk.kdarea[keylen+0] = (bytes >> 8) & 0xFF;
datablk.kdarea[keylen+1] = bytes & 0xFF;
/* Write the volume index block to the output file */
rc = write_block (cif, ofname, &datablk, keylen, datalen,
devtype, heads, trklen, extsize,
&outusedv, &outusedr, &outtrkbr,
&outtrk, &outcyl, &outhead, &outrec);
if (rc < 0) return -1;
XMINFF (4, MSG( HHC02565, "I", outcyl, outcyl, outhead, outhead, outrec, outrec ) );
if (infolvl >= 5)
data_dump (datablk.kdarea, keylen + datalen);
/* Count number of blocks written */
totblks--;
/*---------------------------------*/
/* Initialize the SYS1 index block */
/*---------------------------------*/
memset (datablk.kdarea, 0, keylen + datalen);
/* The key field contains all X'FF' */
memcpy (datablk.kdarea, eighthexFF, 8);
/* The first entry begins after the 2 byte count field */
bytes = 2;
catent = (PDSDIR*)(datablk.kdarea + keylen + bytes);
/* Build the index control entry (ICE) */
/* The ICE name is X'0000000000000001' */
memcpy (catent->pds2name, cvol_low_key, 8);
/* Set TTR to highest block in this index, i.e. X'000002' */
catent->pds2ttrp[0] = 0;
catent->pds2ttrp[1] = 0;
catent->pds2ttrp[2] = 2;
/* Indicator byte X'03' means 3 user halfwords follow, and
uniquely identifies this catalog entry as an ICE */
catent->pds2indc = 3;
/* Set the TTR of this block */
catent->pds2usrd[0] = 0;
catent->pds2usrd[1] = 0;
catent->pds2usrd[2] = 2;
/* The next byte contains the alias count */
catent->pds2usrd[3] = 0;
/* The remaining 2 bytes of userdata are zeroes */
catent->pds2usrd[4] = 0;
catent->pds2usrd[5] = 0;
/* Increment bytes used by the length of the ICE */
bytes += 18;
/* Build the dataset pointers for SYS1.xxxxxxxx datasets */
for (i = 0; i < NUM_SYS1_DATASETS; i++)
{
/* Point to next dataset pointer entry */
catent = (PDSDIR*)(datablk.kdarea + keylen + bytes);
/* Set the name of the dataset pointer entry */
convert_to_ebcdic (catent->pds2name, 8, sys1name[i]);
/* Set the TTR to zero */
catent->pds2ttrp[0] = 0;
catent->pds2ttrp[1] = 0;
catent->pds2ttrp[2] = 0;
/* Indicator byte X'07' means 7 user halfwords follow, and
uniquely identifies the entry as a dataset pointer */
catent->pds2indc = 7;
/* The next two bytes contain the volume count (X'0001') */
catent->pds2usrd[0] = 0;
catent->pds2usrd[1] = 1;
/* The next four bytes contain the UCB type */
catent->pds2usrd[2] = (ucbtype >> 24) & 0xFF;
catent->pds2usrd[3] = (ucbtype >> 16) & 0xFF;
catent->pds2usrd[4] = (ucbtype >> 8) & 0xFF;
catent->pds2usrd[5] = ucbtype & 0xFF;
/* The next six bytes contain the volume serial number */
convert_to_ebcdic (catent->pds2usrd+6, 6, volser);
/* The next two bytes contain the volume seq.no. (X'0000') */
catent->pds2usrd[12] = 0;
catent->pds2usrd[13] = 0;
/* Increment bytes used by the length of the dataset pointer */
bytes += 26;
} /* end for(i) */
/* Point to last entry in block */
catent = (PDSDIR*)(datablk.kdarea + keylen + bytes);
/* Set the last entry in block marker */
memcpy (catent->pds2name, eighthexFF, 8);
/* Increment bytes used by the last entry marker */
bytes += 12;
catent = (PDSDIR*)(datablk.kdarea + keylen + bytes);
/* Set the number of bytes used in this block */
datablk.kdarea[keylen+0] = (bytes >> 8) & 0xFF;
datablk.kdarea[keylen+1] = bytes & 0xFF;
/* Write the index block to the output file */
rc = write_block (cif, ofname, &datablk, keylen, datalen,
devtype, heads, trklen, extsize,
&outusedv, &outusedr, &outtrkbr,
&outtrk, &outcyl, &outhead, &outrec);
if (rc < 0) return -1;
XMINFF (4, MSG( HHC02565, "I", outcyl, outcyl, outhead, outhead, outrec, outrec ) );
if (infolvl >= 5) data_dump (datablk.kdarea, keylen + datalen);
/* Count number of blocks written */
totblks--;
/*--------------------------------------------*/
/* Initialize remaining unused catalog blocks */
/*--------------------------------------------*/
while (totblks > 0)
{
memset (datablk.kdarea, 0, keylen + datalen);
/* Write the volume index block to the output file */
rc = write_block (cif, ofname, &datablk, keylen, datalen,
devtype, heads, trklen, extsize,
&outusedv, &outusedr, &outtrkbr,
&outtrk, &outcyl, &outhead, &outrec);
if (rc < 0) return -1;
XMINFF (4, MSG( HHC02565, "I", outcyl, outcyl, outhead, outhead, outrec, outrec ) );
if (infolvl >= 5) data_dump (datablk.kdarea, keylen + datalen);
/* Count number of blocks written */
totblks--;
} /* end while(totblks) */
/* Set the last record number to X'FF' so that OS/360 catalog
management routines can recognize that the CVOL has been
initialized by detecting X'FF' at ds1lstar+2 in the VTOC */
*lastrec = 0xFF;
/* Return the track balance */
*trkbal = outtrkbr;
/* Write data remaining in track buffer */
rc = write_track (cif, ofname, heads, trklen,
&outusedv, &outtrk, &outcyl, &outhead);
if (rc < 0) return -1;
/* Return number of tracks and starting address of next dataset */
*numtrks = outtrk;
*nxtcyl = outcyl;
*nxthead = outhead;
return 0;
} /* end function cvol_initialize */
/*-------------------------------------------------------------------*/
/* Subroutine to initialize a LOGREC dataset with IFCDIP00 header */
/* Input: */
/* ofname DASD image file name */
/* cif -> CKD image file descriptor */
/* devtype Output device type */
/* heads Output device number of tracks per cylinder */
/* trklen Output device virtual track length */
/* outcyl Output starting cylinder number */
/* outhead Output starting head number */
/* extsize Extent size in tracks */
/* Output: */
/* lastrec Record number of last block written */
/* trkbal Number of bytes remaining on last track */
/* numtrks Number of tracks written */
/* nxtcyl Starting cylinder number for next dataset */
/* nxthead Starting head number for next dataset */
/*-------------------------------------------------------------------*/
static int
dip_initialize (char *ofname, CIFBLK *cif,
U16 devtype, int heads, int trklen,
int outcyl, int outhead, int extsize,
int *lastrec, int *trkbal,
int *numtrks, int *nxtcyl, int *nxthead)
{
int rc; /* Return code */
int keylen; /* Key length of data block */
int datalen; /* Data length of data block */
int outusedv = 0; /* Output bytes used on track
of virtual device */
int outusedr = 0; /* Output bytes used on track
of real device */
int outtrkbr = 0; /* Output bytes remaining on
track of real device */
int outtrk = 0; /* Output relative track */
int outrec = 0; /* Output record number */
int remlen; /* Bytes remaining on 1st trk*/
int physlen; /* Physical track length */
int lasthead; /* Highest head on cylinder */
int endcyl; /* Extent end cylinder */
int endhead; /* Extent end head */
int trklen90; /* 90% of track length */
int cyl90; /* 90% full cylinder number */
int head90; /* 90% full head number */
int reltrk90; /* 90% full relative track */
DIPHDR *diphdr; /* -> Record in data block */
DATABLK datablk; /* Data block */
/* Set the key length and data length for the header record */
keylen = 0;
datalen = sizeof(DIPHDR);
/* Obtain the physical track size and the track balance
remaining on the first track after the header record */
capacity_calc (cif, 0, keylen, datalen, NULL, &remlen,
&physlen, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL);
/* Calculate the end of extent cylinder and head */
lasthead = heads - 1;
endcyl = outcyl;
endhead = outhead + extsize - 1;
while (endhead >= heads)
{
endhead -= heads;
endcyl++;
}
/* Calculate the 90% full cylinder and head */
trklen90 = physlen * 9 / 10;
reltrk90 = extsize * trklen90 / physlen;
if (reltrk90 == 0) reltrk90 = 1;
cyl90 = outcyl;
head90 = outhead + reltrk90 - 1;
while (head90 >= heads)
{
head90 -= heads;
cyl90++;
}
/* Initialize the DIP header record */
diphdr = (DIPHDR*)(datablk.kdarea);
memset (diphdr, 0, sizeof(DIPHDR));
diphdr->recid[0] = 0xFF;
diphdr->recid[1] = 0xFF;
diphdr->bcyl[0] = (outcyl >> 8) & 0xFF;
diphdr->bcyl[1] = outcyl & 0xFF;
diphdr->btrk[0] = (outhead >> 8) & 0xFF;
diphdr->btrk[1] = outhead & 0xFF;
diphdr->ecyl[0] = (endcyl >> 8) & 0xFF;
diphdr->ecyl[1] = endcyl & 0xFF;
diphdr->etrk[0] = (endhead >> 8) & 0xFF;
diphdr->etrk[1] = endhead & 0xFF;
diphdr->restart[2] = (outcyl >> 8) & 0xFF;
diphdr->restart[3] = outcyl & 0xFF;
diphdr->restart[4] = (outhead >> 8) & 0xFF;
diphdr->restart[5] = outhead & 0xFF;
diphdr->restart[6] = 1;
diphdr->trkbal[0] = (remlen >> 8) & 0xFF;
diphdr->trkbal[1] = remlen & 0xFF;
diphdr->trklen[0] = (physlen >> 8) & 0xFF;
diphdr->trklen[1] = physlen & 0xFF;
diphdr->reused[2] = (outcyl >> 8) & 0xFF;
diphdr->reused[3] = outcyl & 0xFF;
diphdr->reused[4] = (outhead >> 8) & 0xFF;
diphdr->reused[5] = outhead & 0xFF;
diphdr->reused[6] = 1;
diphdr->lasthead[0] = (lasthead >> 8) & 0xFF;
diphdr->lasthead[1] = lasthead & 0xFF;
diphdr->trklen90[0] = (trklen90 >> 8) & 0xFF;
diphdr->trklen90[1] = trklen90 & 0xFF;
diphdr->devcode = (ucbtype_code(devtype) & 0x0F) | 0xF0;
diphdr->cchh90[0] = (cyl90 >> 8) & 0xFF;
diphdr->cchh90[1] = cyl90 & 0xFF;
diphdr->cchh90[2] = (head90 >> 8) & 0xFF;
diphdr->cchh90[3] = head90 & 0xFF;
diphdr->endid = 0xFF;
/* Write the data block to the output file */
rc = write_block (cif, ofname, &datablk, keylen, datalen,
devtype, heads, trklen, extsize,
&outusedv, &outusedr, &outtrkbr,
&outtrk, &outcyl, &outhead, &outrec);
if (rc < 0) return -1;
XMINFF (3, MSG( HHC02566, "I", outcyl, outcyl, outhead, outhead, outrec, outrec ) );
if (infolvl >= 5) data_dump (diphdr, sizeof(DIPHDR));
/* Return the last record number and track balance */
*lastrec = outrec;
*trkbal = outtrkbr;
/* Write data remaining in track buffer */
rc = write_track (cif, ofname, heads, trklen,
&outusedv, &outtrk, &outcyl, &outhead);
if (rc < 0) return -1;
/* Return number of tracks and starting address of next dataset */
*numtrks = outtrk;
*nxtcyl = outcyl;
*nxthead = outhead;
return 0;
} /* end function dip_initialize */
/*-------------------------------------------------------------------*/
/* Subroutine to initialize a sequential dataset */
/* Input: */
/* sfname SEQ input file name */
/* ofname DASD image file name */
/* cif -> CKD image file descriptor */
/* devtype Output device type */
/* heads Output device number of tracks per cylinder */
/* trklen Output device virtual track length */
/* outcyl Output starting cylinder number */
/* outhead Output starting head number */
/* extsize Extent size in tracks */
/* dsorg Dataset organization (DA or PS) */
/* recfm Record Format (F or FB) */
/* lrecl Record length */
/* blksz Block size */
/* keyln Key length */
/* Output: */
/* lastrec Record number of last block written */
/* trkbal Number of bytes remaining on last track */
/* numtrks Number of tracks written */
/* nxtcyl Starting cylinder number for next dataset */
/* nxthead Starting head number for next dataset */
/*-------------------------------------------------------------------*/
static int
seq_initialize (char *sfname, char *ofname, CIFBLK *cif, U16 devtype,
int heads, int trklen, int outcyl, int outhead,
int extsize, BYTE dsorg, BYTE recfm,
int lrecl, int blksz, int keyln,
int *lastrec, int *trkbal,
int *numtrks, int *nxtcyl, int *nxthead)
{
int rc; /* Return code */
int sfd; /* Input seq file descriptor */
int size; /* Size left in input file */
int outusedv = 0; /* Output bytes used on track
of virtual device */
int outusedr = 0; /* Output bytes used on track
of real device */
int outtrkbr = 0; /* Output bytes remaining on
track of real device */
int outtrk = 0; /* Output relative track */
int outrec = 0; /* Output record number */
struct stat st; /* Data area for fstat() */
DATABLK datablk; /* Data block */
char pathname[MAX_PATH]; /* sfname in host path format*/
/* Perform some checks */
if (!(dsorg & DSORG_PS) && !(dsorg & DSORG_DA))
{
XMERRF ( MSG( HHC02573, "E", sfname, dsorg ) );
return -1;
}
if (recfm != RECFM_FORMAT_F && recfm != (RECFM_FORMAT_F|RECFM_BLOCKED))
{
XMERRF ( MSG( HHC02574, "E", sfname, recfm ) );
return -1;
}
if (blksz == 0) blksz = lrecl;
if (lrecl == 0) lrecl = blksz;
if (lrecl == 0 || blksz % lrecl != 0
|| (blksz != lrecl && recfm == RECFM_FORMAT_F))
{
XMERRF ( MSG( HHC02575, "E", sfname, lrecl, blksz ) );
return -1;
}
if (keyln > 0 && blksz > lrecl)
{
XMERR ( MSG( HHC02576, "E", sfname, keyln ) );
return -1;
}
/* Open the input file */
hostpath(pathname, sfname, sizeof(pathname));
sfd = HOPEN (pathname, O_RDONLY|O_BINARY);
if (sfd < 0)
{
XMERRF ( MSG( HHC02468, "E", sfname, "open()", strerror(errno) ) );
return -1;
}
/* Get input file status */
rc = fstat(sfd, &st);
if (rc < 0)
{
XMERRF ( MSG( HHC02468, "E", sfname, "fstat()", strerror(errno) ) );
close (sfd);
return -1;
}
size = st.st_size;
/* Read the first track */
rc = read_track (cif, *nxtcyl, *nxthead);
if (rc < 0)
{
XMERRF ( MSG( HHC02547, "E", ofname, *nxtcyl, *nxtcyl, *nxthead, *nxthead ) );
close (sfd);
return -1;
}
while (size > 0)
{
/* Read a block of data from the input file */
rc = read (sfd, &datablk.kdarea, blksz < size ? blksz : size);
if (rc < (blksz < size ? blksz : size))
{
XMERRF ( MSG( HHC02468, "E", sfname, "read()", strerror(errno) ) );
close (sfd);
return -1;
}
size -= rc;
/* Pad the block if necessary */
if (rc < blksz)
{
/* Adjust blksize down to next
highest multiple of lrecl */
blksz = (((rc-1) / lrecl) + 1) * lrecl;
memset (&datablk.kdarea[rc], 0, blksz - rc);
}
rc = write_block (cif, ofname, &datablk, keyln, blksz - keyln,
devtype, heads, trklen, extsize,
&outusedv, &outusedr, &outtrkbr,
&outtrk, &outcyl, &outhead, &outrec);
if (rc < 0)
{
close (sfd);
return -1;
}
}
/* Close the input file */
close (sfd);
/* Create the end of file record */
rc = write_block (cif, ofname, &datablk, 0, 0,
devtype, heads, trklen, extsize,
&outusedv, &outusedr, &outtrkbr,
&outtrk, &outcyl, &outhead, &outrec);
if (rc < 0) return -1;
/* Return the last record number and track balance */
*lastrec = outrec;
*trkbal = outtrkbr;
/* Write data remaining in track buffer */
rc = write_track (cif, ofname, heads, trklen,
&outusedv, &outtrk, &outcyl, &outhead);
if (rc < 0) return -1;
/* Return number of tracks and starting address of next dataset */
*numtrks = outtrk;
*nxtcyl = outcyl;
*nxthead = outhead;
return 0;
} /* end function seq_initialize */
/*-------------------------------------------------------------------*/
/* Subroutine to initialize an empty dataset */
/* Input: */
/* ofname DASD image file name */
/* cif -> CKD image file descriptor */
/* devtype Output device type */
/* heads Output device number of tracks per cylinder */
/* trklen Output device virtual track length */
/* outcyl Output starting cylinder number */
/* outhead Output starting head number */
/* extsize Extent size in tracks */
/* dsorg Dataset organization */
/* dirblks Number of directory blocks */
/* Output: */
/* dirblu Bytes used in last directory block */
/* lastrec Record number of last block written */
/* trkbal Number of bytes remaining on last track */
/* numtrks Number of tracks written */
/* nxtcyl Starting cylinder number for next dataset */
/* nxthead Starting head number for next dataset */
/*-------------------------------------------------------------------*/
static int
empty_initialize (char *ofname, CIFBLK *cif, U16 devtype,
int heads, int trklen, int outcyl, int outhead,
int extsize, BYTE dsorg, int dirblks,
int *dirblu, int *lastrec, int *trkbal,
int *numtrks, int *nxtcyl, int *nxthead)
{
int rc; /* Return code */
int i; /* Loop counter */
int keylen; /* Key length of data block */
int datalen; /* Data length of data block */
int outusedv = 0; /* Output bytes used on track
of virtual device */
int outusedr = 0; /* Output bytes used on track
of real device */
int outtrkbr = 0; /* Output bytes remaining on
track of real device */
int outdblu = 0; /* Output bytes used in last
directory block */
int outtrk = 0; /* Output relative track */
int outrec = 0; /* Output record number */
DATABLK datablk; /* Data block */
/* Initialize the directory if dataset is a PDS */
if (dsorg & DSORG_PO)
{
/* Build the first directory block */
keylen = 8;
datalen = 256;
outdblu = 14;
memset (datablk.kdarea, 0, keylen + datalen);
memcpy (datablk.kdarea, eighthexFF, 8);
datablk.kdarea[keylen] = (outdblu >> 8);
datablk.kdarea[keylen+1] = outdblu & 0xFF;
memcpy (datablk.kdarea + keylen + 2, eighthexFF, 8);
/* Write directory blocks to output dataset */
for (i = 0; i < dirblks; i++)
{
/* Write a directory block */
rc = write_block (cif, ofname, &datablk, keylen, datalen,
devtype, heads, trklen, extsize,
&outusedv, &outusedr, &outtrkbr,
&outtrk, &outcyl, &outhead, &outrec);
if (rc < 0) return -1;
/* Clear subsequent directory blocks to zero */
memset (datablk.kdarea, 0, keylen + datalen);
} /* end for(i) */
} /* end if(DSORG_PO) */
/* Create the end of file record */
keylen = 0;
datalen = 0;
rc = write_block (cif, ofname, &datablk, keylen, datalen,
devtype, heads, trklen, extsize,
&outusedv, &outusedr, &outtrkbr,
&outtrk, &outcyl, &outhead, &outrec);
if (rc < 0) return -1;
/* Return number of bytes used in last directory block */
*dirblu = outdblu;
/* Return the last record number and track balance */
*lastrec = outrec;
*trkbal = outtrkbr;
/* Write data remaining in track buffer */
rc = write_track (cif, ofname, heads, trklen,
&outusedv, &outtrk, &outcyl, &outhead);
if (rc < 0) return -1;
/* Return number of tracks and starting address of next dataset */
*numtrks = outtrk;
*nxtcyl = outcyl;
*nxthead = outhead;
return 0;
} /* end function empty_initialize */
/*-------------------------------------------------------------------*/
/* Subroutine to read a statement from the control file */
/* Input: */
/* cfp Control file pointer */
/* cfname Control file name */
/* stmt Buffer to receive control statement */
/* sbuflen Length of statement buffer */
/* Output: */
/* pstmtno Statement number */
/* The return value is 0 if a statement was successfully read, */
/* +1 if end of file, or -1 if error */
/*-------------------------------------------------------------------*/
static int
read_ctrl_stmt (FILE *cfp, char *cfname, char *stmt, int sbuflen,
int *pstmtno)
{
int stmtlen; /* Length of input statement */
static int stmtno = 0; /* Statement number */
while (1)
{
/* Read next record from control file */
stmtno++;
*pstmtno = stmtno;
if (fgets (stmt, sbuflen, cfp) == NULL)
{
/* Return code +1 if end of control file */
if (feof(cfp)) return +1;
{
char msgbuf[64];
MSGBUF( msgbuf, "line[%d] fgets()", stmtno );
/* Return code -1 if control file input error */
XMERRF ( MSG( HHC02468, "E", cfname, msgbuf, strerror( errno ) ) );
}
return -1;
}
#ifdef EXTERNALGUI
/* Indicate input file progess */
if (extgui) fprintf (stderr, "IPOS=%" I64_FMT "d\n", (U64)ftell(cfp));
#endif /*EXTERNALGUI*/
/* Check for DOS end of file character */
if (stmt[0] == '\x1A')
return +1;
/* Check that end of statement has been read */
#if defined( _MSVC_ )
stmtlen = (int)strlen(stmt);
#else
stmtlen = strlen(stmt);
#endif
if (stmtlen == 0 || stmt[stmtlen-1] != '\n')
{
XMERRF ( MSG( HHC02577, "E", cfname, stmtno ) );
return -1;
}
/* Remove trailing carriage return and line feed */
stmtlen--;
if (stmtlen > 0 && stmt[stmtlen-1] == '\r') stmtlen--;
/* Remove trailing spaces and tab characters */
while (stmtlen > 0 && (stmt[stmtlen-1] == SPACE
|| stmt[stmtlen-1] == '\t')) stmtlen--;
stmt[stmtlen] = '\0';
/* Print the input statement */
XMINFF (0, MSG( HHC02567, "I", cfname, stmtno, stmt ) );
/* Ignore comment statements */
if (stmtlen == 0 || stmt[0] == '#' || stmt[0] == '*')
continue;
break;
} /* end while */
return 0;
} /* end function read_ctrl_stmt */
/*-------------------------------------------------------------------*/
/* Subroutine to parse a dataset statement from the control file */
/* Input: */
/* stmt Control statement */
/* Output: */
/* dsname ASCIIZ dataset name (1-44 bytes + terminator) */
/* method Processing method (see METHOD_xxx defines) */
/* */
/* The following field is returned only for the XMIT method: */
/* ifptr Pointer to XMIT initialization file name */
/* */
/* The following fields are returned for non-XMIT methods: */
/* units Allocation units (C=CYL, T=TRK) */
/* sppri Primary allocation quantity */
/* spsec Secondary allocation quantity */
/* spdir Directory allocation quantity */
/* dsorg 1st byte of dataset organization bits */
/* recfm 1st byte of record format bits */
/* lrecl Logical record length */
/* blksz Block size */
/* keyln Key length */
/* The return value is 0 if successful, or -1 if error. */
/* Control statement format: */
/* dsname method [initfile] [space [dcbattrib]] */
/* The method can be: */
/* XMIT = load PDS from initfile containing an IEBCOPY unload */
/* dataset created using the TSO TRANSMIT command */
/* EMPTY = create empty dataset (do not specify initfile) */
/* DIP = initialize LOGREC dataset with IFCDIP00 header record */
/* CVOL = initialize SYSCTLG dataset as an OS CVOL */
/* VTOC = reserve space for the VTOC (dsname is ignored) */
/* The space allocation can be: */
/* CYL [pri [sec [dir]]] */
/* TRK [pri [sec [dir]]] */
/* If primary quantity is omitted then the dataset will be */
/* allocated the minimum number of tracks or cylinders needed */
/* to contain the data loaded from the initfile. */
/* Default allocation is in tracks. */
/* The dcb attributes can be: */
/* dsorg recfm lrecl blksize keylen */
/* For the XMIT method the dcb attributes are taken from the */
/* initialization file and need not be specified. */
/* Examples: */
/* SYS1.PARMLIB XMIT /cdrom/os360/reslibs/parmlib.xmi */
/* SYS1.NUCLEUS XMIT /cdrom/os360/reslibs/nucleus.xmi CYL */
/* SYS1.SYSJOBQE EMPTY CYL 10 0 0 DA F 176 176 0 */
/* SYS1.DUMP EMPTY CYL 10 2 0 PS FB 4104 4104 0 */
/* SYS1.OBJPDS EMPTY CYL 10 2 50 PO FB 80 3120 0 */
/* SYSVTOC VTOC CYL 1 */
/* SYSCTLG CVOL TRK 10 */
/* SYS1.LOGREC DIP CYL 1 */
/*-------------------------------------------------------------------*/
static int
parse_ctrl_stmt (char *stmt, char *dsname, BYTE *method, char **ifptr,
BYTE *units, int *sppri, int *spsec, int *spdir,
BYTE *dsorg, BYTE *recfm,
int *lrecl, int *blksz, int *keyln)
{
char *pdsnam; /* -> dsname in input stmt */
char *punits; /* -> allocation units */
char *psppri; /* -> primary space quantity */
char *pspsec; /* -> secondary space qty. */
char *pspdir; /* -> directory space qty. */
char *pdsorg; /* -> dataset organization */
char *precfm; /* -> record format */
char *plrecl; /* -> logical record length */
char *pblksz; /* -> block size */
char *pkeyln; /* -> key length */
char *pimeth; /* -> initialization method */
char *pifile; /* -> initialization filename*/
BYTE c; /* Character work area */
char *strtok_str = NULL; /* last token */
/* Parse the input statement */
pdsnam = strtok_r (stmt, " \t", &strtok_str);
pimeth = strtok_r (NULL, " \t", &strtok_str);
/* Check that all mandatory fields are present */
if (pdsnam == NULL || pimeth == NULL)
{
if (pdsnam == NULL)
XMERR ( MSG(HHC02578, "E", "DSNAME" ) );
if (pimeth == NULL)
XMERR ( MSG(HHC02578, "E", "Initialization method" ) );
return -1;
}
/* Return the dataset name in EBCDIC and ASCII */
string_to_upper (pdsnam);
memset (dsname, 0, 45);
strncpy (dsname, pdsnam, 44);
/* Set default dataset attribute values */
*units = 'T';
*sppri = 1;
*spsec = 0;
*spdir = 0;
*dsorg = 0x00;
*recfm = 0x00;
*lrecl = 0;
*blksz = 0;
*keyln = 0;
*ifptr = NULL;
/* Test for valid initialization method */
if (strcasecmp(pimeth, "XMIT") == 0)
*method = METHOD_XMIT;
else if (strcasecmp(pimeth, "VS") == 0)
*method = METHOD_VS;
else if (strcasecmp(pimeth, "EMPTY") == 0)
*method = METHOD_EMPTY;
else if (strcasecmp(pimeth, "DIP") == 0)
*method = METHOD_DIP;
else if (strcasecmp(pimeth, "CVOL") == 0)
*method = METHOD_CVOL;
else if (strcasecmp(pimeth, "VTOC") == 0)
*method = METHOD_VTOC;
else if (strcasecmp(pimeth, "SEQ") == 0)
*method = METHOD_SEQ;
else
{
XMERRF ( MSG( HHC02579, "E", pimeth ) );
return -1;
}
/* Locate the initialization file name */
if (*method == METHOD_XMIT || *method == METHOD_VS || *method == METHOD_SEQ)
{
pifile = strtok_r (NULL, " \t", &strtok_str);
if (pifile == NULL)
{
XMERR ( MSG( HHC02580, "E" ) );
return -1;
}
*ifptr = pifile;
}
/* Determine the space allocation units */
punits = strtok_r (NULL, " \t", &strtok_str );
if (punits == NULL) return 0;
string_to_upper (punits);
if (strcmp(punits, "CYL") == 0)
*units = 'C';
else if (strcmp(punits, "TRK") == 0)
*units = 'T';
else
{
XMERRF ( MSG( HHC02581, "E", punits ) );
return -1;
}
/* Determine the primary space allocation quantity */
psppri = strtok_r (NULL, " \t", &strtok_str);
if (psppri == NULL) return 0;
if (sscanf(psppri, "%u%c", sppri, &c) != 1)
{
XMERRF ( MSG( HHC02582, "E", "Primary", psppri, punits ) );
return -1;
}
/* Determine the secondary space allocation quantity */
pspsec = strtok_r (NULL, " \t", &strtok_str);
if (pspsec == NULL) return 0;
if (sscanf(pspsec, "%u%c", spsec, &c) != 1)
{
XMERRF ( MSG( HHC02582, "E", "Secondary", pspsec, punits ) );
return -1;
}
/* Determine the directory space allocation quantity */
pspdir = strtok_r (NULL, " \t", &strtok_str);
if (pspdir == NULL) return 0;
if (sscanf(pspdir, "%u%c", spdir, &c) != 1)
{
XMERRF ( MSG( HHC02583, "E", pspdir ) );
return -1;
}
/* Determine the dataset organization */
pdsorg = strtok_r (NULL, " \t", &strtok_str);
if (pdsorg == NULL) return 0;
string_to_upper (pdsorg);
if (strcmp(pdsorg, "IS") == 0)
*dsorg = DSORG_IS;
else if (strcmp(pdsorg, "PS") == 0)
*dsorg = DSORG_PS;
else if (strcmp(pdsorg, "DA") == 0)
*dsorg = DSORG_DA;
else if (strcmp(pdsorg, "PO") == 0)
*dsorg = DSORG_PO;
else
{
XMERRF ( MSG( HHC02584, "E", pdsorg ) );
return -1;
}
/* Determine the record format */
precfm = strtok_r (NULL, " \t", &strtok_str);
if (precfm == NULL) return 0;
string_to_upper (precfm);
if (strcmp(precfm, "F") == 0)
*recfm = RECFM_FORMAT_F;
else if (strcmp(precfm, "FB") == 0)
*recfm = RECFM_FORMAT_F | RECFM_BLOCKED;
else if (strcmp(precfm, "FBS") == 0)
*recfm = RECFM_FORMAT_F | RECFM_BLOCKED | RECFM_SPANNED;
else if (strcmp(precfm, "V") == 0)
*recfm = RECFM_FORMAT_V;
else if (strcmp(precfm, "VB") == 0)
*recfm = RECFM_FORMAT_V | RECFM_BLOCKED;
else if (strcmp(precfm, "VBS") == 0)
*recfm = RECFM_FORMAT_V | RECFM_BLOCKED | RECFM_SPANNED;
else if (strcmp(precfm, "U") == 0)
*recfm = RECFM_FORMAT_U;
else
{
XMERRF ( MSG( HHC02585, "E", precfm ) );
return -1;
}
/* Determine the logical record length */
plrecl = strtok_r (NULL, " \t", &strtok_str);
if (plrecl == NULL) return 0;
if (sscanf(plrecl, "%u%c", lrecl, &c) != 1
|| *lrecl > MAX_DATALEN)
{
XMERRF ( MSG( HHC02586, "E", plrecl ) );
return -1;
}
/* Determine the block size */
pblksz = strtok_r (NULL, " \t", &strtok_str);
if (pblksz == NULL) return 0;
if (sscanf(pblksz, "%u%c", blksz, &c) != 1
|| *blksz > MAX_DATALEN)
{
XMERRF ( MSG( HHC02587, "E", pblksz ) );
return -1;
}
/* Determine the key length */
pkeyln = strtok_r (NULL, " \t", &strtok_str);
if (pkeyln == NULL) return 0;
if (sscanf(pkeyln, "%u%c", keyln, &c) != 1
|| *keyln > 255)
{
XMERRF ( MSG( HHC02588, "E", pkeyln ) );
return -1;
}
return 0;
} /* end function parse_ctrl_stmt */
/*-------------------------------------------------------------------*/
/* Subroutine to process the control file */
/* Input: */
/* cfp Control file pointer */
/* cfname Control file name */
/* ofname DASD image file name */
/* cif -> CKD image file descriptor */
/* volser Output volume serial number (ASCIIZ) */
/* devtype Output device type */
/* reqcyls Requested device size in cylinders, or zero */
/* heads Output device number of tracks per cylinder */
/* trklen Output device virtual track length */
/* outcyl Output starting cylinder number */
/* outhead Output starting head number */
/* Output: */
/* Datasets are written to the DASD image file as indicated */
/* by the control statements. */
/*-------------------------------------------------------------------*/
static int
process_control_file (FILE *cfp, char *cfname, char *ofname,
CIFBLK *cif, char *volser, U16 devtype, int reqcyls,
int heads, int trklen, int outcyl, int outhead)
{
int rc; /* Return code */
int i; /* Array subscript */
int n; /* Integer work area */
char dsname[45]; /* Dataset name (ASCIIZ) */
BYTE method; /* Initialization method */
char *ifname; /* ->Initialization file name*/
BYTE units; /* C=CYL, T=TRK */
int sppri; /* Primary space quantity */
int spsec; /* Secondary space quantity */
int spdir; /* Directory space quantity */
BYTE dsorg; /* Dataset organization */
BYTE recfm; /* Record format */
int lrecl; /* Logical record length */
int blksz; /* Block size */
int keyln; /* Key length */
char stmt[256]; /* Control file statement */
int stmtno; /* Statement number */
int mintrks; /* Minimum size of dataset */
int maxtrks; /* Maximum size of dataset */
int outusedv; /* Bytes used in track buffer*/
int tracks = 0; /* Tracks used in dataset */
int numdscb = 0; /* Number of DSCBs */
DATABLK **dscbtab; /* -> Array of DSCB pointers */
int dirblu; /* Bytes used in last dirblk */
int lasttrk; /* Relative track number of
last used track of dataset*/
int lastrec; /* Record number of last used
block of dataset */
int trkbal; /* Bytes unused on last track*/
int bcyl; /* Dataset begin cylinder */
int bhead; /* Dataset begin head */
int ecyl; /* Dataset end cylinder */
int ehead; /* Dataset end head */
int vtoctrk = 0; /* VTOC start relative track */
int vtocext = 0; /* VTOC extent size (tracks) */
BYTE volvtoc[5]; /* VTOC begin CCHHR */
int offset = 0; /* Offset into trkbuf */
int fsflag = 0; /* 1=Free space message sent */
/* Obtain storage for the array of DSCB pointers */
dscbtab = (DATABLK**)malloc (sizeof(DATABLK*) * MAXDSCB);
if (dscbtab == NULL)
{
XMERRF ( MSG( HHC02544, "E", "DSCB pointer array", "malloc()", strerror(errno) ) );
return -1;
}
/* Initialize the DSCB array with format 4 and format 5 DSCBs */
rc = build_format4_dscb (dscbtab, numdscb, cif);
if (rc < 0) return -1;
numdscb++;
rc = build_format5_dscb (dscbtab, numdscb);
if (rc < 0) return -1;
numdscb++;
/* Read dataset statements from control file */
while (1)
{
/* Read next statement from control file */
rc = read_ctrl_stmt (cfp, cfname, stmt, sizeof(stmt), &stmtno);
if (rc < 0) return -1;
/* Exit if end of file */
if (rc > 0)
break;
/* Parse dataset statement from control file */
rc = parse_ctrl_stmt (stmt, dsname, &method, &ifname,
&units, &sppri, &spsec, &spdir,
&dsorg, &recfm, &lrecl, &blksz, &keyln);
/* Exit if error in control file */
if (rc < 0)
{
XMERRF ( MSG( HHC02569, "E", cfname, stmtno ) );
return -1;
}
/* Write empty tracks if allocation is in cylinders */
while (units == 'C' && outhead != 0)
{
/* Initialize track buffer with empty track */
init_track (trklen, cif->trkbuf, outcyl, outhead, &outusedv);
/* Write track to output file */
rc = write_track (cif, ofname, heads, trklen,
&outusedv, &tracks, &outcyl, &outhead);
if (rc < 0) break;
} /* end while */
XMINFF (1, MSG( HHC02568, "I", dsname, outcyl, outcyl, outhead, outhead ) );
bcyl = outcyl;
bhead = outhead;
/* Calculate minimum size of dataset in tracks */
mintrks = (units == 'C' ? sppri * heads : sppri);
/* Create dataset according to method specified */
switch (method) {
case METHOD_XMIT: /* IEBCOPY wrapped in XMIT */
case METHOD_VS: /* "straight" IEBCOPY */
/* Create dataset using IEBCOPY file as input */
maxtrks = MAX_TRACKS;
rc = process_iebcopy_file (ifname, ofname, cif,
devtype, heads, trklen,
outcyl, outhead, maxtrks,
method,
&dsorg, &recfm,
&lrecl, &blksz, &keyln,
&dirblu, &lastrec, &trkbal,
&tracks, &outcyl, &outhead);
if (rc < 0) return -1;
break;
case METHOD_DIP:
/* Initialize LOGREC dataset */
rc = dip_initialize (ofname, cif,
devtype, heads, trklen,
outcyl, outhead, mintrks,
&lastrec, &trkbal,
&tracks, &outcyl, &outhead);
if (rc < 0) return -1;
break;
case METHOD_CVOL:
/* Initialize SYSCTLG dataset */
rc = cvol_initialize (ofname, cif, volser,
devtype, heads, trklen,
outcyl, outhead, mintrks,
&lastrec, &trkbal,
&tracks, &outcyl, &outhead);
if (rc < 0) return -1;
break;
case METHOD_VTOC:
/* Reserve space for VTOC */
vtoctrk = (outcyl * heads) + outhead;
vtocext = mintrks;
tracks = 0;
lastrec = 0;
trkbal = 0;
break;
case METHOD_SEQ:
/* Create sequential dataset */
rc = seq_initialize (ifname, ofname, cif,
devtype, heads, trklen,
outcyl, outhead, mintrks,
dsorg, recfm, lrecl, blksz,
keyln, &lastrec, &trkbal,
&tracks, &outcyl, &outhead);
if (rc < 0) return -1;
break;
default:
case METHOD_EMPTY:
/* Create empty dataset */
rc = empty_initialize (ofname, cif,
devtype, heads, trklen,
outcyl, outhead, mintrks,
dsorg, spdir,
&dirblu, &lastrec, &trkbal,
&tracks, &outcyl, &outhead);
if (rc < 0) return -1;
break;
} /* end switch(method) */
/* Calculate the relative track number of last used track */
lasttrk = tracks - 1;
/* Round up space allocation if allocated in cylinders */
if (units == 'C')
{
n = (tracks + heads - 1) / heads * heads;
if (mintrks < n) mintrks = n;
}
/* Fill unused space in dataset with empty tracks */
while (tracks < mintrks)
{
/* Initialize track buffer with empty track */
init_track (trklen, cif->trkbuf, outcyl, outhead, &outusedv);
/* Write track to output file */
rc = write_track (cif, ofname, heads, trklen,
&outusedv, &tracks, &outcyl, &outhead);
if (rc < 0) return -1;
} /* end while(tracks) */
/* Print number of tracks written to dataset */
XMINFF (2, MSG( HHC02589, "I", dsname, tracks, (tracks == 1 ? "" : "s") ) );
/* Calculate end of extent cylinder and head */
ecyl = (outhead > 0 ? outcyl : outcyl - 1);
ehead = (outhead > 0 ? outhead - 1 : heads - 1);
/* Create format 1 DSCB for the dataset */
if (method != METHOD_VTOC)
{
rc = build_format1_dscb (dscbtab, numdscb, dsname, volser,
dsorg, recfm, lrecl, blksz,
keyln, dirblu, lasttrk, lastrec,
trkbal, units, spsec,
bcyl, bhead, ecyl, ehead);
if (rc < 0) return -1;
numdscb++;
}
} /* end while */
/* Write the VTOC */
rc = write_vtoc (dscbtab, numdscb, cif, ofname, devtype,
reqcyls, heads, trklen, vtoctrk, vtocext,
&outcyl, &outhead, volvtoc);
if (rc < 0) return -1;
/* Write empty tracks up to end of volume */
while (outhead != 0 || outcyl < reqcyls)
{
/* Issue free space information message */
if (fsflag == 0)
{
XMINFF (1, MSG( HHC02568, "I", "****FREE SPACE****", outcyl, outcyl, outhead, outhead ) );
fsflag = 1;
}
#ifdef EXTERNALGUI
/* Indicate output file progess */
if (extgui)
if ((outcyl % 10) == 0)
fprintf (stderr, "OUTCYL=%d\n", outcyl);
#endif /*EXTERNALGUI*/
/* Initialize track buffer with empty track */
init_track (trklen, cif->trkbuf, outcyl, outhead, &outusedv);
/* Write track to output file */
rc = write_track (cif, ofname, heads, trklen,
&outusedv, &tracks, &outcyl, &outhead);
if (rc < 0) return -1;
} /* end while */
if (outcyl > reqcyls && reqcyls != 0)
{
XMINFF (0, MSG( HHC02590, "W", reqcyls ) );
}
XMINFF (0, MSG( HHC02591, "I", ofname, outcyl ) );
/* Update the VTOC pointer in the volume label */
offset = CKDDASD_TRKHDR_SIZE + CKDDASD_RECHDR_SIZE + 8
+ CKDDASD_RECHDR_SIZE + IPL1_KEYLEN + IPL1_DATALEN
+ CKDDASD_RECHDR_SIZE + IPL2_KEYLEN + IPL2_DATALEN
+ CKDDASD_RECHDR_SIZE + VOL1_KEYLEN + 11;
XMINFF (5, MSG( HHC02592, "I", volvtoc[0], volvtoc[1], volvtoc[2], volvtoc[3], volvtoc[4] ) );
rc = read_track (cif, 0, 0);
if (rc < 0)
{
XMERR ( MSG( HHC02593, "E" ) );
return -1;
}
memcpy (cif->trkbuf + offset, volvtoc, sizeof(volvtoc));
cif->trkmodif = 1;
/* Release the DSCB buffers */
for (i = 0; i < numdscb; i++)
free (dscbtab[i]);
/* Release the array of DSCB pointers */
free (dscbtab);
return 0;
} /* end function process_control_file */
/*-------------------------------------------------------------------*/
/* DASDLOAD 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 */
int rc = 0; /* Return code */
char *cfname; /* -> Control file name */
char *ofname; /* -> Output file name */
FILE *cfp; /* Control file pointer */
CIFBLK *cif; /* -> CKD image block */
CKDDEV *ckd; /* -> CKD table entry */
char *volser; /* -> Volume serial (ASCIIZ) */
char *sdevtp; /* -> Device type (ASCIIZ) */
char *sdevsz; /* -> Device size (ASCIIZ) */
char *iplfnm; /* -> IPL text file or NULL */
BYTE c; /* Character work area */
U16 devtype; /* Output device type */
int devcyls; /* Default device size (cyls)*/
int reqcyls; /* Requested device size (cyls)
or 0 = use minimum size */
int outheads; /* Output device trks/cyl */
int outmaxdl; /* Output device maximum size
record data length value */
int outtrklv; /* Output device track length
of virtual device */
int reltrk; /* Output track number */
int outcyl; /* Output cylinder number */
int outhead; /* Output head number */
char stmt[256]; /* Control file statement */
int stmtno; /* Statement number */
BYTE comp = 0xff; /* Compression algoritm */
int altcylflag = 0; /* Alternate cylinders flag */
int flagECmode = 1; /* IPL PSW mode flag */
int flagMachinecheck = 0; /* IPL PSW machine check flag*/
int lfs = 0; /* 1 = Large file */
char pathname[MAX_PATH]; /* cfname in host path format*/
char *strtok_str = NULL; /* last token position */
/* 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( pgmname );
/* Display the program identification message */
MSGBUF( msgbuf, MSG_C( HHC02499, "I", pgm, "Build DASD from TSO XMIT files" ) );
display_version (stderr, msgbuf+10, FALSE);
/* Process optional arguments */
for ( ; argc > 1 && argv[1][0] == '-'; argv++, argc--)
{
if (strcmp("0", &argv[1][1]) == 0)
comp = CCKD_COMPRESS_NONE;
#ifdef CCKD_COMPRESS_ZLIB
else if (strcmp("z", &argv[1][1]) == 0)
comp = CCKD_COMPRESS_ZLIB;
#endif
#ifdef CCKD_COMPRESS_BZIP2
else if (strcmp("bz2", &argv[1][1]) == 0)
comp = CCKD_COMPRESS_BZIP2;
#endif
else if (strcmp("a", &argv[1][1]) == 0)
altcylflag = 1;
else if (strcmp("lfs", &argv[1][1]) == 0 && sizeof(off_t) > 4)
lfs = 1;
else if (strcmp("b", &argv[1][1]) == 0)
flagECmode = 0;
else if (strcmp("m", &argv[1][1]) == 0)
flagMachinecheck = 1;
else argexit(0, pgm);
}
/* Check the number of arguments */
if (argc < 3 || argc > 4)
argexit(4, pgm);
/* The first argument is the control file name */
cfname = argv[1];
if (argv[1] == NULL || strlen(argv[1]) == 0)
argexit(1, pgm);
/* The second argument is the DASD image file name */
ofname = argv[2];
if (argv[2] == NULL || strlen(argv[2]) == 0)
argexit(2, pgm);
/* The optional third argument is the message level */
if (argc > 3 && argv[3] != NULL)
{
if (sscanf(argv[3], "%u%c", &infolvl, &c) != 1
|| infolvl > 5)
argexit(3, pgm);
}
/* Open the control file */
hostpath(pathname, cfname, sizeof(pathname));
cfp = fopen (pathname, "r");
if (cfp == NULL)
{
XMERRF ( MSG( HHC02468, "E", cfname, "fopen()", strerror( errno ) ) );
return -1;
}
/* Read first statement from control file */
rc = read_ctrl_stmt (cfp, cfname, stmt, sizeof(stmt), &stmtno);
if (rc < 0) return -1;
/* Error if end of file */
if (rc > 0)
{
XMERRF ( MSG( HHC02500, "E", cfname ) );
return -1;
}
/* Parse the volume serial statement */
volser = strtok_r (stmt, " \t", &strtok_str);
sdevtp = strtok_r (NULL, " \t", &strtok_str);
sdevsz = strtok_r (NULL, " \t", &strtok_str);
iplfnm = strtok_r (NULL, " \t", &strtok_str);
/* Validate the volume serial number */
if (volser == NULL || strlen(volser) == 0 || strlen(volser) > 6)
{
XMERRF ( MSG( HHC02501, "E", volser, cfname, stmtno ) );
return -1;
}
string_to_upper (volser);
/* Validate the device type */
ckd = dasd_lookup (DASD_CKDDEV, sdevtp, 0, 0);
if (ckd == NULL)
{
XMERRF ( MSG( HHC02502, "E", sdevtp, cfname, stmtno ) );
return -1;
}
devtype = ckd->devt;
/* Obtain number of heads per cylinder, maximum data length per
track, and default number of cylinders per device */
outheads = ckd->heads;
devcyls = ckd->cyls;
if (altcylflag) devcyls += ckd->altcyls;
outmaxdl = ckd->r1;
/* Use default device size if requested size is omitted or
is zero or is "*" */
reqcyls = 0;
if (sdevsz != NULL && strcmp(sdevsz, "*") != 0)
{
/* Validate the requested device size in cylinders */
if (sscanf(sdevsz, "%u%c", &reqcyls, &c) != 1)
{
XMERRF ( MSG( HHC02503, "E", sdevsz, cfname, stmtno ) );
return -1;
}
}
if (reqcyls == 0)
reqcyls = devcyls;
/* Calculate the track size of the virtual device */
outtrklv = sizeof(CKDDASD_TRKHDR)
+ sizeof(CKDDASD_RECHDR) + R0_DATALEN
+ sizeof(CKDDASD_RECHDR) + outmaxdl
+ sizeof(eighthexFF);
outtrklv = ROUND_UP(outtrklv,512);
/* Display progress message */
XMINFF (0, MSG( HHC02520, "I", devtype, volser, outheads, outtrklv ) );
/* Create the output file */
#ifdef EXTERNALGUI
if (extgui) fprintf (stderr, "REQCYLS=%d\n", reqcyls);
#endif /*EXTERNALGUI*/
rc = create_ckd (ofname, devtype, outheads, outmaxdl, reqcyls,
volser, comp, lfs, 0, 0, 0, 1, 0 );
if (rc < 0)
{
XMERRF ( MSG( HHC02504, "E", ofname, "create_ckd()" ) );
return -1;
}
/* Open the output file */
cif = open_ckd_image (ofname, NULL, O_RDWR | O_BINARY, IMAGE_OPEN_NORMAL);
if (!cif)
{
XMERRF ( MSG( HHC02504, "E", ofname, "open_ckd_image()" ) );
return -1;
}
/* Display progress message */
XMINFF (0, MSG( HHC02521, "I", devtype, volser ) );
/* Write track zero to the DASD image file */
rc = write_track_zero (cif, ofname, volser, devtype,
outheads, outtrklv, iplfnm,
&reltrk, &outcyl, &outhead,
flagECmode, flagMachinecheck);
if (rc < 0)
return -1;
/* Process the control file to create the datasets */
rc = process_control_file (cfp, cfname, ofname, cif, volser,
devtype, reqcyls, outheads, outtrklv,
outcyl, outhead);
/* Close files and release buffers */
fclose (cfp);
close_ckd_image (cif);
return rc;
} /* end function main */