/* Code borrowed from dasdpdsu Copyright 1999-2006 Roger Bowler */ /* Changes and additions Copyright 2001-2006, James M. Morrison */ /*-------------------------------------------------------------------*/ /* */ /* dasdseq */ /* */ /* This program retrieves a sequential (DSORG=PS) dataset from */ /* a Hercules CKD/CCKD volume. The input file is assumed to be */ /* encoded in the EBCDIC character set. */ /* */ /*-------------------------------------------------------------------*/ // We don't use some of the regular Hercules dasd routines because // we want the DSCB as read from dasd so we can check some of the // file attributes (such as DSORG, RECFM, LRECL). // Dasdseq now uses the same case for the output dataset as the // user specifies on the command line. Prior versions always // used upper case, which seems unnecessarily loud. #include "hstdinc.h" #include "hercules.h" typedef struct _DASD_VOL_LABEL { /* dasd cyl 0 trk 0 record 3 */ /* identifies volser, owner, and VTOC location */ /* recorded in EBCDIC; 80 bytes in length */ BYTE vollabi[3]; // c'VOL' BYTE volno; // volume label sequence # BYTE volserno[6]; // volume serial BYTE security; // security field, set to 0xc0 BYTE volvtoc[5]; // CCHHR of VTOC's F4DSCB BYTE resv1[21]; // reserved; should be left blank BYTE volowner[14]; // volume owner BYTE resv2[29]; // reserved; should be left blank } DASD_VOL_LABEL; #include "dasdblks.h" #ifndef MAX_EXTENTS // see getF3dscb for notes #define MAX_EXTENTS 123 // maximum supported dataset extents #endif typedef struct _DADSM { DASD_VOL_LABEL volrec; // volume label record FORMAT4_DSCB f4buf; // F4 DSCB DSXTENT f4ext; // VTOC extent info FORMAT3_DSCB f3buf; // F3 DSCB FORMAT1_DSCB f1buf; // F1 DSCB int f1numx; // # valid dataset extents DSXTENT f1ext[MAX_EXTENTS]; // dsn extent info } DADSM; //---------------------------------------------------------------------------------- // Globals //---------------------------------------------------------------------------------- int local_verbose = 0; // verbose setting int copy_verbose = 0; // verbose setting for copyfile char *din; // dasd image filename char *sfn; // shadow file parm int absvalid = 0; // 1 = -abs specified, use CCHH not dsn char *argdsn; // MVS dataset name int expert = 0; // enable -abs help int tran_ascii = 0; // 1 = ascii output #ifdef DEBUG int debug = 1; // enable debug code #else int debug = 0; // disable debug code #endif void sayext(int max, DSXTENT *extent) { int i; fprintf(stderr, " EXTENT --begin-- ---end---\n"); fprintf(stderr, "TYPE NUMBER CCCC HHHH CCCC HHHH\n"); for (i = 0; i < max; i++) { int bcyl = (extent[i].xtbcyl[0] << 8) | extent[i].xtbcyl[1]; int btrk = (extent[i].xtbtrk[0] << 8) | extent[i].xtbtrk[1]; int ecyl = (extent[i].xtecyl[0] << 8) | extent[i].xtecyl[1]; int etrk = (extent[i].xtetrk[0] << 8) | extent[i].xtetrk[1]; fprintf(stderr, " %2.2X %2.2X %4.4X %4.4X %4.4X %4.4X\n", extent[i].xttype, extent[i].xtseqn, bcyl, btrk, ecyl, etrk); } } /* sayext */ //---------------------------------------------------------------------------------- // Display selected F1 DSCB information //---------------------------------------------------------------------------------- void showf1( FILE *fmsg, FORMAT1_DSCB *f1dscb, DSXTENT extent[], int verbose) { int i, dsorg, lrecl, blksize, volseq, x, y, num_extents; char volser[sizeof(f1dscb->ds1dssn) + 1]; char dsn[sizeof(f1dscb->ds1dsnam) + 1]; char txtcredt[9]; // creation date char txtexpdt[9] = "(n/a)"; // expiration date char txtscr[20]; char txtsyscd[14]; char txtdsorg[5] = ""; // dsorg text char txtrecfm[5] = ""; // recfm text if (verbose > 2) { fprintf(fmsg, "showf1 F1 DSCB\n"); data_dump(f1dscb, sizeof(FORMAT1_DSCB)); } make_asciiz(dsn, sizeof(dsn), f1dscb->ds1dsnam, sizeof(f1dscb->ds1dsnam)); make_asciiz(volser, sizeof(volser), f1dscb->ds1dssn, sizeof(f1dscb->ds1dssn)); volseq = (f1dscb->ds1volsq[0] << 8) | (f1dscb->ds1volsq[1]); x = f1dscb->ds1credt[0] + 1900; y = (f1dscb->ds1credt[1] << 8) | f1dscb->ds1credt[2]; sprintf(txtcredt, "%4.4d", x); strcat(txtcredt, "."); sprintf(txtscr, "%3.3d", y); strcat(txtcredt, txtscr); if (f1dscb->ds1expdt[0] || f1dscb->ds1expdt[1] || f1dscb->ds1expdt[2]) { x = f1dscb->ds1expdt[0] + 1900; y = (f1dscb->ds1expdt[1] << 8) | f1dscb->ds1expdt[2]; sprintf(txtexpdt, "%4.4d", x); strcat(txtexpdt, "."); sprintf(txtscr, ".%3.3d", y); strcat(txtexpdt, txtscr); } num_extents = f1dscb->ds1noepv; // Field ignored: ds1nobdb (# bytes used in last PDS dir blk) make_asciiz(txtsyscd, sizeof(txtsyscd), f1dscb->ds1syscd, sizeof(f1dscb->ds1syscd)); dsorg = (f1dscb->ds1dsorg[0] << 8) | (f1dscb->ds1dsorg[1]); if (dsorg & (DSORG_IS * 256)) strcpy(txtdsorg, "ISAM"); if (dsorg & (DSORG_PS * 256)) strcpy(txtdsorg, "PS"); if (dsorg & (DSORG_DA * 256)) strcpy(txtdsorg, "DA"); if (dsorg & (DSORG_PO * 256)) strcpy(txtdsorg, "PO"); if (dsorg & DSORG_AM) strcpy(txtdsorg, "VSAM"); if (txtdsorg[0] == '\0') strcpy(txtdsorg, "?"); if (dsorg & (DSORG_U * 256)) strcat(txtdsorg, "-Unmovable"); if (f1dscb->ds1recfm & RECFM_FORMAT_F) strcpy(txtrecfm, "F"); if (f1dscb->ds1recfm & RECFM_FORMAT_V) strcpy(txtrecfm, "V"); if ((f1dscb->ds1recfm & RECFM_FORMAT_U) == RECFM_FORMAT_U) strcpy(txtrecfm, "U"); if (f1dscb->ds1recfm & RECFM_BLOCKED) strcat(txtrecfm, "B"); if (f1dscb->ds1recfm & RECFM_SPANNED) strcat(txtrecfm, "S"); if (f1dscb->ds1recfm & RECFM_CTLCHAR_A) strcat(txtrecfm, "A"); if (f1dscb->ds1recfm & RECFM_CTLCHAR_M) strcat(txtrecfm, "M"); if (f1dscb->ds1recfm & RECFM_TRKOFLOW) strcat(txtrecfm, "-Track overflow"); // Field ignored: ds1optcd (option codes, same as in DCB) blksize = (f1dscb->ds1blkl[0] << 8) | f1dscb->ds1blkl[1]; lrecl = (f1dscb->ds1lrecl[0] << 8) | f1dscb->ds1lrecl[1]; // Field ignored: ds1keyl (key length) // Field ignored: ds1rkp (relative key position) // Field ignored: ds1dsind (data set indicators) // Field ignored: ds1scalo (secondary allocation) // Field ignored: ds1lstar (pointer to last written block; ttr) // Field ignored: ds1trbal (bytes remaining on last track used) // Extent information was passed to us, so we ignore what's in F1DSCB fprintf(fmsg, "Dataset %s on volume %s sequence %d\n", dsn, volser, volseq); fprintf(fmsg, "Created %s expires %s\n", txtcredt, txtexpdt); fprintf(fmsg, "Dsorg=%s recfm=%s lrecl=%d blksize=%d\n", txtdsorg, txtrecfm, lrecl, blksize); fprintf(fmsg, "System code %s\n", txtsyscd); if (verbose > 1) { fprintf(stderr, "Dataset has %d extent(s)\n", num_extents); if (verbose > 2) data_dump((void *)extent, sizeof(extent) * MAX_EXTENTS); fprintf(stderr, "Extent Information:\n"); fprintf(stderr, " EXTENT --begin-- ---end---\n"); fprintf(stderr, "TYPE NUMBER CCCC HHHH CCCC HHHH\n"); for (i = 0; i < num_extents; i++) { int bcyl = (extent[i].xtbcyl[0] << 8) | extent[i].xtbcyl[1]; int btrk = (extent[i].xtbtrk[0] << 8) | extent[i].xtbtrk[1]; int ecyl = (extent[i].xtecyl[0] << 8) | extent[i].xtecyl[1]; int etrk = (extent[i].xtetrk[0] << 8) | extent[i].xtetrk[1]; fprintf(stderr, " %2.2X %2.2X %4.4X %4.4X %4.4X %4.4X\n", extent[i].xttype, extent[i].xtseqn, bcyl, btrk, ecyl, etrk); } } return; } /* showf1 */ //---------------------------------------------------------------------------------- // Copy DSORG=PS RECFM=F[B] dataset to output file // // Input: // fout FILE * (opened "w" for ascii, "wb" for ebcdic) // cif dasdutil control block // f1dscb F1 DSCB // extent dataset extent array // tran 0 = ebcdic output, 1 = ascii output // ascii output will have trailing blanks removed // verbose 0 = no status messages // 1 = status messages // > 1 debugging messages // > 2 dump read record // > 3 dump written record // > 4 dump read record from input buffer // Output: // File written, messages displayed on stderr // Returns -1 on error, else returns # records written // Notes: // Caller is responsible for opening and closing fout. // // The F1 DSCB's DS1LSTAR field is used to determine EOF (absent a prior // EOF being encountered), since some datasets don't have an EOF marker // present. On my MVSRES, for instance, SYS1.BRODCAST has no EOF marker. // // 2003-01-14 jmm DS1LSTAR may be zero; if so, ignore DS1LSTAR and // hope for valid EOF. //---------------------------------------------------------------------------------- int fbcopy( FILE *fout, CIFBLK *cif, DADSM *dadsm, int tran, int verbose) { FORMAT1_DSCB *f1dscb = &dadsm->f1buf; DSXTENT extent[MAX_EXTENTS]; int rc, trk = 0, trkconv = 999, rec = 1; int cyl = 0, head = 0, rc_rb, len, offset; int rc_copy = 0; int recs_written = 0, lrecl, num_extents; int lstartrack = 0, lstarrec = 0, lstarvalid = 0; BYTE *buffer; char *pascii = NULL; char zdsn[sizeof(f1dscb->ds1dsnam) + 1]; // ascii dsn // Kludge to avoid rewriting this code (for now): memcpy(&extent, (void *)&(dadsm->f1ext), sizeof(extent)); num_extents = f1dscb->ds1noepv; lrecl = (f1dscb->ds1lrecl[0] << 8) | (f1dscb->ds1lrecl[1]); if (absvalid) { strcpy(zdsn, argdsn); if (debug) fprintf(stderr, "fbcopy absvalid\n"); } else { make_asciiz(zdsn, sizeof(zdsn), f1dscb->ds1dsnam, sizeof(f1dscb->ds1dsnam)); if ((f1dscb->ds1lstar[0] !=0) || (f1dscb->ds1lstar[1] != 0) || (f1dscb->ds1lstar[2] != 0)) { lstartrack = (f1dscb->ds1lstar[0] << 8) | (f1dscb->ds1lstar[1]); lstarrec = f1dscb->ds1lstar[2]; lstarvalid = 1; // DS1LSTAR valid } } if (debug) { fprintf(stderr, "fbcopy zdsn %s\n", zdsn); fprintf(stderr, "fbcopy num_extents %d\n", num_extents); fprintf(stderr, "fbcopy lrecl %d\n", lrecl); fprintf(stderr, "fbcopy F1 DSCB\n"); data_dump(f1dscb, sizeof(FORMAT1_DSCB)); sayext(num_extents, (void *)&extent); } if (verbose) // DS1LSTAR = last block written TTR fprintf(stderr, "fbcopy DS1LSTAR %2.2X%2.2X%2.2X lstartrack %d " "lstarrec %d lstarvalid %d\n", f1dscb->ds1lstar[0], f1dscb->ds1lstar[1], f1dscb->ds1lstar[2], lstartrack, lstarrec, lstarvalid); if (tran) { // need ASCII translation buffer? pascii = malloc(lrecl + 1); if (pascii == NULL) { fprintf(stderr, "fbcopy unable to allocate ascii buffer\n"); return -1; } } while (1) { // output records until something stops us // Honor DS1LSTAR when valid if ((lstarvalid) && (trk == lstartrack) && (rec > lstarrec)) { if (verbose) fprintf(stderr, "fbcopy DS1LSTAR indicates EOF\n" "fbcopy DS1LSTAR %2.2X%2.2X%2.2X " "track %d record %d\n", f1dscb->ds1lstar[0], f1dscb->ds1lstar[1], f1dscb->ds1lstar[2], trk, rec); rc_copy = recs_written; break; } // Convert TT to CCHH for upcoming read_block call if (trkconv != trk) { // avoid converting for each block trkconv = trk; // current track converted rc = convert_tt(trk, num_extents, extent, cif->heads, &cyl, &head); if (rc < 0) { fprintf(stderr, "fbcopy convert_tt track %5.5d, rc %d\n", trk, rc); if (absvalid) rc_copy = recs_written; else rc_copy = -1; break; } if (verbose > 1) fprintf(stderr, "fbcopy convert TT %5.5d CCHH %4.4X %4.4X\n", trk, cyl, head); } // Read block from dasd if (verbose > 2) fprintf(stderr, "fbcopy reading track %d " "record %d CCHHR = %4.4X %4.4X %2.2X\n", trk, rec, cyl, head, rec); rc_rb = read_block(cif, cyl, head, rec, NULL, NULL, &buffer, &len); if (rc_rb < 0) { // error fprintf(stderr, "fbcopy error reading %s, rc %d\n", zdsn, rc_rb); rc_copy = -1; break; } // Handle end of track return from read_block if (rc_rb > 0) { // end of track if (verbose > 2) fprintf(stderr, "fbcopy End Of Track %d rec %d\n", trk, rec); trk++; // next track rec = 1; // record 1 on new track continue; } // Check for dataset EOF if (len == 0) { // EOF if (verbose) fprintf(stderr, "fbcopy EOF track %5.5d rec %d\n", trk, rec); if (absvalid) { // capture as much -abs data as possible if (verbose) fprintf(stderr, "fbcopy ignoring -abs EOF\n"); } else { rc_copy = recs_written; break; } } if (verbose > 3) fprintf(stderr, "fbcopy read %d bytes\n", len); if (verbose > 2) { data_dump(buffer, len); fprintf(stderr, "\n"); } // Deblock input dasd block, write records to output dataset for (offset = 0; offset < len; offset += lrecl) { if (verbose > 3) { fprintf(stderr, "fbcopy offset %d length %d rec %d\n", offset, lrecl, recs_written); } if (tran) { // ASCII output memset(pascii, 0, lrecl + 1); make_asciiz(pascii, lrecl + 1, buffer + offset, lrecl); if (verbose > 4) { fprintf(stderr, "fbcopy buffer offset %d rec %d\n", offset, rec); data_dump(buffer + offset, lrecl); } if (verbose > 3) { fprintf(stderr, "->%s<-\n", pascii); data_dump(pascii, lrecl); } fprintf(fout, "%s\n", pascii); } else { // EBCDIC output if (verbose > 3) { fprintf(stderr, "fbcopy EBCDIC buffer\n"); data_dump(buffer + offset, lrecl); } fwrite(buffer + offset, lrecl, 1, fout); } if (ferror(fout)) { fprintf(stderr, "fbcopy error writing %s\n", zdsn); fprintf(stderr, "%s\n", strerror(errno)); rc_copy = -1; } recs_written++; } if (rc_copy != 0) break; else rec++; // next record on track } /* while (1) */ if (pascii) free(pascii); // release ASCII conversion buffer return rc_copy; } /* fbcopy */ //---------------------------------------------------------------------------------- // Given extent information, place it into appropriate extent table entry //---------------------------------------------------------------------------------- void makext( int i, // extent # int heads, // # heads per cylinder on device DSXTENT *extent, // extent table entry int startcyl, // start cylinder int starttrk, // start track int size) { // extent size in tracks int endcyl = ((startcyl * heads) + starttrk + size - 1) / heads; int endtrk = ((startcyl * heads) + starttrk + size - 1) % heads; if (i > (MAX_EXTENTS - 1)) { fprintf(stderr, "makext extent # parm invalid %d, abort\n", i); exit(4); } extent[i].xttype = 1; // extent type extent[i].xtseqn = i; // extent # (relative zero) extent[i].xtbcyl[0] = startcyl >> 8; // begin cyl extent[i].xtbcyl[1] = startcyl - ((startcyl / 256) * 256); extent[i].xtbtrk[0] = starttrk >> 8; extent[i].xtbtrk[1] = starttrk - ((starttrk / 256) * 256); extent[i].xtecyl[0] = endcyl >> 8; // end cyl extent[i].xtecyl[1] = endcyl - ((endcyl / 256) * 256); extent[i].xtetrk[0] = endtrk >> 8; extent[i].xtetrk[1] = endtrk - ((endtrk / 256) * 256); // end track return; } /* makext */ //---------------------------------------------------------------------------------- // showhelp - display syntax help //---------------------------------------------------------------------------------- void showhelp() { fprintf(stderr, (expert) ? "Usage: dasdseq [-debug] [-expert] [-ascii] image [sf=shadow] [attr] filespec\n" " -debug optional - Enables debug mode, additional debug help appears\n" : "Usage: dasdseq [-expert] [-ascii] image [sf=shadow] filespec\n"); fprintf(stderr, " -expert optional - Additional help describes expert operands\n" " -ascii optional - translate output file to ascii, trim trailing blanks\n" " image required - [path/]filename of dasd image file (dasd volume)\n" " shadow optional - [path/]filename of shadow file (note sf=)\n"); if (expert) fprintf(stderr, " ALL EXPERT FACILITIES ARE EXPERIMENTAL\n" " attr optional - dataset attributes (only useful with -abs)\n" " attr syntax: [-recfm fb] [-lrecl aa]\n" " -recfm designates RECFM, reserved for future support\n" " fb - fixed, blocked (only RECFM currently supported)\n" " -lrecl designates dataset LRECL\n" " aa - decimal logical record length (default 80)\n" " Blocksize need not be specified; dasdseq handles whatever\n" " block size comes off the volume.\n" " filespec required (optional sub-operands in the following order):\n" " [-heads xx]\n" " [-abs cc hh tt] [...] [-abs cc hh tt ]\n" " filename\n" " When -abs is -not- specified,\n" " Filename specifies the MVS DSORG=PS dataset on the volume.\n" " The dasd image volume containing the dataset must have a valid VTOC\n" " structure, and a F1 DSCB describing the dataset.\n" " Specifying -debug will (eventually) display extent information.\n" " When -abs is specified, each -abs group specifies one dataset extent.\n" " For multi-extent datasets, -abs groups may be repeated as needed,\n" " in the order in which the dataset's extents occur.\n" " A maximum of %d extents are supported.\n" " No VTOC structure is implied, a F1 DSCB will not be sought.\n" " Dasdseq will frequently report 'track not found in extent table'\n" " (along with a message from fbcopy about rc -1 from convert_tt)\n" " due to potentially missing EOF markers in the extent, and the\n" " fact that the F1 DSCB DS1LSTAR field is not valid.\n" " Check your output file before you panic.\n" " Fbcopy ignores EOF, in case you are attempting to recovery PDS\n" " member(s) from a damaged dasd volume, preferring to wait until\n" " all tracks in the extent have been processed.\n" " Tracks containing PDS members may have more than one EOF per track.\n" " Expect a lot of associated manual effort with -abs.\n" " -heads defines # tracks per cylinder on device;\n" " xx - decimal number of heads per cylinder on device\n" " default xx = 15 (valid for 3380s, 3390s)\n" " -abs indicates the beginning of each extent's location in terms of\n" " absolute dasd image location.\n" " cc - decimal cylinder number (relative zero)\n" " hh - decimal head number (relative zero)\n" " tt - decimal number of tracks in extent\n" " filename will be the filename of the output file in the current directory;\n" " output filename in the same case as the command line filename.\n", MAX_EXTENTS); else fprintf(stderr, " filespec required - MVS dataset name of DSORG=PS dataset, output filename\n"); if (debug) fprintf(stderr, "\n" "Debugging options (at end of dasdseq command)\n" " [verbose [x [y [z]]]]\n\n" " verbose debug output level (default = 0 when not specified)\n" " x main program (default = 1 when verbose specified)\n" " y copyfile + showf1\n" " z dasdutil\n" " Higher numbers produces more output\n"); return; } /* showhelp */ //---------------------------------------------------------------------------------- // parsecmd - parse command line; results stored in program globals //---------------------------------------------------------------------------------- int parsecmd(int argc, char **argv, DADSM *dadsm) { int util_verbose = 0; // Hercules dasdutil.c diagnostic level int heads = 15; // # heads per cylinder on device int extnum = 0; // extent number for makext() int abscyl = 0; // absolute CC (default 0) int abshead = 0; // absolute HH (default 0) int abstrk = 1; // absolute tracks (default 1) int lrecl = 80; // default F1 DSCB lrecl // Usage: dasdseq [-debug] [-expert] [-ascii] image [sf=shadow] [attr] filespec argv++; // skip dasdseq command argv[0] if ((*argv) && (strcasecmp(*argv, "-debug") == 0)) { argv++; debug = 1; fprintf(stderr, "Command line DEBUG specified\n"); } if ((*argv) && (strcasecmp(*argv, "-expert") == 0)) { argv++; expert = 1; if (debug) fprintf(stderr, "EXPERT mode\n"); } if ((*argv) && (strcasecmp(*argv, "-ascii") == 0)) { argv++; tran_ascii = 1; if (debug) fprintf(stderr, "ASCII translation enabled\n"); } if (*argv) din = *argv++; // dasd image filename if (debug) fprintf(stderr, "IMAGE %s\n", din); if (*argv && strlen(*argv) > 3 && !memcmp(*argv, "sf=", 3)) { sfn = *argv++; // shadow file parm } else sfn = NULL; if (debug) fprintf(stderr, "SHADOW %s\n", sfn); dadsm->f1buf.ds1recfm = RECFM_FORMAT_F | RECFM_BLOCKED; // recfm FB for fbcopy if ((*argv) && (strcasecmp(*argv, "-recfm") == 0)) { argv++; // skip -recfm if ((*argv) && (strcasecmp(*argv, "fb") == 0)) { argv++; // skip fb if (debug) fprintf(stderr, "RECFM fb\n"); } else { argv++; // skip bad recfm fprintf(stderr, "Unsupported -recfm value %s\n", *argv); } } if ((*argv) && (strcasecmp(*argv, "-lrecl") == 0)) { argv++; // skip -lrecl if (*argv) lrecl = atoi(*argv++); // lrecl value if (debug) fprintf(stderr, "LRECL %d\n", lrecl); } dadsm->f1buf.ds1lrecl[0] = lrecl >> 8; // for fbcopy dadsm->f1buf.ds1lrecl[1] = lrecl - ((lrecl >> 8) << 8); if ((*argv) && (strcasecmp(*argv, "-heads") == 0)) { argv++; // skip -heads if (*argv) heads = atoi(*argv++); // heads value } if (debug) fprintf(stderr, "HEADS %d\n", heads); if ((*argv) && (strcasecmp(*argv, "-abs") == 0)) { absvalid = 1; // CCHH valid while ((*argv) && (strcasecmp(*argv, "-abs") == 0)) { argv++; // skip -abs abscyl = 0; abshead = 0; abstrk = 1; // defaults if (*argv) abscyl = atoi(*argv++); // abs cc if (*argv) abshead = atoi(*argv++); // abs hh if (*argv) abstrk = atoi(*argv++); // abs tracks // Build extent entry for -abs group makext(extnum, heads, (DSXTENT *) &dadsm->f1ext, abscyl, abshead, abstrk); extnum++; dadsm->f1buf.ds1noepv = extnum; // for fbcopy if (debug) fprintf(stderr, "Absolute CC %d HH %d tracks %d\n", abscyl, abshead, abstrk); if (extnum > MAX_EXTENTS) { fprintf(stderr, "Too many extents, abort\n"); exit(3); } } // if (debug) sayext(MAX_EXTENTS, dadsm->f1ext);// show extent table } if (debug) { fprintf(stderr, "parsecmd completed F1 DSCB\n"); data_dump(&dadsm->f1buf, sizeof(FORMAT1_DSCB)); } if (*argv) argdsn = *argv++; // [MVS dataset name/]output filename if (debug) fprintf(stderr, "DSN %s\n", argdsn); if ((*argv) && ( // support deprecated 'ascii' operand (strcasecmp(*argv, "ascii") == 0) || (strcasecmp(*argv, "-ascii") == 0) ) ) { argv++; tran_ascii = 1; if (debug) fprintf(stderr, "ASCII translation enabled\n"); } set_verbose_util(0); // default util verbosity if ((*argv) && (strcasecmp(*argv, "verbose") == 0)) { local_verbose = 1; argv++; if (*argv) local_verbose = atoi(*argv++); if (*argv) copy_verbose = atoi(*argv++); if (*argv) { util_verbose = atoi(*argv++); set_verbose_util(util_verbose); if (debug) fprintf(stderr, "Utility verbose %d\n", util_verbose); } } // If the user specified expert mode without -abs, give help & exit // Additionally, if the user has "extra" parms, show help & exit // No "extraneous parms" message is issued, since some of the code // above forces *argv to be true when it wants help displayed if ((argc < 3) || (*argv) || ((expert) && (!absvalid))) { showhelp(); // show syntax before bailing exit(2); } return 0; } /* parsecmd */ //---------------------------------------------------------------------------------- // getlabel - retrieve label record from dasd volume image //---------------------------------------------------------------------------------- // // Input: // cif ptr to opened CIFBLK of dasd image // glbuf ptr to 80 byte buffer provided by caller // verbose 0 = no status messages // 1 = status messages // > 1 debugging messages // // Output: // glbuf dasd volume label record // // Returns: 0 OK, else error // // Notes: // The volume label record always resides at CCHHR 0000 0000 03. // The dasd volume label record contains the CCHHR of the VTOC. // The volume label record is copied to the caller's buffer. //---------------------------------------------------------------------------------- int getlabel( CIFBLK *cif, DASD_VOL_LABEL *glbuf, int verbose) { int len, rc; void *plabel; if (verbose) fprintf(stderr, "getlabel reading volume label\n"); rc = read_block(cif, 0, 0, 3, NULL, NULL, (void *) &plabel, &len); if (rc) { fprintf(stderr, "getlabel error reading volume label, rc %d\n", rc); return 1; } if (len != sizeof(DASD_VOL_LABEL)) { fprintf(stderr, "getlabel error: volume label %d, not 80 bytes long\n", len); return 2; } memcpy((void *)glbuf, plabel, sizeof(DASD_VOL_LABEL)); if (verbose > 1) { fprintf(stderr, "getlabel volume label\n"); data_dump(glbuf, len); } return 0; } /* getlabel */ //---------------------------------------------------------------------------------- // getF4dscb - retrieve Format 4 DSCB - VTOC self-descriptor record //---------------------------------------------------------------------------------- // // Input: // cif ptr to opened CIFBLK of dasd image containing dataset // f4dscb ptr to F4 DSCB buffer (key & data) // volrec ptr to buffer containing volume label rec // vtocx ptr to VTOC extent array (one extent only) // verbose 0 = no status messages // 1 = status messages // > 1 debugging messages // // Output: // f4buf F4 DSCB in buffer (44 byte key, 96 bytes of data) // vtocx VTOC extent array updated // // Returns: 0 OK, else error // // Notes: // There should only be one F4 DSCB in the VTOC, and it should always be // the first record in the VTOC. The F4 provides VTOC extent information, // anchors free space DSCBs, and provides information about the device on // which the VTOC resides. //---------------------------------------------------------------------------------- int getF4dscb( CIFBLK *cif, FORMAT4_DSCB *f4dscb, DASD_VOL_LABEL *volrec, DSXTENT *vtocx, int verbose) { char vtockey[sizeof(f4dscb->ds4keyid)]; void *f4key, *f4data; int f4kl, f4dl; int cyl, head, rec, rc; // Extract VTOC's CCHHR from volume label cyl = (volrec->volvtoc[0] << 8) | volrec->volvtoc[1]; head = (volrec->volvtoc[2] << 8) | volrec->volvtoc[3]; rec = volrec->volvtoc[4]; if (verbose > 1) fprintf(stderr, "getF4dscb VTOC F4 at cyl %d head %d rec %d\n", cyl, head, rec); // Read VTOC's Format 4 DSCB (VTOC self-descriptor) if (verbose) fprintf(stderr, "getF4dscb reading VTOC F4 DSCB\n"); rc = read_block(cif, cyl, head, rec, (void *) &f4key, &f4kl, (void *) &f4data, &f4dl); if (rc) { fprintf(stderr, "getF4dscb error reading F4 DSCB, rc %d\n", rc); return 1; } // Verify correct key and data length if ((f4kl != sizeof(f4dscb->ds4keyid)) || (f4dl != (sizeof(FORMAT4_DSCB) - sizeof(f4dscb->ds4keyid)))) { fprintf(stderr, "getF4dscb erroneous key length %d or data length %d\n", f4kl, f4dl); return 2; } // Return data to caller memcpy((void *) &f4dscb->ds4keyid, f4key, f4kl); // copy F4 key into buffer memcpy((void *) &f4dscb->ds4fmtid, f4data, f4dl); // copy F4 data into buffer memcpy((void *) vtocx, (void *)&f4dscb->ds4vtoce, sizeof(f4dscb->ds4vtoce)); // copy VTOC extent entry if (verbose > 1) { fprintf(stderr, "getF4dscb F4 DSCB\n"); data_dump((void *) f4dscb, sizeof(FORMAT4_DSCB)); } // Verify DS4FMTID byte = x'F4', DS4KEYID key = x'04', and DS4NOEXT = x'01' // Do this after copying data to caller's buffer so we can use struct fields // rather than having to calculate offset to verified data; little harm done // if it doesn't verify since we're toast if they're bad. memset(vtockey, 0x04, sizeof(vtockey)); if ((f4dscb->ds4fmtid != 0xf4) || (f4dscb->ds4noext != 0x01) || (memcmp(&f4dscb->ds4keyid, vtockey, sizeof(vtockey)))) { fprintf(stderr, "getF4dscb " "VTOC format id byte invalid (DS4IDFMT) %2.2X, \n" "VTOC key invalid, or multi-extent VTOC\n", f4dscb->ds4fmtid); return 3; } // Display VTOC extent info (always one extent, never more) if (verbose > 1) { fprintf (stderr, "getF4dscb " "VTOC start CCHH=%2.2X%2.2X %2.2X%2.2X " "end CCHH=%2.2X%2.2X %2.2X%2.2X\n", vtocx->xtbcyl[0], vtocx->xtbcyl[1], vtocx->xtbtrk[0], vtocx->xtbtrk[1], vtocx->xtecyl[0], vtocx->xtecyl[1], vtocx->xtetrk[0], vtocx->xtetrk[1]); } return 0; } /* getF4dscb */ //---------------------------------------------------------------------------------- // getF1dscb - retrieve Format 1 DSCB //---------------------------------------------------------------------------------- // // Input: // cif ptr to opened CIFBLK of dasd image containing dataset // zdsn ASCII null-terminated dataset name // f1dscb ptr to F1 DSCB buffer (key & data) // vtocext ptr to VTOC's extent info // verbose 0 = no status messages // 1 = status messages // > 1 debugging messages // // Output: // f1buf F1 DSCB (44 byte key, 96 byte data) // // Returns: 0 OK, else error // // Notes: The F1 DSCB describes the MVS dataset's physical and logical attributes // such as RECFM, LRECL, BLKSIZE, and where on the volume the dataset // resides (the extent information). The first 3 possible extents are // described in the F1 DSCB. If additional extents are allocated, they // are described by F3 DSCBs referred to by the F1 DSCB. //---------------------------------------------------------------------------------- int getF1dscb( CIFBLK *cif, char *pdsn[], FORMAT1_DSCB *f1dscb, DSXTENT *vtocext[], int verbose) { char zdsn[sizeof(f1dscb->ds1dsnam) + 1]; // zASCII dsn BYTE edsn[sizeof(f1dscb->ds1dsnam)]; // EBCDIC dsn void *f1key, *f1data; int f1kl, f1dl; int cyl, head, rec, rc; int vtocextents = 1; // VTOC has only one extent // Locate dataset's F1 DSCB memset(zdsn, 0, sizeof(zdsn)); strncpy(zdsn, *pdsn, sizeof(zdsn) - 1); string_to_upper(zdsn); convert_to_ebcdic(edsn, sizeof(edsn), zdsn); if (verbose) fprintf(stderr, "getF1dscb searching VTOC for %s\n", zdsn); rc = search_key_equal(cif, edsn, sizeof(edsn), vtocextents, (DSXTENT *)vtocext, &cyl, &head, &rec); if (rc) { fprintf(stderr, "getF1dscb search_key_equal rc %d\n", rc); if (verbose) { fprintf(stderr, "getF1dscb key\n"); data_dump(edsn, sizeof(edsn)); } if (rc == 1) fprintf(stderr, "getF1dscb no DSCB found for %s\n", zdsn); return 1; } // Read F1 DSCB describing dataset if (verbose) fprintf(stderr, "getF1dscb reading F1 DSCB\n"); rc = read_block(cif, cyl, head, rec, (void *)&f1key, &f1kl, (void *) &f1data, &f1dl); if (rc) { fprintf(stderr, "getF1dscb error reading F1 DSCB, rc %d\n", rc); return 2; } // Return data to caller if ((f1kl == sizeof(f1dscb->ds1dsnam)) && (f1dl == (sizeof(FORMAT1_DSCB) - sizeof(f1dscb->ds1dsnam)))) { memcpy((void *) &f1dscb->ds1dsnam, f1key, f1kl); // copy F1 key to buffer memcpy((void *) &f1dscb->ds1fmtid, f1data, f1dl); // copy F1 data to buffer } else { fprintf(stderr, "getF1dscb bad key %d or data length %d\n", f1kl, f1dl); return 3; } if (verbose > 1) { fprintf(stderr, "getF1dscb F1 DSCB\n"); data_dump((void *) f1dscb, sizeof(FORMAT1_DSCB)); } // Verify DS1FMTID byte = x'F1' // Do this after copying data to caller's buffer so we can use struct fields // rather than having to calculate offset to verified data; little harm done // if it doesn't verify since we're toast if it's bad. if (f1dscb->ds1fmtid != 0xf1) { fprintf(stderr, "getF1dscb " "F1 DSCB format id byte invalid (DS1IDFMT) %2.2X\n", f1dscb->ds1fmtid); return 4; } return 0; } /* getF1dscb */ //---------------------------------------------------------------------------------- // getF3dscb - Retrieve Format 3 DSCB //---------------------------------------------------------------------------------- // // Input: // cif ptr to opened CIFBLK of dasd image containing dataset // f3cchhr CCHHR of F3 DSCB to be read (key & data) // f3dscb ptr to F3 DSCB buffer // verbose 0 = no status messages // 1 = status messages // > 1 debugging messages // // Output: // f3buf F3 DSCB (44 byte key, 96 byte data) // // Returns: 0 OK, else error // // Notes: The F3 DSCB describes additional dataset extents beyond those // described by the F1 DSCB. Each F3 DSCB describes 13 extents. // Physical sequential datasets are limited to 16 extents on each // volume, extended format datasets are limited to 123 extents on // each volume. Dasdseq doesn't provide explicit support for // multi-volume datasets. // // Note there is extent information embedded in the key. // // If you want support for > 16 extents, you will have to recompile // dasdseq after changing MAX_EXTENTS. // // Warning: I haven't tested the "chase the F3 chain" code, as I have no // reasonable way to do so. The highest level of MVS I can run under // Hercules is MVS38j. //---------------------------------------------------------------------------------- int getF3dscb( CIFBLK *cif, BYTE *f3cchhr, FORMAT3_DSCB *f3dscb, int verbose) { int cyl, head, rec, rc; void *f3key, *f3data; int f3kl, f3dl; cyl = (f3cchhr[0] << 8) | f3cchhr[1]; head = (f3cchhr[2] << 8) | f3cchhr[3]; rec = f3cchhr[4]; if (verbose) fprintf(stderr, "getF3dscb reading F3 DSCB " "cyl %d head %d rec %d\n", cyl, head, rec); rc = read_block (cif, cyl, head, rec, (void *)&f3key, &f3kl, (void *)&f3data, &f3dl); if (rc) { fprintf(stderr, "getF3dscb error reading F3 DSCB, rc %d\n", rc); return 1; } if ((f3kl != 44) || (f3dl != 96)) { fprintf(stderr, "getF3dscb bad key %d or data %d length\n", f3kl, f3dl); return 2; } memcpy((void *) &f3dscb->ds3keyid, f3key, f3kl); // copy F3 key to buffer memcpy((void *) ((BYTE*)f3dscb + f3kl), f3data, f3dl); // copy F3 data to buffer if (verbose > 1) { fprintf(stderr, "getF3dscb F3 DSCB\n"); data_dump((void *) f3dscb, sizeof(FORMAT3_DSCB)); } // Verify DS3FMTID byte = x'F3' // Do this after copying data to caller's buffer so we can use struct fields // rather than having to calculate offset to verified data; little harm done // if it doesn't verify since we're toast if it's bad. if (f3dscb->ds3fmtid != 0xf3) { fprintf(stderr, "getF3dscb " "F3 DSCB format id byte invalid (DS3IDFMT) %2.2X\n", f3dscb->ds3fmtid); return 2; } return 0; } /* getF3dscb */ //---------------------------------------------------------------------------------- // dadsm_setup - retrieve volume label & DSCBs sufficient to describe dataset //---------------------------------------------------------------------------------- // // This routine reads the volume label rec, the VTOC F4 DSCB, the F1 DSCB // for the dataset, and any F3 DSCB(s) associated with the dataset. // Constructs extent array describing space allocated to the dataset. // // Input: // cif ptr to opened CIFBLK of dasd image containing dataset // pdsn ptr to ASCII null-terminated dataset name // dadsm ptr to DADSM workarea // verbose 0 = no status messages // 1 = status messages // > 1 debugging messages // // Output: // dadsm DADSM workarea // // Returns: 0 OK, else error // // Notes: //---------------------------------------------------------------------------------- int dadsm_setup( CIFBLK *cif, char *pdsn[], DADSM *dadsm, int verbose) { DSXTENT *f1x; BYTE *pcchhr; int numx = MAX_EXTENTS; // # extent slots available int rc; // Read dasd volume label record rc = getlabel(cif, &dadsm->volrec, verbose); if (rc) return rc; // Read F4 DSCB, save VTOC extent info rc = getF4dscb(cif, &dadsm->f4buf, &dadsm->volrec, &dadsm->f4ext, verbose); if (rc) return rc; // Read F1 DSCB, save first three extents from F1 DSCB rc = getF1dscb(cif, pdsn, &dadsm->f1buf, (void *)&dadsm->f4ext, verbose); if (rc) return rc; f1x = &dadsm->f1ext[0]; // @ extent # 0 numx -= 3; // will use 3 slots (if available) if (numx < 0) { fprintf(stderr, "dadsm_setup exhausted extent slots\n"); return 1; } memcpy(f1x, &dadsm->f1buf.ds1ext1, sizeof(DSXTENT) * 3); f1x += 3; // @ extent # 3 dadsm->f1numx = dadsm->f1buf.ds1noepv; // # extents alloc'd to dataset if (dadsm->f1numx < 4) { if (verbose > 1) fprintf(stderr, "dadsm_setup " "no F3 DSCB required, only %d extent(s); all in F1\n", dadsm->f1numx); return 0; } // When more than 3 extents, get additional extent info from F3 DSCB(s). // Chase the F3 chain starting with the CCHHR in the F1, accumulating // extent information for the dataset as we progress. pcchhr = (BYTE *)&dadsm->f1buf.ds1ptrds; // @ F1 ptr to F3 while (pcchhr[0] || pcchhr[1] || pcchhr[2] || pcchhr[3] || pcchhr[4]) { rc = getF3dscb(cif, pcchhr, &dadsm->f3buf, verbose); if (rc) return rc; numx -= 4; // use extent slots if (numx < 0) { fprintf(stderr, "dadsm_setup exhausted extent slots\n"); return 2; } memcpy(f1x, &dadsm->f3buf.ds3extnt[0], sizeof(DSXTENT) * 4); f1x += 4; numx -= 9; // use extent slots if (numx < 0) { fprintf(stderr, "dadsm_setup exhausted extent slots\n"); fprintf(stderr, "Maximum supported extents %d\n", MAX_EXTENTS); return 3; } memcpy(f1x, &dadsm->f3buf.ds3adext[0], sizeof(DSXTENT) * 9); f1x += 9; pcchhr = (BYTE *)&dadsm->f3buf.ds3ptrds; // @ next F3 CCHHR } return 0; } /* dadsm_setup */ //---------------------------------------------------------------------------------- // Main //---------------------------------------------------------------------------------- int main(int argc, char **argv) { DADSM dadsm; // DADSM workarea FILE *fout = NULL; // output file CIFBLK *cif; int dsn_recs_written = 0, bail, dsorg, rc; char pathname[MAX_PATH]; fprintf(stderr, "dasdseq %s Copyright 1999-2006 Roger Bowler\n" "Portions Copyright 2001-2006 James M. Morrison\n", VERSION); if (debug) fprintf(stderr, "DEBUG enabled\n"); // Parse command line memset(&dadsm, 0, sizeof(dadsm)); // init DADSM workarea rc = parsecmd(argc, argv, &dadsm); if (rc) exit(rc); // Open CKD image cif = open_ckd_image(din, sfn, O_RDONLY | O_BINARY, 0); if (!cif) { fprintf(stderr, "dasdseq unable to open image file %s\n", din); exit(20); } // Unless -abs specified (in which case trust the expert user): // Retrieve extent information for the dataset // Display dataset attributes // Verify dataset has acceptable attributes if (!absvalid) { rc = dadsm_setup(cif, &argdsn, &dadsm, local_verbose); if (rc) { close_ckd_image(cif); exit(rc); } if (local_verbose) { fprintf(stderr, "\n"); showf1(stderr, &dadsm.f1buf, dadsm.f1ext, copy_verbose); fprintf(stderr, "\n"); } bail = 1; dsorg = (dadsm.f1buf.ds1dsorg[0] << 8) | (dadsm.f1buf.ds1dsorg[1]); if (dsorg & (DSORG_PS * 256)) { if ((dadsm.f1buf.ds1recfm & RECFM_FORMAT) == RECFM_FORMAT_F) bail = 0; if ((dadsm.f1buf.ds1recfm & RECFM_FORMAT) == RECFM_FORMAT_V) { bail = 1; // not yet fprintf(stderr, "dasdseq only supports RECFM=F[B]\n"); } } else fprintf(stderr, "dasdseq only supports DSORG=PS datasets\n"); if (bail) { close_ckd_image(cif); exit(21); } } // Open output dataset (EBCDIC requires binary open) hostpath(pathname, argdsn, sizeof(pathname)); fout = fopen(pathname, (tran_ascii) ? "wb" : "w"); if (fout == NULL) { fprintf(stderr, "dasdseq unable to open output file %s, %s\n", argdsn, strerror(errno)); close_ckd_image(cif); exit(22); } if (local_verbose) fprintf(stderr, "dasdseq writing %s\n", argdsn); // Write dasd data to output dataset dsn_recs_written = fbcopy(fout, cif, &dadsm, tran_ascii, copy_verbose); if (dsn_recs_written == -1) fprintf(stderr, "dasdseq error processing %s\n", argdsn); else fprintf(stderr, "dasdseq wrote %d records to %s\n", dsn_recs_written, argdsn); // Close output dataset, dasd image and return to caller fclose(fout); if (local_verbose > 2) fprintf(stderr, "CLOSED %s\n", argdsn); if (local_verbose > 3) { fprintf(stderr, "CIFBLK\n"); data_dump((void *) cif, sizeof(CIFBLK)); } close_ckd_image(cif); if (local_verbose > 2) fprintf(stderr, "CLOSED image\n"); return rc; } /* main */