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