1/* $NetBSD: tcopy.c,v 1.16 2009/04/13 23:44:49 lukem 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 40static char sccsid[] = "@(#)tcopy.c 8.3 (Berkeley) 1/23/95"; 41#endif 42__RCSID("$NetBSD: tcopy.c,v 1.16 2009/04/13 23:44:49 lukem 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 64static int filen, guesslen, maxblk = MAXREC; 65static long lastrec, record; 66static off_t size, tsize; 67static FILE *msg = stdout; 68 69static void *getspace(int); 70__dead static void intr(int); 71__dead static void usage(void); 72static void verify(int, int, char *); 73static void writeop(int, int); 74 75int 76main(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 } 177r1: 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 233static void 234verify(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 } 252r1: 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 } 262r2: 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 289static void 290intr(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 304static void * 305getspace(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 315static void 316writeop(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 326static void 327usage(void) 328{ 329 330 fprintf(stderr, "usage: tcopy [-cvx] [-s maxblk] src [dest]\n"); 331 exit(1); 332} 333