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