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