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