diff options
Diffstat (limited to 'usr.bin/tcopy/tcopy.c')
-rw-r--r-- | usr.bin/tcopy/tcopy.c | 337 |
1 files changed, 337 insertions, 0 deletions
diff --git a/usr.bin/tcopy/tcopy.c b/usr.bin/tcopy/tcopy.c new file mode 100644 index 0000000..4fa0a33 --- /dev/null +++ b/usr.bin/tcopy/tcopy.c @@ -0,0 +1,337 @@ +/* + * Copyright (c) 1985, 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> + +__FBSDID("$FreeBSD$"); + +#ifndef lint +static const char copyright[] = +"@(#) Copyright (c) 1985, 1987, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif + +#ifndef lint +static const char sccsid[] = "@(#)tcopy.c 8.2 (Berkeley) 4/17/94"; +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/mtio.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <paths.h> +#include <signal.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define MAXREC (64 * 1024) +#define NOCOUNT (-2) + +static int filen, guesslen, maxblk = MAXREC; +static uint64_t lastrec, record, size, tsize; +static FILE *msg; + +static void *getspace(int); +static void intr(int); +static void usage(void) __dead2; +static void verify(int, int, char *); +static void writeop(int, int); +static void rewind_tape(int); + +int +main(int argc, char *argv[]) +{ + int lastnread, nread, nw, inp, outp; + enum {READ, VERIFY, COPY, COPYVERIFY} op = READ; + sig_t oldsig; + int ch, needeof; + char *buff; + const char *inf; + + msg = stdout; + guesslen = 1; + outp = -1; + while ((ch = getopt(argc, argv, "cs:vx")) != -1) + switch((char)ch) { + case 'c': + op = COPYVERIFY; + break; + case 's': + maxblk = atoi(optarg); + if (maxblk <= 0) { + warnx("illegal block size"); + usage(); + } + guesslen = 0; + break; + case 'v': + op = VERIFY; + break; + case 'x': + msg = stderr; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + switch(argc) { + case 0: + if (op != READ) + usage(); + inf = _PATH_DEFTAPE; + break; + case 1: + if (op != READ) + usage(); + inf = argv[0]; + break; + case 2: + if (op == READ) + op = COPY; + inf = argv[0]; + if ((outp = open(argv[1], op == VERIFY ? O_RDONLY : + op == COPY ? O_WRONLY : O_RDWR, DEFFILEMODE)) < 0) + err(3, "%s", argv[1]); + break; + default: + usage(); + } + + if ((inp = open(inf, O_RDONLY, 0)) < 0) + err(1, "%s", inf); + + buff = getspace(maxblk); + + if (op == VERIFY) { + verify(inp, outp, buff); + exit(0); + } + + if ((oldsig = signal(SIGINT, SIG_IGN)) != SIG_IGN) + (void) signal(SIGINT, intr); + + needeof = 0; + for (lastnread = NOCOUNT;;) { + if ((nread = read(inp, buff, maxblk)) == -1) { + while (errno == EINVAL && (maxblk -= 1024)) { + nread = read(inp, buff, maxblk); + if (nread >= 0) + goto r1; + } + err(1, "read error, file %d, record %ju", filen, (intmax_t)record); + } else if (nread != lastnread) { + if (lastnread != 0 && lastnread != NOCOUNT) { + if (lastrec == 0 && nread == 0) + fprintf(msg, "%ju records\n", (intmax_t)record); + else if (record - lastrec > 1) + fprintf(msg, "records %ju to %ju\n", + (intmax_t)lastrec, (intmax_t)record); + else + fprintf(msg, "record %ju\n", (intmax_t)lastrec); + } + if (nread != 0) + fprintf(msg, "file %d: block size %d: ", + filen, nread); + (void) fflush(stdout); + lastrec = record; + } +r1: guesslen = 0; + if (nread > 0) { + if (op == COPY || op == COPYVERIFY) { + if (needeof) { + writeop(outp, MTWEOF); + needeof = 0; + } + nw = write(outp, buff, nread); + if (nw != nread) { + if (nw == -1) { + warn("write error, file %d, record %ju", filen, + (intmax_t)record); + } else { + warnx("write error, file %d, record %ju", filen, + (intmax_t)record); + warnx("write (%d) != read (%d)", nw, nread); + } + errx(5, "copy aborted"); + } + } + size += nread; + record++; + } else { + if (lastnread <= 0 && lastnread != NOCOUNT) { + fprintf(msg, "eot\n"); + break; + } + fprintf(msg, + "file %d: eof after %ju records: %ju bytes\n", + filen, (intmax_t)record, (intmax_t)size); + needeof = 1; + filen++; + tsize += size; + size = record = lastrec = 0; + lastnread = 0; + } + lastnread = nread; + } + fprintf(msg, "total length: %ju bytes\n", (intmax_t)tsize); + (void)signal(SIGINT, oldsig); + if (op == COPY || op == COPYVERIFY) { + writeop(outp, MTWEOF); + writeop(outp, MTWEOF); + if (op == COPYVERIFY) { + rewind_tape(outp); + rewind_tape(inp); + verify(inp, outp, buff); + } + } + exit(0); +} + +static void +verify(int inp, int outp, char *outb) +{ + int eot, inmaxblk, inn, outmaxblk, outn; + char *inb; + + inb = getspace(maxblk); + inmaxblk = outmaxblk = maxblk; + for (eot = 0;; guesslen = 0) { + if ((inn = read(inp, inb, inmaxblk)) == -1) { + if (guesslen) + while (errno == EINVAL && (inmaxblk -= 1024)) { + inn = read(inp, inb, inmaxblk); + if (inn >= 0) + goto r1; + } + warn("read error"); + break; + } +r1: if ((outn = read(outp, outb, outmaxblk)) == -1) { + if (guesslen) + while (errno == EINVAL && (outmaxblk -= 1024)) { + outn = read(outp, outb, outmaxblk); + if (outn >= 0) + goto r2; + } + warn("read error"); + break; + } +r2: if (inn != outn) { + fprintf(msg, + "%s: tapes have different block sizes; %d != %d.\n", + "tcopy", inn, outn); + break; + } + if (!inn) { + if (eot++) { + fprintf(msg, "tcopy: tapes are identical.\n"); + return; + } + } else { + if (bcmp(inb, outb, inn)) { + fprintf(msg, + "tcopy: tapes have different data.\n"); + break; + } + eot = 0; + } + } + exit(1); +} + +static void +intr(int signo __unused) +{ + if (record) { + if (record - lastrec > 1) + fprintf(msg, "records %ju to %ju\n", (intmax_t)lastrec, (intmax_t)record); + else + fprintf(msg, "record %ju\n", (intmax_t)lastrec); + } + fprintf(msg, "interrupt at file %d: record %ju\n", filen, (intmax_t)record); + fprintf(msg, "total length: %ju bytes\n", (uintmax_t)(tsize + size)); + exit(1); +} + +static void * +getspace(int blk) +{ + void *bp; + + if ((bp = malloc((size_t)blk)) == NULL) + errx(11, "no memory"); + return (bp); +} + +static void +writeop(int fd, int type) +{ + struct mtop op; + + op.mt_op = type; + op.mt_count = (daddr_t)1; + if (ioctl(fd, MTIOCTOP, (char *)&op) < 0) + err(6, "tape op"); +} + +static void +usage(void) +{ + fprintf(stderr, "usage: tcopy [-cvx] [-s maxblk] [src [dest]]\n"); + exit(1); +} + +static void +rewind_tape(int fd) +{ + struct stat sp; + + if(fstat(fd, &sp)) + errx(12, "fstat in rewind"); + + /* + * don't want to do tape ioctl on regular files: + */ + if( S_ISREG(sp.st_mode) ) { + if( lseek(fd, 0, SEEK_SET) == -1 ) + errx(13, "lseek"); + } else + /* assume its a tape */ + writeop(fd, MTREW); +} |