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