xref: /freebsd/usr.bin/tcopy/tcopy.c (revision 9bd497b8354567454e075076d40c996e21bd6095)
1 /*
2  * Copyright (c) 1985, 1987, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #include <sys/cdefs.h>
35 
36 __FBSDID("$FreeBSD$");
37 
38 #ifndef lint
39 static const char copyright[] =
40 "@(#) Copyright (c) 1985, 1987, 1993\n\
41 	The Regents of the University of California.  All rights reserved.\n";
42 #endif
43 
44 #ifndef lint
45 static const char sccsid[] = "@(#)tcopy.c	8.2 (Berkeley) 4/17/94";
46 #endif
47 
48 #include <sys/types.h>
49 #include <sys/stat.h>
50 #include <sys/ioctl.h>
51 #include <sys/mtio.h>
52 
53 #include <err.h>
54 #include <errno.h>
55 #include <fcntl.h>
56 #include <paths.h>
57 #include <signal.h>
58 #include <stdint.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 u_int64_t	lastrec, record, size, tsize;
69 FILE	*msg;
70 
71 void	*getspace(int);
72 void	 intr(int);
73 static void	 usage(void) __dead2;
74 void	 verify(int, int, char *);
75 void	 writeop(int, int);
76 void	rewind_tape(int);
77 
78 int
79 main(int argc, char *argv[])
80 {
81 	int lastnread, nread, nw, inp, outp;
82 	enum {READ, VERIFY, COPY, COPYVERIFY} op = READ;
83 	sig_t oldsig;
84 	int ch, needeof;
85 	char *buff;
86 	const char *inf;
87 
88 	msg = stdout;
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 		break;
135 	default:
136 		usage();
137 	}
138 
139 	if ((inp = open(inf, O_RDONLY, 0)) < 0)
140 		err(1, "%s", inf);
141 
142 	buff = getspace(maxblk);
143 
144 	if (op == VERIFY) {
145 		verify(inp, outp, buff);
146 		exit(0);
147 	}
148 
149 	if ((oldsig = signal(SIGINT, SIG_IGN)) != SIG_IGN)
150 		(void) signal(SIGINT, intr);
151 
152 	needeof = 0;
153 	for (lastnread = NOCOUNT;;) {
154 		if ((nread = read(inp, buff, maxblk)) == -1) {
155 			while (errno == EINVAL && (maxblk -= 1024)) {
156 				nread = read(inp, buff, maxblk);
157 				if (nread >= 0)
158 					goto r1;
159 			}
160 			err(1, "read error, file %d, record %qu", filen, record);
161 		} else if (nread != lastnread) {
162 			if (lastnread != 0 && lastnread != NOCOUNT) {
163 				if (lastrec == 0 && nread == 0)
164 					fprintf(msg, "%qu records\n", record);
165 				else if (record - lastrec > 1)
166 					fprintf(msg, "records %qu to %qu\n",
167 					    lastrec, record);
168 				else
169 					fprintf(msg, "record %qu\n", lastrec);
170 			}
171 			if (nread != 0)
172 				fprintf(msg, "file %d: block size %d: ",
173 				    filen, nread);
174 			(void) fflush(stdout);
175 			lastrec = record;
176 		}
177 r1:		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 					if (nw == -1) {
187 					warn("write error, file %d, record %qu", filen, record);
188 					} else {
189 					warnx("write error, file %d, record %qu", filen, record);
190 					warnx("write (%d) != read (%d)", nw, nread);
191 					}
192 					errx(5, "copy aborted");
193 				}
194 			}
195 			size += nread;
196 			record++;
197 		} else {
198 			if (lastnread <= 0 && lastnread != NOCOUNT) {
199 				fprintf(msg, "eot\n");
200 				break;
201 			}
202 			fprintf(msg,
203 			    "file %d: eof after %qu records: %qu bytes\n",
204 			    filen, record, size);
205 			needeof = 1;
206 			filen++;
207 			tsize += size;
208 			size = record = lastrec = 0;
209 			lastnread = 0;
210 		}
211 		lastnread = nread;
212 	}
213 	fprintf(msg, "total length: %qu bytes\n", tsize);
214 	(void)signal(SIGINT, oldsig);
215 	if (op == COPY || op == COPYVERIFY) {
216 		writeop(outp, MTWEOF);
217 		writeop(outp, MTWEOF);
218 		if (op == COPYVERIFY) {
219 			rewind_tape(outp);
220 			rewind_tape(inp);
221 			verify(inp, outp, buff);
222 		}
223 	}
224 	exit(0);
225 }
226 
227 void
228 verify(int inp, int outp, char *outb)
229 {
230 	int eot, inmaxblk, inn, outmaxblk, outn;
231 	char *inb;
232 
233 	inb = getspace(maxblk);
234 	inmaxblk = outmaxblk = maxblk;
235 	for (eot = 0;; guesslen = 0) {
236 		if ((inn = read(inp, inb, inmaxblk)) == -1) {
237 			if (guesslen)
238 				while (errno == EINVAL && (inmaxblk -= 1024)) {
239 					inn = read(inp, inb, inmaxblk);
240 					if (inn >= 0)
241 						goto r1;
242 				}
243 			warn("read error");
244 			break;
245 		}
246 r1:		if ((outn = read(outp, outb, outmaxblk)) == -1) {
247 			if (guesslen)
248 				while (errno == EINVAL && (outmaxblk -= 1024)) {
249 					outn = read(outp, outb, outmaxblk);
250 					if (outn >= 0)
251 						goto r2;
252 				}
253 			warn("read error");
254 			break;
255 		}
256 r2:		if (inn != outn) {
257 			fprintf(msg,
258 			    "%s: tapes have different block sizes; %d != %d.\n",
259 			    "tcopy", inn, outn);
260 			break;
261 		}
262 		if (!inn) {
263 			if (eot++) {
264 				fprintf(msg, "tcopy: tapes are identical.\n");
265 				return;
266 			}
267 		} else {
268 			if (bcmp(inb, outb, inn)) {
269 				fprintf(msg,
270 				    "tcopy: tapes have different data.\n");
271 				break;
272 			}
273 			eot = 0;
274 		}
275 	}
276 	exit(1);
277 }
278 
279 void
280 intr(int signo __unused)
281 {
282 	if (record) {
283 		if (record - lastrec > 1)
284 			fprintf(msg, "records %qu to %qu\n", lastrec, record);
285 		else
286 			fprintf(msg, "record %qu\n", lastrec);
287 	}
288 	fprintf(msg, "interrupt at file %d: record %qu\n", filen, record);
289 	fprintf(msg, "total length: %ju bytes\n", (uintmax_t)(tsize + size));
290 	exit(1);
291 }
292 
293 void *
294 getspace(int blk)
295 {
296 	void *bp;
297 
298 	if ((bp = malloc((size_t)blk)) == NULL)
299 		errx(11, "no memory");
300 	return (bp);
301 }
302 
303 void
304 writeop(int fd, int type)
305 {
306 	struct mtop op;
307 
308 	op.mt_op = type;
309 	op.mt_count = (daddr_t)1;
310 	if (ioctl(fd, MTIOCTOP, (char *)&op) < 0)
311 		err(6, "tape op");
312 }
313 
314 static void
315 usage(void)
316 {
317 	fprintf(stderr, "usage: tcopy [-cvx] [-s maxblk] [src [dest]]\n");
318 	exit(1);
319 }
320 
321 void
322 rewind_tape(int fd)
323 {
324 	struct stat sp;
325 
326 	if(fstat(fd, &sp))
327 		errx(12, "fstat in rewind");
328 
329 	/*
330 	 * don't want to do tape ioctl on regular files:
331 	 */
332 	if( S_ISREG(sp.st_mode) ) {
333 		if( lseek(fd, 0, SEEK_SET) == -1 )
334 			errx(13, "lseek");
335 	} else
336 		/*  assume its a tape	*/
337 		writeop(fd, MTREW);
338 }
339