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