mirror of
https://github.com/SDL-Hercules-390/hyperion.git
synced 2026-04-11 06:35:02 +02:00
4505 lines
177 KiB
C
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(©r1->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 (¬elist, 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, ¬elist, 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, ©filn,
|
|
&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 */
|