xref: /netbsd-src/usr.bin/tcopy/tcopy.c (revision b5677b36047b601b9addaaa494a58ceae82c2a6c)
1 /*	$NetBSD: tcopy.c,v 1.15 2008/07/21 14:19:26 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
40 static char sccsid[] = "@(#)tcopy.c	8.3 (Berkeley) 1/23/95";
41 #endif
42 __RCSID("$NetBSD: tcopy.c,v 1.15 2008/07/21 14:19:26 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 
64 int	filen, guesslen, maxblk = MAXREC;
65 long	lastrec, record;
66 off_t	size, tsize;
67 FILE	*msg = stdout;
68 
69 void	*getspace __P((int));
70 void	 intr __P((int));
71 int	 main __P((int, char **));
72 void	 usage __P((void));
73 void	 verify __P((int, int, char *));
74 void	 writeop __P((int, int));
75 
76 int
77 main(argc, argv)
78 	int argc;
79 	char *argv[];
80 {
81 	int ch, needeof, nw, inp, outp;
82 	ssize_t lastnread, nread;
83 	enum {READ, VERIFY, COPY, COPYVERIFY} op = READ;
84 	sig_t oldsig;
85 	char *buff, *inf;
86 
87 	outp = 0;
88 	inf = NULL;
89 	guesslen = 1;
90 	while ((ch = getopt(argc, argv, "cs:vx")) != -1)
91 		switch((char)ch) {
92 		case 'c':
93 			op = COPYVERIFY;
94 			break;
95 		case 's':
96 			maxblk = atoi(optarg);
97 			if (maxblk <= 0) {
98 				warnx("illegal block size");
99 				usage();
100 			}
101 			guesslen = 0;
102 			break;
103 		case 'v':
104 			op = VERIFY;
105 			break;
106 		case 'x':
107 			msg = stderr;
108 			break;
109 		case '?':
110 		default:
111 			usage();
112 		}
113 	argc -= optind;
114 	argv += optind;
115 
116 	switch(argc) {
117 	case 0:
118 		if (op != READ)
119 			usage();
120 		inf = _PATH_DEFTAPE;
121 		break;
122 	case 1:
123 		if (op != READ)
124 			usage();
125 		inf = argv[0];
126 		break;
127 	case 2:
128 		if (op == READ)
129 			op = COPY;
130 		inf = argv[0];
131 		if ((outp = open(argv[1], op == VERIFY ? O_RDONLY :
132 		    op == COPY ? O_WRONLY : O_RDWR, DEFFILEMODE)) < 0) {
133 			err(3, "%s", argv[1]);
134 		}
135 		break;
136 	default:
137 		usage();
138 	}
139 
140 	if ((inp = open(inf, O_RDONLY, 0)) < 0)
141 		err(1, "%s", inf);
142 
143 	buff = getspace(maxblk);
144 
145 	if (op == VERIFY) {
146 		verify(inp, outp, buff);
147 		exit(0);
148 	}
149 
150 	if ((oldsig = signal(SIGINT, SIG_IGN)) != SIG_IGN)
151 		(void) signal(SIGINT, intr);
152 
153 	needeof = 0;
154 	for (lastnread = NOCOUNT;;) {
155 		if ((nread = read(inp, buff, maxblk)) == -1) {
156 			while (errno == EINVAL && (maxblk -= 1024)) {
157 				nread = read(inp, buff, maxblk);
158 				if (nread >= 0)
159 					goto r1;
160 			}
161 			err(1, "read error, file %d, record %ld",
162 			    filen, record);
163 		} else if (nread != lastnread) {
164 			if (lastnread != 0 && lastnread != NOCOUNT) {
165 				if (lastrec == 0 && nread == 0)
166 					fprintf(msg, "%ld records\n", record);
167 				else if (record - lastrec > 1)
168 					fprintf(msg, "records %ld to %ld\n",
169 					    lastrec, record);
170 				else
171 					fprintf(msg, "record %ld\n", lastrec);
172 			}
173 			if (nread != 0)
174 				fprintf(msg, "file %d: block size %ld: ",
175 				    filen, (long)nread);
176 			(void) fflush(stdout);
177 			lastrec = record;
178 		}
179 r1:		guesslen = 0;
180 		if (nread > 0) {
181 			if (op == COPY || op == COPYVERIFY) {
182 				if (needeof) {
183 					writeop(outp, MTWEOF);
184 					needeof = 0;
185 				}
186 				nw = write(outp, buff, nread);
187 				if (nw != nread) {
188 				    int error = errno;
189 				    fprintf(stderr,
190 					"write error, file %d, record %ld: ",
191 					filen, record);
192 				    if (nw == -1)
193 					fprintf(stderr,
194 						": %s", strerror(error));
195 				    else
196 					fprintf(stderr,
197 					    "write (%d) != read (%ld)\n",
198 					    nw, (long)nread);
199 				    fprintf(stderr, "copy aborted\n");
200 				    exit(5);
201 				}
202 			}
203 			size += nread;
204 			record++;
205 		} else {
206 			if (lastnread <= 0 && lastnread != NOCOUNT) {
207 				fprintf(msg, "eot\n");
208 				break;
209 			}
210 			fprintf(msg,
211 			    "file %d: eof after %ld records: %lld bytes\n",
212 			    filen, record, (long long)size);
213 			needeof = 1;
214 			filen++;
215 			tsize += size;
216 			size = record = lastrec = 0;
217 			lastnread = 0;
218 		}
219 		lastnread = nread;
220 	}
221 	fprintf(msg, "total length: %lld bytes\n", (long long)tsize);
222 	(void)signal(SIGINT, oldsig);
223 	if (op == COPY || op == COPYVERIFY) {
224 		writeop(outp, MTWEOF);
225 		writeop(outp, MTWEOF);
226 		if (op == COPYVERIFY) {
227 			writeop(outp, MTREW);
228 			writeop(inp, MTREW);
229 			verify(inp, outp, buff);
230 		}
231 	}
232 	exit(0);
233 }
234 
235 void
236 verify(inp, outp, outb)
237 	int inp, outp;
238 	char *outb;
239 {
240 	int eot, inmaxblk, inn, outmaxblk, outn;
241 	char *inb;
242 
243 	inb = getspace(maxblk);
244 	inmaxblk = outmaxblk = maxblk;
245 	for (eot = 0;; guesslen = 0) {
246 		if ((inn = read(inp, inb, inmaxblk)) == -1) {
247 			if (guesslen)
248 				while (errno == EINVAL && (inmaxblk -= 1024)) {
249 					inn = read(inp, inb, inmaxblk);
250 					if (inn >= 0)
251 						goto r1;
252 				}
253 			warn("read error");
254 			break;
255 		}
256 r1:		if ((outn = read(outp, outb, outmaxblk)) == -1) {
257 			if (guesslen)
258 				while (errno == EINVAL && (outmaxblk -= 1024)) {
259 					outn = read(outp, outb, outmaxblk);
260 					if (outn >= 0)
261 						goto r2;
262 				}
263 			warn("read error");
264 			break;
265 		}
266 r2:		if (inn != outn) {
267 			fprintf(msg,
268 			    "%s: tapes have different block sizes; %d != %d.\n",
269 			    "tcopy", inn, outn);
270 			break;
271 		}
272 		if (!inn) {
273 			if (eot++) {
274 				fprintf(msg, "%s: tapes are identical.\n",
275 					"tcopy");
276 				free(inb);
277 				return;
278 			}
279 		} else {
280 			if (memcmp(inb, outb, inn)) {
281 				fprintf(msg,
282 				    "%s: tapes have different data.\n",
283 					"tcopy");
284 				break;
285 			}
286 			eot = 0;
287 		}
288 	}
289 	free(inb);
290 	exit(1);
291 }
292 
293 void
294 intr(signo)
295 	int signo;
296 {
297 	if (record) {
298 		if (record - lastrec > 1)
299 			fprintf(msg, "records %ld to %ld\n", lastrec, record);
300 		else
301 			fprintf(msg, "record %ld\n", lastrec);
302 	}
303 	fprintf(msg, "interrupt at file %d: record %ld\n", filen, record);
304 	fprintf(msg, "total length: %lld bytes\n", (long long)(tsize + size));
305 	(void)raise_default_signal(signo);
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