xref: /freebsd/usr.bin/tcopy/tcopy.c (revision 952d112864d8008aa87278a30a539d888a8493cd)
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 #ifndef lint
35 static char copyright[] =
36 "@(#) Copyright (c) 1985, 1987, 1993\n\
37 	The Regents of the University of California.  All rights reserved.\n";
38 #endif /* not lint */
39 
40 #ifndef lint
41 static char sccsid[] = "@(#)tcopy.c	8.2 (Berkeley) 4/17/94";
42 #endif /* not lint */
43 
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include <sys/ioctl.h>
47 #include <sys/mtio.h>
48 
49 #include <errno.h>
50 #include <fcntl.h>
51 #include <signal.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <unistd.h>
56 
57 #include "pathnames.h"
58 
59 #define	MAXREC	(64 * 1024)
60 #define	NOCOUNT	(-2)
61 
62 int	filen, guesslen, maxblk = MAXREC;
63 u_long	lastrec, record, size, tsize;
64 FILE	*msg = stdout;
65 
66 void	*getspace __P((int));
67 void	 intr __P((int));
68 void	 usage __P((void));
69 void	 verify __P((int, int, char *));
70 void	 writeop __P((int, int));
71 
72 int
73 main(argc, argv)
74 	int argc;
75 	char *argv[];
76 {
77 	register 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, *inf;
82 
83 	guesslen = 1;
84 	while ((ch = getopt(argc, argv, "cs:vx")) != -1)
85 		switch((char)ch) {
86 		case 'c':
87 			op = COPYVERIFY;
88 			break;
89 		case 's':
90 			maxblk = atoi(optarg);
91 			if (maxblk <= 0) {
92 				fprintf(stderr, "tcopy: illegal block size\n");
93 				usage();
94 			}
95 			guesslen = 0;
96 			break;
97 		case 'v':
98 			op = VERIFY;
99 			break;
100 		case 'x':
101 			msg = stderr;
102 			break;
103 		case '?':
104 		default:
105 			usage();
106 		}
107 	argc -= optind;
108 	argv += optind;
109 
110 	switch(argc) {
111 	case 0:
112 		if (op != READ)
113 			usage();
114 		inf = _PATH_DEFTAPE;
115 		break;
116 	case 1:
117 		if (op != READ)
118 			usage();
119 		inf = argv[0];
120 		break;
121 	case 2:
122 		if (op == READ)
123 			op = COPY;
124 		inf = argv[0];
125 		if ((outp = open(argv[1], op == VERIFY ? O_RDONLY :
126 		    op == COPY ? O_WRONLY : O_RDWR, DEFFILEMODE)) < 0) {
127 			perror(argv[1]);
128 			exit(3);
129 		}
130 		break;
131 	default:
132 		usage();
133 	}
134 
135 	if ((inp = open(inf, O_RDONLY, 0)) < 0) {
136 		perror(inf);
137 		exit(1);
138 	}
139 
140 	buff = getspace(maxblk);
141 
142 	if (op == VERIFY) {
143 		verify(inp, outp, buff);
144 		exit(0);
145 	}
146 
147 	if ((oldsig = signal(SIGINT, SIG_IGN)) != SIG_IGN)
148 		(void) signal(SIGINT, intr);
149 
150 	needeof = 0;
151 	for (lastnread = NOCOUNT;;) {
152 		if ((nread = read(inp, buff, maxblk)) == -1) {
153 			while (errno == EINVAL && (maxblk -= 1024)) {
154 				nread = read(inp, buff, maxblk);
155 				if (nread >= 0)
156 					goto r1;
157 			}
158 			fprintf(stderr, "read error, file %d, record %ld: ",
159 			    filen, record);
160 			perror("");
161 			exit(1);
162 		} else if (nread != lastnread) {
163 			if (lastnread != 0 && lastnread != NOCOUNT) {
164 				if (lastrec == 0 && nread == 0)
165 					fprintf(msg, "%ld records\n", record);
166 				else if (record - lastrec > 1)
167 					fprintf(msg, "records %ld to %ld\n",
168 					    lastrec, record);
169 				else
170 					fprintf(msg, "record %ld\n", lastrec);
171 			}
172 			if (nread != 0)
173 				fprintf(msg, "file %d: block size %d: ",
174 				    filen, nread);
175 			(void) fflush(stdout);
176 			lastrec = record;
177 		}
178 r1:		guesslen = 0;
179 		if (nread > 0) {
180 			if (op == COPY || op == COPYVERIFY) {
181 				if (needeof) {
182 					writeop(outp, MTWEOF);
183 					needeof = 0;
184 				}
185 				nw = write(outp, buff, nread);
186 				if (nw != nread) {
187 				    fprintf(stderr,
188 					"write error, file %d, record %ld: ",
189 					filen, record);
190 				    if (nw == -1)
191 					perror("");
192 				    else
193 					fprintf(stderr,
194 					    "write (%d) != read (%d)\n",
195 					    nw, nread);
196 				    fprintf(stderr, "copy aborted\n");
197 				    exit(5);
198 				}
199 			}
200 			size += nread;
201 			record++;
202 		} else {
203 			if (lastnread <= 0 && lastnread != NOCOUNT) {
204 				fprintf(msg, "eot\n");
205 				break;
206 			}
207 			fprintf(msg,
208 			    "file %d: eof after %lu records: %lu bytes\n",
209 			    filen, record, size);
210 			needeof = 1;
211 			filen++;
212 			tsize += size;
213 			size = record = lastrec = 0;
214 			lastnread = 0;
215 		}
216 		lastnread = nread;
217 	}
218 	fprintf(msg, "total length: %lu bytes\n", tsize);
219 	(void)signal(SIGINT, oldsig);
220 	if (op == COPY || op == COPYVERIFY) {
221 		writeop(outp, MTWEOF);
222 		writeop(outp, MTWEOF);
223 		if (op == COPYVERIFY) {
224 			writeop(outp, MTREW);
225 			writeop(inp, MTREW);
226 			verify(inp, outp, buff);
227 		}
228 	}
229 	exit(0);
230 }
231 
232 void
233 verify(inp, outp, outb)
234 	register int inp, outp;
235 	register char *outb;
236 {
237 	register int eot, inmaxblk, inn, outmaxblk, outn;
238 	register 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 			perror("tcopy: 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 			perror("tcopy: 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 				return;
273 			}
274 		} else {
275 			if (bcmp(inb, outb, inn)) {
276 				fprintf(msg,
277 				    "tcopy: tapes have different data.\n");
278 				break;
279 			}
280 			eot = 0;
281 		}
282 	}
283 	exit(1);
284 }
285 
286 void
287 intr(signo)
288 	int signo;
289 {
290 	if (record)
291 		if (record - lastrec > 1)
292 			fprintf(msg, "records %ld to %ld\n", lastrec, record);
293 		else
294 			fprintf(msg, "record %ld\n", lastrec);
295 	fprintf(msg, "interrupt at file %d: record %ld\n", filen, record);
296 	fprintf(msg, "total length: %ld bytes\n", tsize + size);
297 	exit(1);
298 }
299 
300 void *
301 getspace(blk)
302 	int blk;
303 {
304 	void *bp;
305 
306 	if ((bp = malloc((size_t)blk)) == NULL) {
307 		fprintf(stderr, "tcopy: no memory\n");
308 		exit(11);
309 	}
310 	return (bp);
311 }
312 
313 void
314 writeop(fd, type)
315 	int fd, type;
316 {
317 	struct mtop op;
318 
319 	op.mt_op = type;
320 	op.mt_count = (daddr_t)1;
321 	if (ioctl(fd, MTIOCTOP, (char *)&op) < 0) {
322 		perror("tcopy: tape op");
323 		exit(6);
324 	}
325 }
326 
327 void
328 usage()
329 {
330 	fprintf(stderr, "usage: tcopy [-cvx] [-s maxblk] src [dest]\n");
331 	exit(1);
332 }
333