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