/* * * Title: * Backup * * Synopsis: * vmsbu [-f tapefile] [-t] [filename1] [filename2] ... * * Decription: * Program to extract files from a VMS backup tape * * -f Use the next argument as the name of the tapefile instead * of /dev/rmt0m * * -t List the contents of the tape. No files are extracted and * filename1, filename2 ... are ignored. * * If no filenames are specified, the entire tape is retored. If one * or more of filename1, filename2, ... are specified, only these * files are extracted. The filenames cannot contain wildcards and * must be appear exactly as they are on the tape. This usually means * that filenames must be enclosed in double quotes, as the vms version * number is preceeded by a ";". * * Author: * John Douglas CAREY. * modified - Doug Shearer Feb 87 * * Net-addess: * john%monu1.oz@seismo.ARPA * * History: * Version 1.0 - September 1984 * Can only read variable length records * Version 1.1 * Cleaned up the program from the original hack * Can now read stream files * Version 1.2 * Now convert filename from VMS to UNIX * and creates sub-directories * Version 1.3 * Works on the Pyramid if SWAP is defined * Version 1.4 * Reads files spanning multiple tape blocks * Version 1.5 * Always reset reclen = 0 on file open * Now output fixed length records * * Version 2.0 - July 1985 * VMS Version 4.0 causes a rethink !! * Now use mtio operations instead of opening and closing file * Blocksize now grabed from the label * * Version 2.1 - September 1985 * Handle variable length records of zero length. * * Version 2.2 - July 1986 * Handle FORTRAN records of zero length. * Inserted exit(0) at end of program. * Distributed program in aus.sources * * Version 2.3 - August 1986 * Handle FORTRAN records with record length fields * at the end of a block * Put debug output to a file. * Distributed program in net.sources * * Version ?? - Feb 1987 ... modified Doug Shearer * add -f and -t options * delete code to fix up VAX Fortran carriage control * * Version ?? - Jan 1990 ... modified Ken Rossman * Change default tape drive (back) to /dev/rmt0. * * Installation: * * Computer Centre * Monash University * Wellington Road * Clayton * Victoria 3168 * AUSTRALIA * */ #include #include #include #include #include #include #ifdef pyr #define SWAP #endif pyr struct bbh { short bbh_dol_w_size; short bbh_dol_w_opsys; short bbh_dol_w_subsys; short bbh_dol_w_applic; long bbh_dol_l_number; char bbh_dol_t_spare_1[20]; short bbh_dol_w_struclev; short bbh_dol_w_volnum; long bbh_dol_l_crc; long bbh_dol_l_blocksize; long bbh_dol_l_flags; char bbh_dol_t_ssname[32]; short bbh_dol_w_fid[3]; short bbh_dol_w_did[3]; char bbh_dol_t_filename[128]; char bbh_dol_b_rtype; char bbh_dol_b_rattrib; short bbh_dol_w_rsize; char bbh_dol_b_bktsize; char bbh_dol_b_vfcsize; short bbh_dol_w_maxrec; long bbh_dol_l_filesize; char bbh_dol_t_spare_2[22]; short bbh_dol_w_checksum; } *block_header; struct brh { short brh_dol_w_rsize; short brh_dol_w_rtype; long brh_dol_l_flags; long brh_dol_l_address; long brh_dol_l_spare; } *record_header; /* define record types */ #define brh_dol_k_null 0 #define brh_dol_k_summary 1 #define brh_dol_k_volume 2 #define brh_dol_k_file 3 #define brh_dol_k_vbn 4 #define brh_dol_k_physvol 5 #define brh_dol_k_lbn 6 #define brh_dol_k_fid 7 struct bsa { short bsa_dol_w_size; short bsa_dol_w_type; char bsa_dol_t_text[1]; } *data_item; #ifdef STREAM char *tapefile = "/dev/rts8"; #else char *tapefile = "/dev/rmt0"; #endif char filename[128]; int filesize; char recfmt; /* record format */ #define FAB_dol_C_UDF 0 /* undefined */ #define FAB_dol_C_FIX 1 /* fixed-length record */ #define FAB_dol_C_VAR 2 /* variable-length record */ #define FAB_dol_C_VFC 3 /* variable-length with fixed-length control record */ #define FAB_dol_C_STM 4 /* RMS-11 stream record (valid only for sequential org) */ #define FAB_dol_C_STMLF 5 /* stream record delimited by LF (sequential org only) */ #define FAB_dol_C_STMCR 6 /* stream record delimited by CR (sequential org only) */ #define FAB_dol_C_MAXRFM 6 /* maximum rfm supported */ char recatt; /* record attributes */ #define FAB_dol_V_FTN 0 /* FORTRAN carriage control character */ #define FAB_dol_V_CR 1 /* line feed - record -carriage return */ #define FAB_dol_V_PRN 2 /* print-file carriage control */ #define FAB_dol_V_BLK 3 /* records don't cross block boundaries */ #define FANO 20 #ifdef pyr static struct bsa *file_table[FANO]; #else struct bsa *file_table[FANO]; #endif FILE *f = NULL; int file_count; short reclen; short fix; short recsize; int vfcsize; #ifdef NEWD FILE *lf; #endif NEWD #define TRUE 1 #define FALSE 0 char **selected_names; int select = FALSE; int list = FALSE; FILE * openfile(fn) char *fn; { char ufn[256]; char *p, *q, s; /* copy fn to ufn and convert to lower case */ p = fn; q = ufn; while (*p) { if (isupper(*p)) *q = *p - 'A' + 'a'; else *q = *p; p++; q++; } *q = '\0'; /* convert the VMS to UNIX and make the directory path */ p = ufn; q = ++p; while (*q) { if (*q == '.' || *q == ']') { s = *q; *q = '\0'; mkdir(p, 0755); *q = '/'; if (s == ']') break; } *q++; } #ifdef VERNO /* strip off the version number */ while (*q && *q != ';') q++; *q = '\0'; #endif /* open the file for writing */ return(fopen(p, "w")); } process_file(buffer) char *buffer; { int i, n; char *p, *q; short dsize, nblk, lnch; int c; short *s; s = (short *) buffer; /* check the header word */ if (*s != 257) { printf("Snark: invalid data header\n"); exit(1); } c = 2; for (i = 0; i < FANO; i++) { file_table[i] = (struct bsa *) &buffer[c]; #ifndef SWAP dsize = file_table[i]->bsa_dol_w_size; #else swap(&file_table[i]->bsa_dol_w_size, &dsize, sizeof(short)); #endif c += dsize + 4; } /* extract file name */ #ifndef SWAP dsize = file_table[0]->bsa_dol_w_size; #else swap(&file_table[0]->bsa_dol_w_size, &dsize, sizeof(short)); #endif p = file_table[0]->bsa_dol_t_text; q = filename; for (i = 0; i < dsize; i++) *q++ = *p++; *q = '\0'; /* extract file's record attributes */ #ifndef SWAP dsize = file_table[5]->bsa_dol_w_size; #else swap(&file_table[5]->bsa_dol_w_size, &dsize, sizeof(short)); #endif p = file_table[5]->bsa_dol_t_text; recfmt = p[0]; recatt = p[1]; #ifndef SWAP bcopy(&p[2], &recsize, sizeof(short)); #else swap(&p[2], &recsize, sizeof(short)); #endif vfcsize = p[15]; if (vfcsize == 0) vfcsize = 2; #ifdef DEBUG printf("recfmt = %d\n", recfmt); printf("recatt = %d\n", recatt); printf("reclen = %d\n", recsize); printf("vfcsize = %d\n", vfcsize); #endif #ifndef SWAP bcopy(&p[10], &nblk, sizeof(short)); bcopy(&p[12], &lnch, sizeof(short)); #else swap(&p[10], &nblk, sizeof(short)); swap(&p[12], &lnch, sizeof(short)); #endif filesize = (nblk-1)*512 + lnch; #ifdef DEBUG printf("nbk = %d, lnch = %d\n", nblk, lnch); printf("filesize = 0x%x\n", filesize); #endif /* open the file */ if (f != NULL) { fclose(f); file_count = 0; reclen = 0; } /* this is a real HACK: */ /* if listing the tape, print file name and put file to /dev/null */ /* if selecting files and this one is not selected put file to /dev/null */ /* if " " " " " " selected print "extracted" and */ /* open the real file name */ if (list == TRUE) { printf("%s\n", filename); f = openfile("/dev/null"); } else if (select == FALSE | (select == TRUE && is_it_selected(filename) == TRUE)) { printf("extracting %s\n", filename); f = openfile(filename); } else { f = openfile("/dev/null"); } } /* * * does the filename match one of the ones the user asked for ? * */ is_it_selected(filename) char *filename; { char **tmp; for (tmp = selected_names; *tmp != NULL; tmp++) { if (!strcmp(filename, *tmp)) return(1); } return(0); } /* * * process a virtual block record (file record) * */ process_vbn(buffer, rsize) char *buffer; unsigned short rsize; { int c, i; if (f == NULL) { return; } i = 0; while (file_count+i < filesize && i < rsize) { switch (recfmt) { case FAB_dol_C_FIX: if (reclen == 0) { reclen = recsize; } fputc(buffer[i], f); i++; reclen--; if (recsize % 2 == 1 && reclen == 0) { /* skip the null character padding at end of odd */ /* length record */ i++; } break; case FAB_dol_C_VAR: case FAB_dol_C_VFC: if (reclen == 0) { reclen = *((short *) &buffer[i]); #ifdef SWAP swap(&reclen, &reclen, sizeof(short)); #endif #ifdef NEWD fprintf(lf, "---\n"); fprintf(lf, "reclen = %d\n", reclen); fprintf(lf, "i = %d\n", i); fprintf(lf, "rsize = %d\n", rsize); #endif NEWD fix = reclen; i += 2; if (recfmt == FAB_dol_C_VFC) { i += vfcsize; reclen -= vfcsize; } #ifdef FIX_FORT_CARRIAGECONTROL } else if (reclen == fix && recatt == (1 << FAB_dol_V_FTN)) { if (buffer[i] == '0') fputc('\n', f); else if (buffer[i] == '1') fputc('\f', f); i++; reclen--; #endif FIX_FORT_CARRIAGECONTROL } else { fputc(buffer[i], f); i++; reclen--; } if (reclen == 0) { fputc('\n', f); if (i & 1) i++; } break; case FAB_dol_C_STM: case FAB_dol_C_STMLF: if (reclen < 0) { printf("SCREAM\n"); } if (reclen == 0) { reclen = 512; } c = buffer[i++]; reclen--; if (c == '\n') { reclen = 0; } fputc(c, f); break; case FAB_dol_C_STMCR: c = buffer[i++]; if (c == '\r') fputc('\n', f); else fputc(c, f); break; default: fclose(f); unlink(filename); fprintf(stderr, "Invalid record format = %d\n", recfmt); return; } } file_count += i; } #ifdef SWAP /* * * do swapping for Motorola type architectures * */ swap(from, to, nbytes) char *from, *to; int nbytes; { int i, j; char temp[100]; for (i = 0; i < nbytes; i++) temp[i] = from[i]; for (i = 0, j = nbytes-1; i < nbytes; i++, j--) to[i] = temp[j]; } #endif /* * * process a backup block * */ process_block(block, blocksize) char *block; int blocksize; { unsigned short bhsize, rsize, rtype; unsigned long bsize, i; i = 0; /* read the backup block header */ block_header = (struct bbh *) &block[i]; i += sizeof(struct bbh); bhsize = block_header->bbh_dol_w_size; bsize = block_header->bbh_dol_l_blocksize; #ifdef SWAP swap(&bhsize, &bhsize, sizeof(short)); swap(&bsize, &bsize, sizeof(long)); #endif /* check the validity of the header block */ if (bhsize != sizeof(struct bbh)) { fprintf(stderr, "Snark: Invalid header block size\n"); exit(1); } if (bsize != 0 && bsize != blocksize) { fprintf(stderr, "Snark: Invalid block size\n"); exit(1); } #ifdef DEBUG printf("new block: i = %d, bsize = %d\n", i, bsize); #endif /* read the records */ while (i < bsize) { /* read the backup record header */ record_header = (struct brh *) &block[i]; i += sizeof(struct brh); rtype = record_header->brh_dol_w_rtype; rsize = record_header->brh_dol_w_rsize; #ifdef SWAP swap(&rtype, &rtype, sizeof(short)); swap(&rsize, &rsize, sizeof(short)); #endif #ifdef DEBUG printf("rtype = %d\n", rtype); printf("rsize = %d\n", rsize); printf("flags = 0x%x\n", record_header->brh_dol_l_flags); printf("addr = 0x%x\n", record_header->brh_dol_l_address); printf("i = %d\n", i); #endif switch (rtype) { case brh_dol_k_null: #ifdef DEBUG printf("rtype = null\n"); #endif break; case brh_dol_k_summary: #ifdef DEBUG printf("rtype = summary\n"); #endif break; case brh_dol_k_file: #ifdef DEBUG printf("rtype = file\n"); #endif process_file(&block[i]); break; case brh_dol_k_vbn: #ifdef DEBUG printf("rtype = vbn\n"); #endif process_vbn(&block[i], rsize); break; case brh_dol_k_physvol: #ifdef DEBUG printf("rtype = physvol\n"); #endif break; case brh_dol_k_lbn: #ifdef DEBUG printf("rtype = lbn\n"); #endif break; case brh_dol_k_fid: #ifdef DEBUG printf("rtype = fid\n"); #endif break; default: fprintf(stderr, " Snark: invalid record type\n"); fprintf(stderr, " record type = %d\n", rtype); exit(1); } #ifdef pyr i = i + rsize; #else i += rsize; #endif } } #define LABEL_SIZE 80 main(argc, argv) int argc; char *argv[]; { int fd; /* tape file descriptor */ int i; char label[LABEL_SIZE]; char *strncpy(); char *block; int blocksize; struct mtop op; #ifdef NEWD /* open debug file */ lf = fopen("log", "w"); if (lf == NULL) { perror("log"); exit(1); } #endif for (argc--, argv++; argc > 0; argc--, argv++) { if (!strcmp(*argv, "-f")) { argc--, argv++; tapefile = *argv; } else if (!strcmp(*argv, "-t")) { list = TRUE; } else if (*argv == NULL) { select = FALSE; break; } else { select = TRUE; selected_names = argv; break; } } /* open the tape file */ fd = open(tapefile, O_RDONLY); if (fd < 0) { fprintf(stderr,"open error\n"); perror(tapefile); exit(1); } /* rewind the tape */ op.mt_op = MTREW; op.mt_count = 1; i = ioctl(fd, MTIOCTOP, &op); if (i < 0) { fprintf(stderr,"ioctl MTREW error\n"); perror(tapefile); exit(1); } /* read the tape label - 4 records of 80 bytes */ while ((i = read(fd, label, LABEL_SIZE)) != 0) { if (i != LABEL_SIZE) { fprintf(stderr, "Snark: bad label record\n"); exit(1); } /* get the block size */ if (strncmp(label, "HDR2", 4) == 0) { sscanf(label+5, "%5d", &blocksize); #ifdef DEBUG printf("blocksize = %d\n", blocksize); #endif } } #ifdef BOB op.mt_op = MTFSF; op.mt_count = 0; i = ioctl(fd, MTIOCTOP, &op); if ( i < 0) { fprintf(stderr,"ioctl MTFSF error\n"); perror(tapefile); exit(1); } #endif /* get the block buffer */ block = (char *) malloc(blocksize); if (block == (char *) 0) { fprintf(stderr, "memory allocation for block failed\n"); exit(1); } /* read the backup tape blocks until end of file */ while ((i = read(fd, block, blocksize)) != 0) { if (i != blocksize) { fprintf(stderr, "bad block read i = %d\n", i); exit(1); } process_block(block, blocksize); } printf("End of save set\n"); /* close the tape */ close(fd); #ifdef NEWD /* close debug file */ fclose(lf); #endif NEWD /* exit cleanly */ exit(0); }