diff options
Diffstat (limited to 'usr.bin/tcopy')
-rw-r--r-- | usr.bin/tcopy/Makefile | 6 | ||||
-rw-r--r-- | usr.bin/tcopy/tcopy.1 | 125 | ||||
-rw-r--r-- | usr.bin/tcopy/tcopy.c | 337 |
3 files changed, 468 insertions, 0 deletions
diff --git a/usr.bin/tcopy/Makefile b/usr.bin/tcopy/Makefile new file mode 100644 index 0000000..1bae0b2 --- /dev/null +++ b/usr.bin/tcopy/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 6/6/93 +# $FreeBSD$ + +PROG= tcopy + +.include <bsd.prog.mk> diff --git a/usr.bin/tcopy/tcopy.1 b/usr.bin/tcopy/tcopy.1 new file mode 100644 index 0000000..a99f2e0 --- /dev/null +++ b/usr.bin/tcopy/tcopy.1 @@ -0,0 +1,125 @@ +.\" Copyright (c) 1985, 1990, 1991, 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. +.\" +.\" @(#)tcopy.1 8.2 (Berkeley) 4/17/94 +.\" $FreeBSD$ +.\" +.Dd December 20, 2006 +.Dt TCOPY 1 +.Os +.Sh NAME +.Nm tcopy +.Nd copy and/or verify mag tapes +.Sh SYNOPSIS +.Nm +.Op Fl cvx +.Op Fl s Ar maxblk +.Oo Ar src Op Ar dest +.Oc +.Sh DESCRIPTION +The +.Nm +utility is designed to copy magnetic tapes. +The only assumption made +about the tape layout is that there are two sequential EOF marks +at the end. +By default, the +.Nm +utility will print +information about the sizes of records and files found +on the +.Pa /dev/sa0 +tape, or on the tape specified by the +.Ar src +argument. +If a destination tape is also specified by the +.Ar dest +argument, a copy of the source tape will be made. +The blocking on the +destination tape will be identical to that used on the source tape. +Copying +a tape will yield the same program output as if just printing the sizes. +.Pp +The following options are available: +.Bl -tag -width ".Fl s Ar maxblk" +.It Fl c +Copy +.Ar src +to +.Ar dest +and then verify that the two tapes are identical. +.It Fl s Ar maxblk +Specify a maximum block size, +.Ar maxblk . +.It Fl v +Given the two tapes +.Ar src +and +.Ar dest , +verify that they are identical. +.It Fl x +Output all informational messages to the standard error +instead of the standard output. +This option is useful when +.Ar dest +is given as +.Pa /dev/stdout . +.El +.Sh SEE ALSO +.Xr mt 1 , +.Xr mtio 4 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.3 . +.Sh BUGS +.Bl -item +.It +Writing an image of a tape to a file does not preserve much more than +the raw data. +Block size(s) and tape EOF marks are lost which would +otherwise be preserved in a tape-to-tape copy. +.It +End of data (EOD) is determined by two sequential EOF marks +with no data between them. +There used to be old systems which typically wrote three EOF's between tape +files. +The +.Nm +utility will erroneously stop copying early in this case. +.It +When using the copy/verify option +.Fl c , +.Nm +does not rewind the tapes prior to start. +A rewind is performed +after writing, prior to the verification stage. +If one does not start +at the beginning-of-tape (BOT) then the comparison +may not be of the intended data. +.El 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); +} |