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
631590Srgrimesint	filen, guesslen, maxblk = MAXREC;
6446203Sphku_int64_t	lastrec, record, size, tsize;
6581607SpeterFILE	*msg;
661590Srgrimes
6792922Simpvoid	*getspace(int);
6892922Simpvoid	 intr(int);
69201612Sdwmalonestatic void	 usage(void) __dead2;
7092922Simpvoid	 verify(int, int, char *);
7192922Simpvoid	 writeop(int, int);
7246203Sphkvoid	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
2261590Srgrimesvoid
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
2781590Srgrimesvoid
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
2921590Srgrimesvoid *
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
3021590Srgrimesvoid
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
32046203Sphkvoid
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