xref: /freebsd/usr.bin/tcopy/tcopy.c (revision b3e7694832e81d7a904a10f525f8797b753bf0d3)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1985, 1987, 1993
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 
34 __FBSDID("$FreeBSD$");
35 
36 #ifndef lint
37 static const char copyright[] =
38 "@(#) Copyright (c) 1985, 1987, 1993\n\
39 	The Regents of the University of California.  All rights reserved.\n";
40 #endif
41 
42 #ifndef lint
43 static const char sccsid[] = "@(#)tcopy.c	8.2 (Berkeley) 4/17/94";
44 #endif
45 
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #include <sys/ioctl.h>
49 #include <sys/mtio.h>
50 
51 #include <err.h>
52 #include <errno.h>
53 #include <fcntl.h>
54 #include <paths.h>
55 #include <sys/sysctl.h>
56 #include <signal.h>
57 #include <stdint.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <unistd.h>
62 
63 #define	MAXREC	(64 * 1024)
64 #define	NOCOUNT	(-2)
65 
66 static int	filen, guesslen, maxblk = MAXREC;
67 static uint64_t	lastrec, record, size, tsize;
68 static FILE	*msg;
69 
70 static void	*getspace(int);
71 static void	 intr(int);
72 static void	 usage(void) __dead2;
73 static void	 verify(int, int, char *);
74 static void	 writeop(int, int);
75 static void	 rewind_tape(int);
76 
77 int
78 main(int argc, char *argv[])
79 {
80 	int lastnread, nread, nw, inp, outp;
81 	enum {READ, VERIFY, COPY, COPYVERIFY} op = READ;
82 	sig_t oldsig;
83 	int ch, needeof;
84 	char *buff;
85 	const char *inf;
86 	unsigned long maxphys = 0;
87 	size_t l_maxphys = sizeof maxphys;
88 
89 	if (!sysctlbyname("kern.maxphys", &maxphys, &l_maxphys, NULL, 0))
90 		maxblk = maxphys;
91 
92 	msg = stdout;
93 	guesslen = 1;
94 	outp = -1;
95 	while ((ch = getopt(argc, argv, "cs:vx")) != -1)
96 		switch((char)ch) {
97 		case 'c':
98 			op = COPYVERIFY;
99 			break;
100 		case 's':
101 			maxblk = atoi(optarg);
102 			if (maxblk <= 0) {
103 				warnx("illegal block size");
104 				usage();
105 			}
106 			guesslen = 0;
107 			break;
108 		case 'v':
109 			op = VERIFY;
110 			break;
111 		case 'x':
112 			msg = stderr;
113 			break;
114 		case '?':
115 		default:
116 			usage();
117 		}
118 	argc -= optind;
119 	argv += optind;
120 
121 	switch(argc) {
122 	case 0:
123 		if (op != READ)
124 			usage();
125 		inf = _PATH_DEFTAPE;
126 		break;
127 	case 1:
128 		if (op != READ)
129 			usage();
130 		inf = argv[0];
131 		break;
132 	case 2:
133 		if (op == READ)
134 			op = COPY;
135 		inf = argv[0];
136 		if ((outp = open(argv[1], op == VERIFY ? O_RDONLY :
137 		    op == COPY ? O_WRONLY : O_RDWR, DEFFILEMODE)) < 0)
138 			err(3, "%s", argv[1]);
139 		break;
140 	default:
141 		usage();
142 	}
143 
144 	if ((inp = open(inf, O_RDONLY, 0)) < 0)
145 		err(1, "%s", inf);
146 
147 	buff = getspace(maxblk);
148 
149 	if (op == VERIFY) {
150 		verify(inp, outp, buff);
151 		exit(0);
152 	}
153 
154 	if ((oldsig = signal(SIGINT, SIG_IGN)) != SIG_IGN)
155 		(void) signal(SIGINT, intr);
156 
157 	needeof = 0;
158 	for (lastnread = NOCOUNT;;) {
159 		if ((nread = read(inp, buff, maxblk)) == -1) {
160 			while (errno == EINVAL && (maxblk -= 1024)) {
161 				nread = read(inp, buff, maxblk);
162 				if (nread >= 0)
163 					goto r1;
164 			}
165 			err(1, "read error, file %d, record %ju", filen, (intmax_t)record);
166 		} else if (nread != lastnread) {
167 			if (lastnread != 0 && lastnread != NOCOUNT) {
168 				if (lastrec == 0 && nread == 0)
169 					fprintf(msg, "%ju records\n", (intmax_t)record);
170 				else if (record - lastrec > 1)
171 					fprintf(msg, "records %ju to %ju\n",
172 					    (intmax_t)lastrec, (intmax_t)record);
173 				else
174 					fprintf(msg, "record %ju\n", (intmax_t)lastrec);
175 			}
176 			if (nread != 0)
177 				fprintf(msg, "file %d: block size %d: ",
178 				    filen, 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 					if (nw == -1) {
192 						warn("write error, file %d, record %ju", filen,
193 						    (intmax_t)record);
194 					} else {
195 						warnx("write error, file %d, record %ju", filen,
196 						    (intmax_t)record);
197 						warnx("write (%d) != read (%d)", nw, nread);
198 					}
199 					errx(5, "copy aborted");
200 				}
201 			}
202 			size += nread;
203 			record++;
204 		} else {
205 			if (lastnread <= 0 && lastnread != NOCOUNT) {
206 				fprintf(msg, "eot\n");
207 				break;
208 			}
209 			fprintf(msg,
210 			    "file %d: eof after %ju records: %ju bytes\n",
211 			    filen, (intmax_t)record, (intmax_t)size);
212 			needeof = 1;
213 			filen++;
214 			tsize += size;
215 			size = record = lastrec = 0;
216 			lastnread = 0;
217 		}
218 		lastnread = nread;
219 	}
220 	fprintf(msg, "total length: %ju bytes\n", (intmax_t)tsize);
221 	(void)signal(SIGINT, oldsig);
222 	if (op == COPY || op == COPYVERIFY) {
223 		writeop(outp, MTWEOF);
224 		writeop(outp, MTWEOF);
225 		if (op == COPYVERIFY) {
226 			rewind_tape(outp);
227 			rewind_tape(inp);
228 			verify(inp, outp, buff);
229 		}
230 	}
231 	exit(0);
232 }
233 
234 static void
235 verify(int inp, int outp, char *outb)
236 {
237 	int eot, inmaxblk, inn, outmaxblk, outn;
238 	char *inb;
239 
240 	inb = getspace(maxblk);
241 	inmaxblk = outmaxblk = maxblk;
242 	for (eot = 0;; guesslen = 0) {
243 		if ((inn = read(inp, inb, inmaxblk)) == -1) {
244 			if (guesslen)
245 				while (errno == EINVAL && (inmaxblk -= 1024)) {
246 					inn = read(inp, inb, inmaxblk);
247 					if (inn >= 0)
248 						goto r1;
249 				}
250 			warn("read error");
251 			break;
252 		}
253 r1:		if ((outn = read(outp, outb, outmaxblk)) == -1) {
254 			if (guesslen)
255 				while (errno == EINVAL && (outmaxblk -= 1024)) {
256 					outn = read(outp, outb, outmaxblk);
257 					if (outn >= 0)
258 						goto r2;
259 				}
260 			warn("read error");
261 			break;
262 		}
263 r2:		if (inn != outn) {
264 			fprintf(msg,
265 			    "%s: tapes have different block sizes; %d != %d.\n",
266 			    "tcopy", inn, outn);
267 			break;
268 		}
269 		if (!inn) {
270 			if (eot++) {
271 				fprintf(msg, "tcopy: tapes are identical.\n");
272 				free(inb);
273 				return;
274 			}
275 		} else {
276 			if (bcmp(inb, outb, inn)) {
277 				fprintf(msg,
278 				    "tcopy: tapes have different data.\n");
279 				break;
280 			}
281 			eot = 0;
282 		}
283 	}
284 	exit(1);
285 }
286 
287 static void
288 intr(int signo __unused)
289 {
290 	if (record) {
291 		if (record - lastrec > 1)
292 			fprintf(msg, "records %ju to %ju\n", (intmax_t)lastrec, (intmax_t)record);
293 		else
294 			fprintf(msg, "record %ju\n", (intmax_t)lastrec);
295 	}
296 	fprintf(msg, "interrupt at file %d: record %ju\n", filen, (intmax_t)record);
297 	fprintf(msg, "total length: %ju bytes\n", (uintmax_t)(tsize + size));
298 	exit(1);
299 }
300 
301 static void *
302 getspace(int blk)
303 {
304 	void *bp;
305 
306 	if ((bp = malloc((size_t)blk)) == NULL)
307 		errx(11, "no memory");
308 	return (bp);
309 }
310 
311 static void
312 writeop(int fd, int type)
313 {
314 	struct mtop op;
315 
316 	op.mt_op = type;
317 	op.mt_count = (daddr_t)1;
318 	if (ioctl(fd, MTIOCTOP, (char *)&op) < 0)
319 		err(6, "tape op");
320 }
321 
322 static void
323 usage(void)
324 {
325 	fprintf(stderr, "usage: tcopy [-cvx] [-s maxblk] [src [dest]]\n");
326 	exit(1);
327 }
328 
329 static void
330 rewind_tape(int fd)
331 {
332 	struct stat sp;
333 
334 	if(fstat(fd, &sp))
335 		errx(12, "fstat in rewind");
336 
337 	/*
338 	 * don't want to do tape ioctl on regular files:
339 	 */
340 	if( S_ISREG(sp.st_mode) ) {
341 		if( lseek(fd, 0, SEEK_SET) == -1 )
342 			errx(13, "lseek");
343 	} else
344 		/*  assume its a tape	*/
345 		writeop(fd, MTREW);
346 }
347