#!/bin/sh # This is a shell script - extract with sh sed 's/M//' << XXX_EOF_REALLY > README M M Tapecv Version 1.2 M March 17, 1996 M M tapecv is a tool to copy and verify tapes. It requires two M tape drives, but no disk space. It can copy between drives M on one host, or on two different hosts on a network. It handles M any density, capacity, etc., and requires no knowledge of M tape format (except when copying from cartridge to mag tape). M tapecv has special provisions for copying a limited variety M of bootable or fixed blocksize cartridge tapes to magtape. M M It can also be used to determine a tape's structure by M copying it to the null device in verbose mode (the verify M will, of course, fail). M M It should work on any system which provides rexec(3) and M rmt(8). Any machine without these should punt. M M To build tapecv, modify the Makefile to taste and run "make", M followed by "make install" and (optionally) "make man". M M I hereby release this tool to the public domain. It is so M simple it isn't really worth retaining rights to. I would M appreciate it you left my name in this, and in any copies M re-distributed as is or modified. M M Please send any bug fixes and enhancements back to me so I M can maintain a master version. M M David L. Markowitz M David.Markowitz@litronic.com M XXX_EOF_REALLY sed 's/M//' << XXX_EOF_REALLY > CHANGES M M Changes in tapecv V1.2 M MAll counter variables changed from "int" to "unsigned long" to assure Mhandling of longest possible tapes in the future (short of going to M"long long", which most compilers don't GROK yet). M MChanged man page to mention other uses of the -b flag. M MRelaxed limitation on very large tape records. M MAdded a printout of the number of files copied if verbose. M MAdded multiple verbosity levels (0-4). M MAdded -bigtape option. M MAdded -noverify option. M MAdded -rcmd option. M MSimplified open_drives() to open_drive(). M MSimplified close_drives() to close_drive(). M MSimplified rewind_tapes() to rewind_tape(). M MAdded "tapecv:" to all error messages in case it is used from within scripts. M M M Changes in tapecv V1.1 M MChanged #include to to work with BSD and Sys V. M MFixed a bug in close_drives() (cmd not initialized in to.remote case). M MAdded support for Solaris 2 (SVR4). M MAdded partial support for AIX (local tapes only). XXX_EOF_REALLY sed 's/M//' << XXX_EOF_REALLY > rmt_protocol M MThe protocol used by /etc/rmt in SunOS: M M All responses start with M A\n M or M E\n\n M M Some may have extra data on successful execution as documented. M M================================================================================ M M Open: O\n\n M is garbage M M Close: C\n M is garbage M M Lseek: L\n\n M aka: L\n\n M is current file position in bytes M M Write: W\n M is number of bytes written M M Read: R\n M is number of bytes read M follow M M Ioctl: I\n\n M where is an integer from the set M #define MTWEOF 0 /* write an end-of-file record */ M #define MTFSF 1 /* forward space file */ M #define MTBSF 2 /* backward space file */ M #define MTFSR 3 /* forward space record */ M #define MTBSR 4 /* backward space record */ M #define MTREW 5 /* rewind */ M #define MTOFFL 6 /* rewind and put drive offline */ M #define MTNOP 7 /* no op, sets status only */ M #define MTRETEN 8 /* retension the tape */ M #define MTERASE 9 /* erase the entire tape */ M is number of ops performed (actually, after ) M M Status: S M follow M M================================================================================ XXX_EOF_REALLY sed 's/M//' << XXX_EOF_REALLY > Makefile M# M# Choose your compiler. M# MCC= cc M M# M# Define SVR4 if you have memcpy() instead of bcopy(). (i.e. Solaris 2) M# Define AIX if you are building this on an AIX host. M# *Warning* AIX's rmt is incompatible! tapecv will not work M# with a *remote* AIX host, but will work with local tape drives, M# and with remote drives on rmt-compatible hosts. M# MCFLAGS= -O -sb -DSVR4 M M# Add any libraries needed here. MLDFLAGS= -lsocket -lnsl M M# Where the binary and man page go. MDESTDIR=/usr/local/bin MMANDIR= /usr/man/manl M MMAN= tapecv.l M MHDRS= tapecv.h mtio.h M MOBJS= close_drive.o copy_file.o copy_record.o copy_tape.o \ M fdgets.o format.o initialize.o main.o open_drive.o \ M read_block.o readsocket.o rewind_tape.o \ M verify_tape.o verify_file.o verify_record.o M MSRCS= close_drive.c copy_file.c copy_record.c copy_tape.c \ M fdgets.c format.c initialize.c main.c open_drive.c \ M read_block.c readsocket.c rewind_tape.c \ M verify_tape.c verify_file.c verify_record.c M M Mtapecv : $(OBJS) M $(CC) $(LDFLAGS) -o tapecv $(OBJS) M M$(OBJS) : $(HDRS) M M Minstall : tapecv M install -s tapecv $(DESTDIR) M Mclean : M rm -f tapecv $(OBJS) core M Mman : M install -c $(MAN) $(MANDIR) M Mshar : M mkshar tapecv.shar README CHANGES rmt_protocol Makefile $(MAN) $(HDRS) $(SRCS) XXX_EOF_REALLY sed 's/M//' << XXX_EOF_REALLY > tapecv.l M.TH TAPECV 1 local M.SH NAME Mtapecv \- tape copy and verify M.SH SYNOPSIS M.B tapecv M[ -v | -vN ] M[ -b | -B ] M[ -bigtape ] M[ -noverify ] M[ -rcmd ] Mfrom to M.SH DESCRIPTION M.I Tapecv Mis a tool that copies magnetic tapes between two tape drives Mand verifies the copy. MIt can work with local or remote drives, Mat any density supported by the drives. M.I Tapecv Mrequires no knowledge of tape format M(except when copying from cartridge to mag tape). M.PP MThe M.I from Mand M.I to Marguments must be tape device names (i.e. /dev/rmt8). MThey may optionally include a remote host name Mseparated from the device name by a colon (i.e. krypton:/dev/rmt0). M.SH OPTIONS MThe verbose option M.I "-v" Mcauses M.I tapecv Mto print out copious information concerning the tape format Mand the status of the copy. MIf it is followed by a number, it sets verbosity to that level (1-4), Motherwise level 4 is used. MHigher numbers give more output. M.PP MThe record size (or blocking factor) option M.I "-b " Mspecifies the blocking factor in 512-byte tape blocks (ala tar(1)), Mand is useful for copying from cartridge tape to magtape, Mor for specifying the blocking factor when your reading drive doesn't Mhandle requests larger than the actual block size properly. M.I tapecv Massumes that all records are the specified size. MDo not use this when copying from mag tape or between cartridges! M.PP MIf M.I "-B" Mis used instead, M.I tapecv Mwill assume that this is a Sun bootable tape, Mand copy in that special format. M.PP MThe big tape option M.I "-bigtape" Mcauses the tape drives to be closed between each file. MFor this to operate properly, you must specify no-rewind tape devices Mfor both tapes. M(If you don\'t, the tapes will be rewound between each file copy, Mleading to infinite copying if the source tape rewinds or to Moverwriting each file onto the beginning of the target tape if it rewinds. MThis is not enforced by tapecv.) MThis option allows M.I tapecv Mto copy tapes larger than 2 gigabytes, so long as no individual Mfile is longer than that. M.PP MThe M.I "-noverify" Moption turns off the verification pass. MOnly a copy is done. M.PP MThe M.I "-rcmd" Moption causes M.I tapecv Mto use the rcmd() function instead of the rexec() function. MThis requires rsh-like access permissions on remote machines M(~/.rhosts, hosts.equiv) Minstead of using a ~/.netrc file or prompting for a password. M.SH AUTHOR MDavid L. Markowitz M.br MDavid.Markowitz@litronic.com M.SH "SEE ALSO" Mtar(1), dd(1), rmt(8), mtio(4), rsh(1), rcmd(3N), rexec(3N) XXX_EOF_REALLY sed 's/M//' << XXX_EOF_REALLY > tapecv.h M/* M * tapecv.h M * M * Author: David L. Markowitz (dav@gtc.com) M */ M M#include M M#ifdef SVR4 M#include M#define gethostname(s, len) sysinfo(SI_HOSTNAME, s, len) M#define bcopy(s1, s2, n) memcpy(s2, s1, n) M#define bcmp(s1, s2, n) memcmp(s2, s1, n) M#define index(s, c) strchr(s, c) M M char *strchr(); M#else M char *index(); M#endif M M/* Some OSes limit this more. This define should really be OS specific */ M#define MAXBLK (126*1024) M M/* Run-time options */ Mlong max_block; Mint boot_tape; Mint verbose; Mint bigtape; Mint noverify; Mint use_rcmd; Munsigned long nfiles; /* # of files to copy */ M Mchar response[MAXBLK]; Mchar data_from[MAXBLK]; Mchar data_to[MAXBLK]; Mchar from_dev[32], to_dev[32]; M Munsigned long files_copied, M records_copied; Munsigned long files_verified, M records_verified; M Mchar cmd[64]; M Mstruct td { M int fd; M int local; M int cartridge; M}; M Mstruct td from, to; M XXX_EOF_REALLY sed 's/M//' << XXX_EOF_REALLY > mtio.h M/* mtio.h - for vendor independence */ M M#ifdef AIX3 M# include M# include M# define MTIOCTOP STIOCTOP M# define MTREW STREW M# define MTERASE STERASE M# define MTRETEN STRETEN M# define MTWEOF STWEOF M# define MTFSF STFSF M# define MTFSR STFSR M# define MTRSF STRSF M# define MTRSR STRSR M# define mtop stop M# define mt_op st_op M# define mt_count st_count M#else M# include M#endif XXX_EOF_REALLY sed 's/M//' << XXX_EOF_REALLY > close_drive.c M/* M * close_drive M * M * Author: David L. Markowitz (dav@gtc.com) M */ M M#include "tapecv.h" M Mclose_drive(drive) Mstruct td drive; M{ M if (drive.local) { M close(drive.fd); M } M else { M sprintf(cmd, "C\n"); M if (write(drive.fd, cmd, strlen(cmd)) < 0) M perror("tapecv: close_drive: write(drive) failed"); M read(drive.fd, response, 1); M if (response[0] != 'A') { M fdgets(drive.fd, response); /* Error number */ M fdgets(drive.fd, response); /* Error msg */ M fprintf(stderr, "tapecv: close_drive: ioctl failed: %s", M response); M exit(-1); M } M fdgets(drive.fd, response); /* discard this junk */ M } M M return; M} XXX_EOF_REALLY sed 's/M//' << XXX_EOF_REALLY > copy_file.c M/* M * copy_file M * M * Author: David L. Markowitz (dav@gtc.com) M */ M M#include "tapecv.h" M#include M#include M#include "mtio.h" M Mcopy_file(from, to) Mstruct td from, to; M{ M unsigned long records_copied; M struct mtop mtop; M int real_block; M M records_copied = 0; M if (boot_tape && files_copied != 0) { M /* Assume a boot tape's special format (Argh!) */ M real_block = max_block; M max_block = 512; M if (copy_record(from, to)) { M max_block = real_block; M goto eof; M } M max_block = real_block; M records_copied++; M } M M for (; ; records_copied++) M if (copy_record(from, to)) M break; M Meof: M /* Write an EOF mark on the tape */ M if (to.local) { M mtop.mt_op = MTWEOF; M mtop.mt_count = 1; M ioctl(to.fd, MTIOCTOP, &mtop); M } M else { M sprintf(cmd, "I%d\n1\n", MTWEOF); M if (write(to.fd, cmd, strlen(cmd)) < 0) M perror("copy_file - write(to) failed"); M read(to.fd, response, 1); M if (response[0] != 'A') { M fdgets(to.fd, response); /* Error number */ M fdgets(to.fd, response); /* Error msg */ M fprintf(stderr, "tapecv: copy_file: ioctl failed: %s", M response); M exit(-1); M } M fdgets(to.fd, response); /* discard this junk */ M } M M if (bigtape) { M close_drive(from); M open_drive(from, from_dev); M close_drive(to); M open_drive(to, to_dev); M } M M if (verbose > 2) { M if (records_copied > 0) M printf("tapecv: Copied file %d: %d records\n", M files_copied+1, records_copied); M else M printf("tapecv: End of tape - rewinding\n"); M } M M if (records_copied > 0) M return(0); /* Not EOT */ M else M return(1); /* Empty file ::= EOT */ M} XXX_EOF_REALLY sed 's/M//' << XXX_EOF_REALLY > copy_record.c M/* M * copy record M * M * Author: David L. Markowitz (dav@gtc.com) M */ M M#include "tapecv.h" M Mcopy_record(from, to) Mstruct td from, to; M{ M int i; M unsigned long bytes_read = 0; M unsigned long bytes_written = 0; M char *cp; M M bytes_read = read_block(from); M M if (bytes_read == 0) M return(1); M else if (to.cartridge && ((bytes_read % 512) != 0)) { M cp = response + bytes_read; M while ((bytes_read%512) != 0) { M *cp++ = 0; M bytes_read++; M } M } M M if (to.local) { M if ((bytes_written = write(to.fd, response, bytes_read)) M < bytes_read) { M perror("Write failed"); M exit(-1); M } M } M else { M sprintf(cmd, "W%d\n", bytes_read); M write(to.fd, cmd, strlen(cmd)); M write(to.fd, response, bytes_read); M read(to.fd, response, 1); M if (response[0] == 'A') { M fdgets(to.fd, response); M bytes_written = atoi(response); M } M else { M fprintf(stderr, "tapecv: Response was %c\n", M response[0]); M fdgets(to.fd, response); /* Error number */ M fprintf(stderr, M "tapecv: write failed: Error number %s\n", M response); M fdgets(to.fd, response); /* Error msg */ M fprintf(stderr, "%s", response); M exit(-1); M } M } M M if (bytes_written != bytes_read) { M fprintf(stderr, "tapecv: write didn't match read\n"); M fprintf(stderr, "tapecv: %d read, %d written\n", M bytes_read, bytes_written); M exit(-1); M } M M if ((verbose > 3) && (bytes_read > 0)) M printf("tapecv: Copied a %d byte record\n", bytes_read); M M return(0); M} XXX_EOF_REALLY sed 's/M//' << XXX_EOF_REALLY > copy_tape.c M/* M * copy tape M * M * Author: David L. Markowitz (dav@gtc.com) M */ M M#include "tapecv.h" M Mcopy_tape(from, to) Mstruct td from, to; M{ M for (files_copied = 0; files_copied != nfiles; files_copied++) M if (copy_file(from, to)) M break; M M if ((verbose > 1) && (files_copied > 0)) M printf("tapecv: Copied %d files\n", files_copied); M M return; M} XXX_EOF_REALLY sed 's/M//' << XXX_EOF_REALLY > fdgets.c M/* M * fdgets M * M * Read a string from a file descriptor. M * NULL terminate it. M * M * Author: David L. Markowitz (dav@gtc.com) M */ M M#include M Mfdgets(fd, string) Mint fd; Mchar *string; M{ M int offset; M M for (offset=0; ; offset++) { M if (read(fd, string+offset, 1) < 1) { M string[offset] = 0; M break; M } M if (string[offset] == '\n') { M string[++offset] = 0; M break; M } M } M} XXX_EOF_REALLY sed 's/M//' << XXX_EOF_REALLY > format.c M/* M * format() M * M * Author: David L. Markowitz (dav@gtc.com) M */ M M#include "tapecv.h" M Mformat() M{ M fprintf(stderr, "Format: tapecv \n"); M fprintf(stderr, "where can be a local tape drive pathname\n"); M fprintf(stderr, "or a hostname, a colon (:), and a tape drive "); M fprintf(stderr, "on that host.\n"); M exit(-1); M} XXX_EOF_REALLY sed 's/M//' << XXX_EOF_REALLY > initialize.c M/* M * initialize M * M * Author: David L. Markowitz (dav@gtc.com) M */ M M#include "tapecv.h" M#include M Minitialize(argc, argv) Mint argc; Mchar **argv; M{ M char *cp; M char from_host[257], to_host[257]; M char *from_host_name = from_host, *to_host_name = to_host; M char *user, *getenv(); M struct servent *execserv, *getservbyname(); M M /* Defaults */ M nfiles = -1; /* No limit */ M verbose = 0; M bigtape = 0; M noverify = 0; M use_rcmd = 0; M max_block = MAXBLK; M M while (argc > 3) { M if (strncmp(argv[1], "-v", 2) == 0) { M if (argv[1][2] == NULL) M verbose = 4; M else M verbose = argv[1][2] - '0'; M } M else if (strcmp(argv[1], "-b") == 0) { M max_block = atol(argv[2]) * 512; M M /* Solaris and other new OSes may allow very large tape M * block sizes. Default to MAXBLK, but allow the user M * to override this here. M */ M if (max_block < 512) { M printf("tapecv: Block size %d too small!\n", M max_block); M exit(-1); M } M else M printf("tapecv: Block size set to %d bytes (%d blocks)\n", M max_block, max_block/512); M /* Extra decrement */ M argc--; M argv++; M } M else if (strcmp(argv[1], "-B") == 0) { M boot_tape++; M max_block = 8192; M printf("tapecv: Block size set to 8192 bytes (16 blocks)\n"); M } M else if (strcmp(argv[1], "-bigtape") == 0) { M bigtape++; M printf("tapecv: Copying a big tape - make sure you used no-rewind tape devices\n"); M } M else if (strcmp(argv[1], "-noverify") == 0) { M noverify++; M printf("tapecv: Copy only - no verification will be done\n"); M } M else if (strcmp(argv[1], "-rcmd") == 0) { M use_rcmd++; M printf("tapecv: using rcmd()\n"); M } M else if (strcmp(argv[1], "-n") == 0) { M argc--; M argv++; M nfiles = atoi(argv[1]); M printf("tapecv: Copying only the first %d files\n", nfiles); M } M M argc--; M argv++; M } M M /* get drive names */ M if (argc != 3) M format(); M M if ((cp=index(argv[1], ':')) == NULL) { M strcpy(from_dev, argv[1]); M gethostname(from_host, 257); M from.local = 1; M } M else { M *cp = NULL; M strcpy(from_host, argv[1]); M strcpy(from_dev, cp+1); M from.local = 0; M } M if (strncmp(from_dev, "/dev/rmt", 8) == 0) M from.cartridge = 0; M else M from.cartridge = 1; M M if ((cp=index(argv[2], ':')) == NULL) { M strcpy(to_dev, argv[2]); M gethostname(to_host, 257); M to.local = 1; M } M else { M *cp = NULL; M strcpy(to_host, argv[2]); M strcpy(to_dev, cp+1); M to.local = 0; M } M if (strncmp(to_dev, "/dev/rmt", 8) == 0) M to.cartridge = 0; M else M to.cartridge = 1; M M if (verbose) M printf("tapecv: Copying from %s:%s to %s:%s\n", M from_host, from_dev, to_host, to_dev); M M if ((execserv = getservbyname("exec", "tcp")) == NULL) M perror("getservbyname(exec, tcp) failed"); M M if (use_rcmd) { M user = getenv("USER"); M M if (!from.local) { M if ((from.fd = rcmd(&from_host_name, execserv->s_port, M user, user, "/etc/rmt", NULL)) == -1) { M perror("rcmd(from, rmt) failed"); M exit(-1); M } M } M M if (!to.local) { M if ((to.fd = rcmd(&to_host_name, execserv->s_port, M user, user, "/etc/rmt", NULL)) == -1) { M perror("rcmd(to, rmt) failed"); M exit(-1); M } M } M } M else { /* rexec */ M if (!from.local) { M if ((from.fd = rexec(&from_host_name, execserv->s_port, M NULL, NULL, "/etc/rmt", NULL)) == -1) { M perror("rexec(from, rmt) failed"); M exit(-1); M } M } M M if (!to.local) { M if ((to.fd = rexec(&to_host_name, execserv->s_port, M NULL, NULL, "/etc/rmt", NULL)) == -1) { M perror("rexec(to, rmt) failed"); M exit(-1); M } M } M } M M return; M} XXX_EOF_REALLY sed 's/M//' << XXX_EOF_REALLY > main.c M/* M * tapecv M * M * Copy and verify a tape from one possibly remote M * tape drive to another. M * M * Author: David L. Markowitz (David.Markowitz@litronic.com) M */ M M#include "tapecv.h" M Mstatic char *version = "Tapecv version 1.2, 5/23/96"; M Mmain(argc, argv) Mint argc; Mchar **argv; M{ M initialize(argc, argv); M M open_drive(from, from_dev); M open_drive(to, to_dev); M M copy_tape(from, to); M M rewind_tape(from); M rewind_tape(to); M M if (!noverify) M verify_tape(from, to); M M close_drive(from); M close_drive(to); M M exit(0); M} XXX_EOF_REALLY sed 's/M//' << XXX_EOF_REALLY > open_drive.c M/* M * open_drive() M * M * Author: David L. Markowitz (dav@gtc.com) M */ M M#include "tapecv.h" M Mopen_drive(tape_drive, tape_drive_dev) Mstruct td tape_drive; Mchar *tape_drive_dev; M{ M char s[80]; M M /* Initiate rmt's on remote hosts */ M if (tape_drive.local) { M if ((tape_drive.fd = open(tape_drive_dev, 0)) == -1) { M sprintf(s, "tapecv: open(%s) failed", tape_drive_dev); M perror(s); M exit(-1); M } M } M else { M sprintf(cmd, "O%s\n%d\n", tape_drive_dev, 0); M if (write(tape_drive.fd, cmd, strlen(cmd)) < 0) { M sprintf(s, "tapecv: open_drive: write(%s) failed", M tape_drive_dev); M perror(s); M exit(-1); M } M read(tape_drive.fd, response, 1); M if (response[0] != 'A') { M fdgets(tape_drive.fd, response); /* Error num */ M fdgets(tape_drive.fd, response); /* Error msg */ M fprintf(stderr, "tapecv: open_drive: open(%s) failed: %s", M tape_drive_dev, response); M exit(-1); M } M fdgets(tape_drive.fd, response); /* discard this junk */ M } M} XXX_EOF_REALLY sed 's/M//' << XXX_EOF_REALLY > read_block.c M/* M * read block M * M * Author: David L. Markowitz (dav@gtc.com) M */ M M#include "tapecv.h" M#include M Mread_block(from) Mstruct td from; M{ M unsigned long bytes_read; M extern int errno; M char errnum[MAXBLK]; M M if (from.local) { M if ((bytes_read = read(from.fd, response, max_block)) < 0) { M if ((errno == EIO) && from.cartridge) { M /* Pretend this was EOT */ M return(0); M } M perror("tapecv: read from local tape failed"); M exit(-1); M } M } M else { M sprintf(cmd, "R%d\n", max_block); M write(from.fd, cmd, strlen(cmd)); M read(from.fd, response, 1); M if (response[0] == 'A') { M fdgets(from.fd, response); M bytes_read = atoi(response); M readsocket(from.fd, response, bytes_read); M } M else { M /* Get the error info */ M fdgets(from.fd, errnum); /* Error number */ M fdgets(from.fd, response); /* Error msg */ M M if (strcmp(errnum, "5") && from.cartridge) { M /* Pretend this was EOT */ M return(0); M } M fprintf(stderr, M "tapecv: read from remote tape failed: %s", M response); M exit(-1); M } M } M M return(bytes_read); M} XXX_EOF_REALLY sed 's/M//' << XXX_EOF_REALLY > readsocket.c M/* M * Read data from a socket, handling partial reads. M * M * Author: David L. Markowitz (dav@gtc.com) M */ M M#include "tapecv.h" M Mreadsocket(fd, buffer, bytes) Mint fd; Mchar *buffer; Mint bytes; M{ M int i; M M while ((i = read(fd, buffer, bytes)) < bytes) { M if (i < 0) { M perror("tapecv: Read from socket failed"); M exit(-1); M } M if (i == 0) { M fprintf(stderr, "tapecv: EOF on socket\n"); M exit(-1); M } M bytes -= i; M buffer += i; M } M} XXX_EOF_REALLY sed 's/M//' << XXX_EOF_REALLY > rewind_tape.c M/* M * rewind_tape M * M * Author: David L. Markowitz (dav@gtc.com) M */ M M#include "tapecv.h" M#include M#include M#include "mtio.h" M Mrewind_tape(tape) Mstruct td tape; M{ M struct mtop mtop; M M /* rewind this tape */ M if (tape.local) { M mtop.mt_op = MTREW; M mtop.mt_count = 1; M ioctl(tape.fd, MTIOCTOP, &mtop); M } M else { M sprintf(cmd, "I%d\n1\n", MTREW); M if (write(tape.fd, cmd, strlen(cmd)) < 0) M perror("tapecv: write(tape) failed"); M read(tape.fd, response, 1); M if (response[0] != 'A') { M fdgets(tape.fd, response); /* Error number */ M fdgets(tape.fd, response); /* Error msg */ M fprintf(stderr, "tapecv: ioctl failed: %s", M response); M exit(-1); M } M fdgets(tape.fd, response); /* discard this junk */ M } M M return; M} XXX_EOF_REALLY sed 's/M//' << XXX_EOF_REALLY > verify_tape.c M/* M * verify_tape M * M * Author: David L. Markowitz (dav@gtc.com) M */ M M#include "tapecv.h" M Mverify_tape(from, to) Mstruct td from, to; M{ M for (files_verified = 0; nfiles != files_verified; files_verified++) M if (verify_file(from, to)) M break; M M printf("tapecv: Tape verified\n"); M M return; M} XXX_EOF_REALLY sed 's/M//' << XXX_EOF_REALLY > verify_file.c M/* M * verify file M * M * Author: David L. Markowitz (dav@gtc.com) M */ M M#include "tapecv.h" M Mverify_file(from, to) Mstruct td from, to; M{ M int real_block; M M records_verified = 0; M if (boot_tape && files_verified != 0) { M /* Assume a boot tape's special format (Argh!) */ M real_block = max_block; M max_block = 512; M if (verify_record(from, to)) { M max_block = real_block; M goto eof; M } M max_block = real_block; M records_verified++; M } M M for (; ; records_verified++) M if (verify_record(from, to)) M break; M M if ((verbose > 2) && records_verified > 0) M printf("tapecv: Verified file %d: %d records\n", M files_verified+1, records_verified); M Meof: M if (records_verified > 0) M return(0); /* Not EOF */ M else M return(1); /* Empty file ::= EOT */ M} XXX_EOF_REALLY sed 's/M//' << XXX_EOF_REALLY > verify_record.c M/* M * verify record M * M * Author: David L. Markowitz (dav@gtc.com) M */ M M#include "tapecv.h" M#include M Mverify_record(from, to) Mstruct td from, to; M{ M int i; M unsigned long bytes_from = 0; M unsigned long bytes_to = 0; M char *cp; M M bytes_from = read_block(from); M bcopy(response, data_from, bytes_from); M M bytes_to = read_block(to); M bcopy(response, data_to, bytes_to); M M if (to.cartridge && ((bytes_from % 512) != 0)) { M cp = data_from + bytes_from; M while ((bytes_from%512) != 0) { M *cp++ = 0; M bytes_from++; M } M } M M if (bytes_from != bytes_to) { M fprintf(stderr, "tapecv: block sizes didn't match\n"); M fprintf(stderr, "%d on tape read, %d on tape written\n", M bytes_from, bytes_to); M exit(-1); M } M M if (bcmp(data_from, data_to, bytes_from)) { M fprintf(stderr, M "tapecv: data doesn't match on file %d, record %d\n", M files_verified+1, records_verified+1); M M i = open("from.data", O_CREAT|O_WRONLY|O_TRUNC, 0666); M write(i, data_from, bytes_from); M close(i); M M i = open("to.data", O_CREAT|O_WRONLY|O_TRUNC, 0666); M write(i, data_to, bytes_from); M close(i); M M fprintf(stderr, "tapecv: Records saved in from.data and to.data\n"); M exit(-1); M } M M if ((verbose > 3) && bytes_from > 0) M printf("tapecv: Verified a %d byte record\n", bytes_from); M M if (bytes_from == 0) M return(1); /* EOF */ M else M return(0); M} XXX_EOF_REALLY