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