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