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