#ifdef ournix #include "ournix.h" #endif #ifndef LINT /* { */ char sccsID[] = "@(#) slice.c V3.9 Copyright Julian H. Stacey, Munich, \ 1990 12 13 - 2005 01 02.\n"; #endif /* } */ /* Manual exists */ #include #ifndef MSDOS /* { unix */ #include #endif /* } */ #include #include #ifdef MSDOS /* { */ #include #endif /* } */ #ifdef i386 /* { */ #include #endif /* } */ #include #include extern int atoi() ; #ifndef unix extern char *malloc() ; #else /* #include #error " has been replaced by " */ #include #endif typedef char FLAG ; char **ARGV; FLAG pipe_f = 0 ; /* 1 = tape data is to be connected to a process pipe */ FLAG zero_f = 0 ; /* 1 = warn if suspicious all zeroes */ FLAG ones_f = 0 ; /* 1 = warn if suspicious all ones */ FLAG write_f = 0 ; /* 1 = write tape */ FLAG read_f = 1 ; /* 1 = read tape */ FLAG delete_f = 0 ; /* 1 = delete slice after writing slice to output */ FLAG verbose_f = 0 ; /* 1 = provide verbose diagnostics */ FLAG no_rewind_f = 0 ; /* 1 = dont rewind before testing tape, allows user * to skip previous files on tape by reading * a no_rewind_on_close device from a shell script, * before then calling slice */ int block_size = 8 * 1024 ; /* tar B is 20 */ /* Symmetric Unix device driver actually appears to write 8192 bytes * at a time to /dev/rcst2, regardless of the block size this user * program passes to the system */ char *base_p ; /* read pointer */ long skip_count = 0L ; /* allows us to skip some initial blocks */ #ifdef unix /* { */ char default_tape[] = "/dev/rmt8" ; #endif /* } */ #ifdef MSDOS /* { */ char default_tape[] = "cst:" ; /* need msdos adviser for name cst: ? */ #endif /* } */ FILE *my_out ; /* prior to 386bsd, used to be: struct _iobuf *my_out */ char skeleton[] = "%s/tp_%07ld.%s" ; /* slice file names */ /* 7 digits allows a 4 gig * DVD to be sliced into 2K * files (though the top * directory gets awfully big * & ls very slow) */ main(argc,argv) int argc ; char **argv ; { int errs = 0 ; /* cumulative error count */ int byte_count ; char *p_tmp ; ARGV = argv ; #ifdef VSL /* { */ #include "../../include/vsl.h" #endif /* } */ my_out = stdout ; while (--argc) { if (**++argv == '-') { switch ( *++*argv ) { case '-': pipe_f = 1 ; break ; case 'v' : verbose_f = 1 ; break ; case 'n' : no_rewind_f = 1 ; break ; case 'r' : read_f = 1 ; write_f = 0 ; break ; case 'w' : write_f = 1 ; read_f = 0 ; break ; case 'd' : delete_f = 1 ; break ; case 'b' : if (--argc <= 0) syntax() ; ++argv ; p_tmp = *argv+strlen(*argv) -1 ; byte_count = 1 ; if ((*p_tmp == 'k') || (*p_tmp == 'K') ) { byte_count = 1024 ; *p_tmp = '\0' ; } else if ((*p_tmp == 'b') || (*p_tmp == 'B') ) { byte_count = 512 ; *p_tmp = '\0' ; } else if ((*p_tmp == 'w') || (*p_tmp == 'W') ) { byte_count = 2 ; *p_tmp = '\0' ; } byte_count = byte_count * atoi(*argv); if (byte_count < 0 ) syntax() ; block_size = byte_count ; break ; case 's' : if (--argc <= 0) syntax() ; skip_count = atol(*++argv); if (skip_count < 0 ) syntax() ; break ; case '0' : zero_f = 1 ; break ; case '1' : ones_f = 1 ; break ; default: syntax() ; break ; } } else break ; } if ( zero_f && ones_f ) { fprintf(stderr, "%s: -0 & -1 not allowed together.\n", *ARGV) ; syntax() ; } if (verbose_f) fprintf(my_out,"Using a block size of %d\n",block_size); if ((argc == 0) && !pipe_f) /* user has not specified file names or pipe IO, we will use default name, but in case user actually wanted pipe IO, we warn him */ fprintf(my_out, "%s: %s %s %s %s\n", *ARGV, "Will use", default_tape, "As neither file names given,", "nor pipe IO specified with --" ) ; if (argc > 1) syntax() ; if ((argc == 1) && pipe_f) { fprintf(stderr, "%s: Pipes output and file output not allowed together.\n", *ARGV) ; syntax() ; } if (delete_f && read_f) syntax() ; if (pipe_f && write_f) { /* Avoid errors going into pipe */ fprintf(stderr, "%s: Status to , avoids data on pipe.\n", *ARGV ); my_out = stderr ; } /* End of checking parameters */ if ((base_p = malloc((unsigned)block_size)) == (void *)0) { fprintf(stderr,"%s: malloc(%u) failed.\n",*ARGV,block_size) ; perror(*ARGV) ; my_exit(1) ; } if (pipe_f) my_exit(do_tape((write_f) ? "pipe_output" : "pipe_input")) ; else if (argc == 0) my_exit(do_tape(default_tape)) ; else while (argc--) /* currently only 1 file is allowable */ errs += do_tape(*argv++) ; if (base_p != (void *)0) free(base_p); my_exit(errs) ; } /* return 0 = ok, 1 = error */ do_tape(tape_name) char *tape_name ; { int fd_tape ; /* tape file descrip */ int fd_slice ; /* slice file descrip */ char *message ; int read_count ; int write_count ; char dir_name[20] ; /* eg "sl_12345" */ char slice_name[2*20] ; /* eg "sl_21224/tp_2122415.ok" */ int errs = 0 ; long prefix = 0L ; /* sequential 1 2 3 4 */ char *postfix ; /* "ok" or "bad" */ /* If read values from tape happened to return 1024 1024 1023 1 1024 (as happens on SCS occasionally) file names written would be tp_0000001.ok tp_0000002.ok tp_0000003.bad tp_000004.bad tp_0000005.ok (which is not what is wanted) */ message = write_f ? "writing" : "reading" ; if (verbose_f) fprintf(my_out,"%s %s %s.\n",*ARGV, message, tape_name); if (read_f) { if (!(pipe_f || no_rewind_f)) my_rewind(tape_name); if (pipe_f) fd_tape = fileno(stdin) ; else if ((fd_tape = open(tape_name,O_RDONLY #ifdef MSDOS /* { */ | O_BINARY , S_IREAD #else /* }{ */ ,0640 #endif /* } */ )) == EOF ) { perror(*ARGV) ; my_exit(1) ; } for (;;) { read_count = read(fd_tape, base_p, block_size ) ; if (read_count == 0 ) break ; /* EOF */ #define AMOUNT 100 if ((prefix % AMOUNT) == 0L) { sprintf(dir_name,"sl_%05d", (int)(prefix / AMOUNT) ) ; mkdir(dir_name,0755); } postfix = (read_count == block_size ) ? "ok" : "bad" ; (void)sprintf(slice_name, skeleton, dir_name, prefix,postfix); if (read_count != block_size) printf( "%s: for %s, tried to read %d, got %d.\n", *ARGV, slice_name, block_size, read_count ) ; /* maybe we tried to read 20K of tape, when * only 10K remained for input, or maybe * an scs tape driver lost a byte, & we are * in the middle of reading 8191 bytes, * followed by 1 byte ! */ if (read_count < 0 ) { /* error */ perror(*ARGV) ; errs++ ; break ; } if (zero_f || ones_f) block_faked( read_count, slice_name ) ; if (skip_count > 0L) skip_count-- ; else { if ((fd_slice = open(slice_name, O_WRONLY | O_CREAT | O_TRUNC #ifdef MSDOS /* { */ | O_BINARY , S_IWRITE #else /* }{ */ ,0640 #endif /* } */ )) == EOF ) { perror(*ARGV) ; fprintf(my_out, "%s: Error: failed to open %s for writing.\n", *ARGV, slice_name) ; errs++ ; break ; } if ((write_count = write(fd_slice,base_p,read_count)) != read_count) { fprintf(my_out, "%s: Error: failed to write %d bytes to %s (only wrote %d), so deleted.\n", *ARGV, read_count, slice_name, write_count) ; /* if disk becomes full by writing slice, we get a short slice with name .ok, this could be embarassing, so it is deleted */ unlink(slice_name); errs++ ; break ; } (void) close(fd_slice) ; } prefix++ ; } if (!pipe_f) (void) close(fd_tape) ; } else /* write_f */ { if (!pipe_f && !no_rewind_f) my_rewind(tape_name); if (pipe_f) fd_tape = fileno(stdout) ; else if ((fd_tape = open(tape_name,O_WRONLY | O_CREAT | O_TRUNC #ifdef MSDOS /* { */ | O_BINARY , S_IWRITE #else /* }{ */ ,0640 #endif /* } */ )) == EOF ) { perror(*ARGV) ; my_exit(1) ; } for (;;) { if ((prefix % AMOUNT) == 0L) (void) sprintf(dir_name,"sl_%05d", (int)(prefix / AMOUNT) ) ; (void)sprintf(slice_name, skeleton, dir_name, prefix, "ok"); if ( skip_count > 0L ) skip_count-- ; else { if ((fd_slice = open(slice_name, O_RDONLY #ifdef MSDOS /* { */ | O_BINARY , S_IREAD #endif /* } */ )) == EOF ) { fprintf(my_out, "%s: Error: failed to open %s for reading.\n", *ARGV, slice_name) ; perror(*ARGV) ; errs++ ; break ; } if ((read_count = read(fd_slice,base_p,block_size)) != block_size) { perror(*ARGV) ; fprintf(my_out, "%s: Warning: failed to read %d bytes from %s (only read %d).\n", *ARGV, block_size, slice_name, read_count) ; /* maybe we tried to read 20K when only * 10K remained in input file, or maybe * an scs tape driver lost a byte !, * in which case we have just read * 8191 bytes, 1 byte, or 0 bytes * if a human has concatenated * 2 slices together, & created a dummy * slice to get us to skip to next real * 8192 slice */ } if (read_count < 0 ) { /* error */ perror(*ARGV) ; errs++ ; break ; } if (zero_f || ones_f) block_faked( read_count, slice_name ) ; if ((write_count = write(fd_tape, base_p, read_count ) ) != read_count) { printf( "%s: for %s, tried to write %d, wrote %d.\n", *ARGV, slice_name, read_count, write_count ) ; perror(*ARGV) ; errs++ ; break ; /* EOF */ } if (close(fd_slice)) { errs++ ; printf( "%s: Error: failed to close %s.\n", *ARGV, slice_name ) ; perror(*ARGV) ; break ; } } if (delete_f && unlink(slice_name)) { errs++ ; printf("%s: Caution: failed to unlink %s.\n", *ARGV, slice_name ) ; perror(*ARGV) ; } if (delete_f && ((prefix % AMOUNT) == AMOUNT - 1) ) { /* remove used directory */ (void) rmdir(dir_name); } prefix++ ; } if (delete_f && !errs && ((prefix % AMOUNT) != 0L) ) /* remove last directory */ (void) rmdir(dir_name); if (!pipe_f) (void) close(fd_tape) ; } return(errs?1:0) ; } /* Warns if a defective driver or drive or media has delivered a block with all bits low or high. */ block_faked( blok_size, slice ) unsigned blok_size ; /* buffer size */ char *slice ; /* name of slice */ { char *bas_p = base_p ; /* buffer base */ unsigned char c ; if (!(zero_f || ones_f)) return ; while (blok_size--) { c = *bas_p++ ; if (( c == 0 ) && zero_f ) continue ; if (( c == 0xFF ) && ones_f ) continue ; return ; } fprintf(my_out, "%s: Warning: %s just %s.\n", *ARGV, slice, ( ones_f ) ? "0xFF" : "0x00" ) ; } /* open & close device to ensure if the physical device was previously * used as `a no rewind on completion' tape drive, that it will now be * rewound prior to use. * fd_tape is not left open because although it would be slightly * more effecient on cpu, it would complicate the code too much, * and compared with the real physical device work we are * doing, a couple more system calls dont make much difference. */ my_rewind(tape_name) char *tape_name ; { int fd_tape ; /* file descrip */ if (verbose_f) fprintf(my_out, "%s: Started rewinding %s.\n",*ARGV,tape_name) ; if ((fd_tape = open(tape_name, O_RDONLY )) == EOF) { fprintf(my_out, "%s: Error: failed to open %s to ensure rewound.\n", *ARGV, tape_name) ; perror(*ARGV) ; return(1) ; } /* now rewind tape */ /* Used to do an lseek, but maybe this doesnt retention tape, * so after a tape got mangled, repaired, mangled again, i * decided to do a real read followed by close, to ensure * tape is rewound, but I dont really know how the TEAC * drive is controlled, so this read may be superfluous, * however before taking it out, consider the possibility * of multiple system crashes whilst the tape is inserted, * and or multiple insertions before the tape is actually used. * Only problem with a read is that if it is virgin media, * tape driver reports an error on /dev/console. */ if ( read(fd_tape,base_p,block_size) != block_size) { fprintf(my_out, "%s: Error: failed to rewind %s.\n", *ARGV, tape_name) ; perror(*ARGV) ; return(1) ; } (void) close(fd_tape) ; if (verbose_f) fprintf(my_out, "%s: Finished rewinding %s.\n", *ARGV,tape_name) ; return(0); } syntax() { fprintf(my_out, "%s\n%s %s [-- or special_file]\n", "Syntax error, correct syntax is:", *ARGV, "[-b block_size] [-d] [-r] [-n] [-s skip_blocks] [-v] [-w] [-0] [-1] [-- or file or special_file]" ) ; my_exit(1) ; } my_exit(val) int val ; { if (verbose_f) fprintf(my_out,"%s: Finished.\n",*ARGV); exit(val) ; }