11590Srgrimes/* 21590Srgrimes * Copyright (c) 1985, 1987, 1993 31590Srgrimes * The Regents of the University of California. All rights reserved. 41590Srgrimes * 51590Srgrimes * Redistribution and use in source and binary forms, with or without 61590Srgrimes * modification, are permitted provided that the following conditions 71590Srgrimes * are met: 81590Srgrimes * 1. Redistributions of source code must retain the above copyright 91590Srgrimes * notice, this list of conditions and the following disclaimer. 101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111590Srgrimes * notice, this list of conditions and the following disclaimer in the 121590Srgrimes * documentation and/or other materials provided with the distribution. 131590Srgrimes * 4. Neither the name of the University nor the names of its contributors 141590Srgrimes * may be used to endorse or promote products derived from this software 151590Srgrimes * without specific prior written permission. 161590Srgrimes * 171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271590Srgrimes * SUCH DAMAGE. 281590Srgrimes */ 291590Srgrimes 3087709Smarkm#include <sys/cdefs.h> 3187709Smarkm 3287709Smarkm__FBSDID("$FreeBSD$"); 3387709Smarkm 341590Srgrimes#ifndef lint 3528198Scharnierstatic const char copyright[] = 361590Srgrimes"@(#) Copyright (c) 1985, 1987, 1993\n\ 371590Srgrimes The Regents of the University of California. All rights reserved.\n"; 3887709Smarkm#endif 391590Srgrimes 401590Srgrimes#ifndef lint 4187709Smarkmstatic const char sccsid[] = "@(#)tcopy.c 8.2 (Berkeley) 4/17/94"; 4228198Scharnier#endif 431590Srgrimes 441590Srgrimes#include <sys/types.h> 451590Srgrimes#include <sys/stat.h> 461590Srgrimes#include <sys/ioctl.h> 471590Srgrimes#include <sys/mtio.h> 481590Srgrimes 4928198Scharnier#include <err.h> 501590Srgrimes#include <errno.h> 511590Srgrimes#include <fcntl.h> 5273986Sobrien#include <paths.h> 531590Srgrimes#include <signal.h> 5493123Smike#include <stdint.h> 551590Srgrimes#include <stdio.h> 561590Srgrimes#include <stdlib.h> 571590Srgrimes#include <string.h> 581590Srgrimes#include <unistd.h> 591590Srgrimes 601590Srgrimes#define MAXREC (64 * 1024) 611590Srgrimes#define NOCOUNT (-2) 621590Srgrimes 63227186Sedstatic int filen, guesslen, maxblk = MAXREC; 64227186Sedstatic uint64_t lastrec, record, size, tsize; 65227186Sedstatic FILE *msg; 661590Srgrimes 67227186Sedstatic void *getspace(int); 68227186Sedstatic void intr(int); 69201612Sdwmalonestatic void usage(void) __dead2; 70227186Sedstatic void verify(int, int, char *); 71227186Sedstatic void writeop(int, int); 72227186Sedstatic void rewind_tape(int); 731590Srgrimes 741590Srgrimesint 75201612Sdwmalonemain(int argc, char *argv[]) 761590Srgrimes{ 77201612Sdwmalone int lastnread, nread, nw, inp, outp; 781590Srgrimes enum {READ, VERIFY, COPY, COPYVERIFY} op = READ; 791590Srgrimes sig_t oldsig; 801590Srgrimes int ch, needeof; 8187709Smarkm char *buff; 8287709Smarkm const char *inf; 831590Srgrimes 8481607Speter msg = stdout; 851590Srgrimes guesslen = 1; 86209570Sgavin outp = -1; 8724360Simp while ((ch = getopt(argc, argv, "cs:vx")) != -1) 881590Srgrimes switch((char)ch) { 891590Srgrimes case 'c': 901590Srgrimes op = COPYVERIFY; 911590Srgrimes break; 921590Srgrimes case 's': 931590Srgrimes maxblk = atoi(optarg); 941590Srgrimes if (maxblk <= 0) { 9528198Scharnier warnx("illegal block size"); 961590Srgrimes usage(); 971590Srgrimes } 981590Srgrimes guesslen = 0; 991590Srgrimes break; 1001590Srgrimes case 'v': 1011590Srgrimes op = VERIFY; 1021590Srgrimes break; 1031590Srgrimes case 'x': 1041590Srgrimes msg = stderr; 1051590Srgrimes break; 1061590Srgrimes case '?': 1071590Srgrimes default: 1081590Srgrimes usage(); 1091590Srgrimes } 1101590Srgrimes argc -= optind; 1111590Srgrimes argv += optind; 1121590Srgrimes 1131590Srgrimes switch(argc) { 1141590Srgrimes case 0: 1151590Srgrimes if (op != READ) 1161590Srgrimes usage(); 1171590Srgrimes inf = _PATH_DEFTAPE; 1181590Srgrimes break; 1191590Srgrimes case 1: 1201590Srgrimes if (op != READ) 1211590Srgrimes usage(); 1221590Srgrimes inf = argv[0]; 1231590Srgrimes break; 1241590Srgrimes case 2: 1251590Srgrimes if (op == READ) 1261590Srgrimes op = COPY; 1271590Srgrimes inf = argv[0]; 1281590Srgrimes if ((outp = open(argv[1], op == VERIFY ? O_RDONLY : 12928198Scharnier op == COPY ? O_WRONLY : O_RDWR, DEFFILEMODE)) < 0) 13028198Scharnier err(3, "%s", argv[1]); 1311590Srgrimes break; 1321590Srgrimes default: 1331590Srgrimes usage(); 1341590Srgrimes } 1351590Srgrimes 13628198Scharnier if ((inp = open(inf, O_RDONLY, 0)) < 0) 13728198Scharnier err(1, "%s", inf); 1381590Srgrimes 1391590Srgrimes buff = getspace(maxblk); 1401590Srgrimes 1411590Srgrimes if (op == VERIFY) { 1421590Srgrimes verify(inp, outp, buff); 1431590Srgrimes exit(0); 1441590Srgrimes } 1451590Srgrimes 1461590Srgrimes if ((oldsig = signal(SIGINT, SIG_IGN)) != SIG_IGN) 1471590Srgrimes (void) signal(SIGINT, intr); 1481590Srgrimes 1491590Srgrimes needeof = 0; 1501590Srgrimes for (lastnread = NOCOUNT;;) { 1511590Srgrimes if ((nread = read(inp, buff, maxblk)) == -1) { 1521590Srgrimes while (errno == EINVAL && (maxblk -= 1024)) { 1531590Srgrimes nread = read(inp, buff, maxblk); 1541590Srgrimes if (nread >= 0) 1551590Srgrimes goto r1; 1561590Srgrimes } 157209570Sgavin err(1, "read error, file %d, record %ju", filen, (intmax_t)record); 1581590Srgrimes } else if (nread != lastnread) { 1591590Srgrimes if (lastnread != 0 && lastnread != NOCOUNT) { 1601590Srgrimes if (lastrec == 0 && nread == 0) 161209570Sgavin fprintf(msg, "%ju records\n", (intmax_t)record); 1621590Srgrimes else if (record - lastrec > 1) 163209570Sgavin fprintf(msg, "records %ju to %ju\n", 164209570Sgavin (intmax_t)lastrec, (intmax_t)record); 1651590Srgrimes else 166209570Sgavin fprintf(msg, "record %ju\n", (intmax_t)lastrec); 1671590Srgrimes } 1681590Srgrimes if (nread != 0) 1691590Srgrimes fprintf(msg, "file %d: block size %d: ", 1701590Srgrimes filen, nread); 1711590Srgrimes (void) fflush(stdout); 1721590Srgrimes lastrec = record; 1731590Srgrimes } 1741590Srgrimesr1: guesslen = 0; 1751590Srgrimes if (nread > 0) { 1761590Srgrimes if (op == COPY || op == COPYVERIFY) { 1771590Srgrimes if (needeof) { 1781590Srgrimes writeop(outp, MTWEOF); 1791590Srgrimes needeof = 0; 1801590Srgrimes } 1811590Srgrimes nw = write(outp, buff, nread); 1821590Srgrimes if (nw != nread) { 18328198Scharnier if (nw == -1) { 184209570Sgavin warn("write error, file %d, record %ju", filen, 185209570Sgavin (intmax_t)record); 18628198Scharnier } else { 187209570Sgavin warnx("write error, file %d, record %ju", filen, 188209570Sgavin (intmax_t)record); 189209570Sgavin warnx("write (%d) != read (%d)", nw, nread); 19028198Scharnier } 19128198Scharnier errx(5, "copy aborted"); 1921590Srgrimes } 1931590Srgrimes } 1941590Srgrimes size += nread; 1951590Srgrimes record++; 1961590Srgrimes } else { 1971590Srgrimes if (lastnread <= 0 && lastnread != NOCOUNT) { 1981590Srgrimes fprintf(msg, "eot\n"); 1991590Srgrimes break; 2001590Srgrimes } 2011590Srgrimes fprintf(msg, 202209570Sgavin "file %d: eof after %ju records: %ju bytes\n", 203209570Sgavin filen, (intmax_t)record, (intmax_t)size); 2041590Srgrimes needeof = 1; 2051590Srgrimes filen++; 2061590Srgrimes tsize += size; 2071590Srgrimes size = record = lastrec = 0; 2081590Srgrimes lastnread = 0; 2091590Srgrimes } 2101590Srgrimes lastnread = nread; 2111590Srgrimes } 212209570Sgavin fprintf(msg, "total length: %ju bytes\n", (intmax_t)tsize); 2131590Srgrimes (void)signal(SIGINT, oldsig); 2141590Srgrimes if (op == COPY || op == COPYVERIFY) { 2151590Srgrimes writeop(outp, MTWEOF); 2161590Srgrimes writeop(outp, MTWEOF); 2171590Srgrimes if (op == COPYVERIFY) { 21846203Sphk rewind_tape(outp); 21946203Sphk rewind_tape(inp); 2201590Srgrimes verify(inp, outp, buff); 2211590Srgrimes } 2221590Srgrimes } 2231590Srgrimes exit(0); 2241590Srgrimes} 2251590Srgrimes 226227186Sedstatic void 227201612Sdwmaloneverify(int inp, int outp, char *outb) 2281590Srgrimes{ 229201612Sdwmalone int eot, inmaxblk, inn, outmaxblk, outn; 230201612Sdwmalone char *inb; 2311590Srgrimes 2321590Srgrimes inb = getspace(maxblk); 2331590Srgrimes inmaxblk = outmaxblk = maxblk; 2341590Srgrimes for (eot = 0;; guesslen = 0) { 2351590Srgrimes if ((inn = read(inp, inb, inmaxblk)) == -1) { 2361590Srgrimes if (guesslen) 2371590Srgrimes while (errno == EINVAL && (inmaxblk -= 1024)) { 2381590Srgrimes inn = read(inp, inb, inmaxblk); 2391590Srgrimes if (inn >= 0) 2401590Srgrimes goto r1; 2411590Srgrimes } 24228198Scharnier warn("read error"); 2431590Srgrimes break; 2441590Srgrimes } 2451590Srgrimesr1: if ((outn = read(outp, outb, outmaxblk)) == -1) { 2461590Srgrimes if (guesslen) 2471590Srgrimes while (errno == EINVAL && (outmaxblk -= 1024)) { 2481590Srgrimes outn = read(outp, outb, outmaxblk); 2491590Srgrimes if (outn >= 0) 2501590Srgrimes goto r2; 2511590Srgrimes } 25228198Scharnier warn("read error"); 2531590Srgrimes break; 2541590Srgrimes } 2551590Srgrimesr2: if (inn != outn) { 2561590Srgrimes fprintf(msg, 2571590Srgrimes "%s: tapes have different block sizes; %d != %d.\n", 2581590Srgrimes "tcopy", inn, outn); 2591590Srgrimes break; 2601590Srgrimes } 2611590Srgrimes if (!inn) { 2621590Srgrimes if (eot++) { 2631590Srgrimes fprintf(msg, "tcopy: tapes are identical.\n"); 2641590Srgrimes return; 2651590Srgrimes } 2661590Srgrimes } else { 2671590Srgrimes if (bcmp(inb, outb, inn)) { 2681590Srgrimes fprintf(msg, 2691590Srgrimes "tcopy: tapes have different data.\n"); 2701590Srgrimes break; 2711590Srgrimes } 2721590Srgrimes eot = 0; 2731590Srgrimes } 2741590Srgrimes } 2751590Srgrimes exit(1); 2761590Srgrimes} 2771590Srgrimes 278227186Sedstatic void 279201612Sdwmaloneintr(int signo __unused) 2801590Srgrimes{ 28148566Sbillf if (record) { 2821590Srgrimes if (record - lastrec > 1) 283209570Sgavin fprintf(msg, "records %ju to %ju\n", (intmax_t)lastrec, (intmax_t)record); 2841590Srgrimes else 285209570Sgavin fprintf(msg, "record %ju\n", (intmax_t)lastrec); 28648566Sbillf } 287209570Sgavin fprintf(msg, "interrupt at file %d: record %ju\n", filen, (intmax_t)record); 28887763Smarkm fprintf(msg, "total length: %ju bytes\n", (uintmax_t)(tsize + size)); 2891590Srgrimes exit(1); 2901590Srgrimes} 2911590Srgrimes 292227186Sedstatic void * 293201612Sdwmalonegetspace(int blk) 2941590Srgrimes{ 2951590Srgrimes void *bp; 2961590Srgrimes 29728198Scharnier if ((bp = malloc((size_t)blk)) == NULL) 29828198Scharnier errx(11, "no memory"); 2991590Srgrimes return (bp); 3001590Srgrimes} 3011590Srgrimes 302227186Sedstatic void 303201612Sdwmalonewriteop(int fd, int type) 3041590Srgrimes{ 3051590Srgrimes struct mtop op; 3061590Srgrimes 3071590Srgrimes op.mt_op = type; 3081590Srgrimes op.mt_count = (daddr_t)1; 30928198Scharnier if (ioctl(fd, MTIOCTOP, (char *)&op) < 0) 31028198Scharnier err(6, "tape op"); 3111590Srgrimes} 3121590Srgrimes 31328198Scharnierstatic void 314201612Sdwmaloneusage(void) 3151590Srgrimes{ 31628198Scharnier fprintf(stderr, "usage: tcopy [-cvx] [-s maxblk] [src [dest]]\n"); 3171590Srgrimes exit(1); 3181590Srgrimes} 31946203Sphk 320227186Sedstatic void 32146203Sphkrewind_tape(int fd) 32246203Sphk{ 32346203Sphk struct stat sp; 32446203Sphk 32546203Sphk if(fstat(fd, &sp)) 32646203Sphk errx(12, "fstat in rewind"); 32746203Sphk 32846203Sphk /* 32946203Sphk * don't want to do tape ioctl on regular files: 33046203Sphk */ 33146203Sphk if( S_ISREG(sp.st_mode) ) { 33246203Sphk if( lseek(fd, 0, SEEK_SET) == -1 ) 33346203Sphk errx(13, "lseek"); 33446203Sphk } else 33546203Sphk /* assume its a tape */ 33646203Sphk writeop(fd, MTREW); 33746203Sphk} 338