1/*	$NetBSD: tcopy.c,v 1.16 2009/04/13 23:44:49 lukem Exp $	*/
2
3/*
4 * Copyright (c) 1985, 1987, 1993, 1995
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33#ifndef lint
34__COPYRIGHT("@(#) Copyright (c) 1985, 1987, 1993\
35 The Regents of the University of California.  All rights reserved.");
36#endif /* not lint */
37
38#ifndef lint
39#if 0
40static char sccsid[] = "@(#)tcopy.c	8.3 (Berkeley) 1/23/95";
41#endif
42__RCSID("$NetBSD: tcopy.c,v 1.16 2009/04/13 23:44:49 lukem Exp $");
43#endif /* not lint */
44
45#include <sys/types.h>
46#include <sys/stat.h>
47#include <sys/ioctl.h>
48#include <sys/mtio.h>
49
50#include <err.h>
51#include <errno.h>
52#include <paths.h>
53#include <fcntl.h>
54#include <signal.h>
55#include <stdio.h>
56#include <stdlib.h>
57#include <string.h>
58#include <unistd.h>
59#include <util.h>
60
61#define	MAXREC	(64 * 1024)
62#define	NOCOUNT	(-2)
63
64static int	filen, guesslen, maxblk = MAXREC;
65static long	lastrec, record;
66static off_t	size, tsize;
67static FILE	*msg = stdout;
68
69static void	*getspace(int);
70__dead static void	 intr(int);
71__dead static void	 usage(void);
72static void	 verify(int, int, char *);
73static void	 writeop(int, int);
74
75int
76main(int argc, char *argv[])
77{
78	int ch, needeof, nw, inp, outp;
79	ssize_t lastnread, nread;
80	enum {READ, VERIFY, COPY, COPYVERIFY} op = READ;
81	sig_t oldsig;
82	char *buff;
83	const char *inf;
84
85	outp = 0;
86	inf = NULL;
87	guesslen = 1;
88	while ((ch = getopt(argc, argv, "cs:vx")) != -1)
89		switch((char)ch) {
90		case 'c':
91			op = COPYVERIFY;
92			break;
93		case 's':
94			maxblk = atoi(optarg);
95			if (maxblk <= 0) {
96				warnx("illegal block size");
97				usage();
98			}
99			guesslen = 0;
100			break;
101		case 'v':
102			op = VERIFY;
103			break;
104		case 'x':
105			msg = stderr;
106			break;
107		case '?':
108		default:
109			usage();
110		}
111	argc -= optind;
112	argv += optind;
113
114	switch(argc) {
115	case 0:
116		if (op != READ)
117			usage();
118		inf = _PATH_DEFTAPE;
119		break;
120	case 1:
121		if (op != READ)
122			usage();
123		inf = argv[0];
124		break;
125	case 2:
126		if (op == READ)
127			op = COPY;
128		inf = argv[0];
129		if ((outp = open(argv[1], op == VERIFY ? O_RDONLY :
130		    op == COPY ? O_WRONLY : O_RDWR, DEFFILEMODE)) < 0) {
131			err(3, "%s", argv[1]);
132		}
133		break;
134	default:
135		usage();
136	}
137
138	if ((inp = open(inf, O_RDONLY, 0)) < 0)
139		err(1, "%s", inf);
140
141	buff = getspace(maxblk);
142
143	if (op == VERIFY) {
144		verify(inp, outp, buff);
145		exit(0);
146	}
147
148	if ((oldsig = signal(SIGINT, SIG_IGN)) != SIG_IGN)
149		(void) signal(SIGINT, intr);
150
151	needeof = 0;
152	for (lastnread = NOCOUNT;;) {
153		if ((nread = read(inp, buff, maxblk)) == -1) {
154			while (errno == EINVAL && (maxblk -= 1024)) {
155				nread = read(inp, buff, maxblk);
156				if (nread >= 0)
157					goto r1;
158			}
159			err(1, "read error, file %d, record %ld",
160			    filen, record);
161		} else if (nread != lastnread) {
162			if (lastnread != 0 && lastnread != NOCOUNT) {
163				if (lastrec == 0 && nread == 0)
164					fprintf(msg, "%ld records\n", record);
165				else if (record - lastrec > 1)
166					fprintf(msg, "records %ld to %ld\n",
167					    lastrec, record);
168				else
169					fprintf(msg, "record %ld\n", lastrec);
170			}
171			if (nread != 0)
172				fprintf(msg, "file %d: block size %ld: ",
173				    filen, (long)nread);
174			(void) fflush(stdout);
175			lastrec = record;
176		}
177r1:		guesslen = 0;
178		if (nread > 0) {
179			if (op == COPY || op == COPYVERIFY) {
180				if (needeof) {
181					writeop(outp, MTWEOF);
182					needeof = 0;
183				}
184				nw = write(outp, buff, nread);
185				if (nw != nread) {
186				    int error = errno;
187				    fprintf(stderr,
188					"write error, file %d, record %ld: ",
189					filen, record);
190				    if (nw == -1)
191					fprintf(stderr,
192						": %s", strerror(error));
193				    else
194					fprintf(stderr,
195					    "write (%d) != read (%ld)\n",
196					    nw, (long)nread);
197				    fprintf(stderr, "copy aborted\n");
198				    exit(5);
199				}
200			}
201			size += nread;
202			record++;
203		} else {
204			if (lastnread <= 0 && lastnread != NOCOUNT) {
205				fprintf(msg, "eot\n");
206				break;
207			}
208			fprintf(msg,
209			    "file %d: eof after %ld records: %lld bytes\n",
210			    filen, record, (long long)size);
211			needeof = 1;
212			filen++;
213			tsize += size;
214			size = record = lastrec = 0;
215			lastnread = 0;
216		}
217		lastnread = nread;
218	}
219	fprintf(msg, "total length: %lld bytes\n", (long long)tsize);
220	(void)signal(SIGINT, oldsig);
221	if (op == COPY || op == COPYVERIFY) {
222		writeop(outp, MTWEOF);
223		writeop(outp, MTWEOF);
224		if (op == COPYVERIFY) {
225			writeop(outp, MTREW);
226			writeop(inp, MTREW);
227			verify(inp, outp, buff);
228		}
229	}
230	exit(0);
231}
232
233static void
234verify(int inp, int outp, char *outb)
235{
236	int eot, inmaxblk, inn, outmaxblk, outn;
237	char *inb;
238
239	inb = getspace(maxblk);
240	inmaxblk = outmaxblk = maxblk;
241	for (eot = 0;; guesslen = 0) {
242		if ((inn = read(inp, inb, inmaxblk)) == -1) {
243			if (guesslen)
244				while (errno == EINVAL && (inmaxblk -= 1024)) {
245					inn = read(inp, inb, inmaxblk);
246					if (inn >= 0)
247						goto r1;
248				}
249			warn("read error");
250			break;
251		}
252r1:		if ((outn = read(outp, outb, outmaxblk)) == -1) {
253			if (guesslen)
254				while (errno == EINVAL && (outmaxblk -= 1024)) {
255					outn = read(outp, outb, outmaxblk);
256					if (outn >= 0)
257						goto r2;
258				}
259			warn("read error");
260			break;
261		}
262r2:		if (inn != outn) {
263			fprintf(msg,
264			    "%s: tapes have different block sizes; %d != %d.\n",
265			    "tcopy", inn, outn);
266			break;
267		}
268		if (!inn) {
269			if (eot++) {
270				fprintf(msg, "%s: tapes are identical.\n",
271					"tcopy");
272				free(inb);
273				return;
274			}
275		} else {
276			if (memcmp(inb, outb, inn)) {
277				fprintf(msg,
278				    "%s: tapes have different data.\n",
279					"tcopy");
280				break;
281			}
282			eot = 0;
283		}
284	}
285	free(inb);
286	exit(1);
287}
288
289static void
290intr(int signo)
291{
292	if (record) {
293		if (record - lastrec > 1)
294			fprintf(msg, "records %ld to %ld\n", lastrec, record);
295		else
296			fprintf(msg, "record %ld\n", lastrec);
297	}
298	fprintf(msg, "interrupt at file %d: record %ld\n", filen, record);
299	fprintf(msg, "total length: %lld bytes\n", (long long)(tsize + size));
300	(void)raise_default_signal(signo);
301	exit(1);
302}
303
304static void *
305getspace(int blk)
306{
307	void *bp;
308
309	if ((bp = malloc((size_t)blk)) == NULL)
310		errx(11, "no memory");
311
312	return (bp);
313}
314
315static void
316writeop(int fd, int type)
317{
318	struct mtop op;
319
320	op.mt_op = type;
321	op.mt_count = (daddr_t)1;
322	if (ioctl(fd, MTIOCTOP, (char *)&op) < 0)
323		err(6, "tape op");
324}
325
326static void
327usage(void)
328{
329
330	fprintf(stderr, "usage: tcopy [-cvx] [-s maxblk] src [dest]\n");
331	exit(1);
332}
333