1 /* $NetBSD: main.c,v 1.79 2024/10/04 11:38:44 rillig Exp $ */ 2 3 /*- 4 * Copyright (c) 1980, 1991, 1993, 1994 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) 1980, 1991, 1993, 1994\ 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[] = "@(#)main.c 8.6 (Berkeley) 5/1/95"; 41 #else 42 __RCSID("$NetBSD: main.c,v 1.79 2024/10/04 11:38:44 rillig Exp $"); 43 #endif 44 #endif /* not lint */ 45 46 #include <sys/param.h> 47 #include <sys/time.h> 48 #include <sys/stat.h> 49 #include <sys/mount.h> 50 #include <sys/sysctl.h> 51 52 #include <ufs/ffs/fs.h> 53 #include <ufs/ffs/ffs_extern.h> 54 55 #include <ctype.h> 56 #include <err.h> 57 #include <errno.h> 58 #include <fcntl.h> 59 #include <fstab.h> 60 #include <signal.h> 61 #include <stdio.h> 62 #include <stdlib.h> 63 #include <string.h> 64 #include <time.h> 65 #include <unistd.h> 66 #include <util.h> 67 68 #include "dump.h" 69 #include "pathnames.h" 70 #include "snapshot.h" 71 72 union u_spcl u_spcl; 73 struct ufsi *ufsib; 74 int mapsize; 75 char *usedinomap; 76 char *dumpdirmap; 77 char *dumpinomap; 78 char *disk; 79 char *disk_dev; 80 const char *tape; 81 const char *dumpdates; 82 const char *temp; 83 char lastlevel; 84 char level; 85 int uflag; 86 const char *dumpdev; 87 int eflag; 88 int lflag; 89 int diskfd; 90 int tapefd; 91 int pipeout; 92 int trueinc; 93 ino_t curino; 94 int newtape; 95 u_int64_t tapesize; 96 long tsize; 97 long asize; 98 int etapes; 99 int nonodump; 100 int unlimited; 101 time_t tstart_writing; 102 time_t tstart_volume; 103 int xferrate; 104 char sblock_buf[MAXBSIZE]; 105 int dev_bshift; 106 int tp_bshift; 107 int needswap; 108 109 int timestamp; /* print message timestamps */ 110 int notify; /* notify operator flag */ 111 u_int64_t blockswritten; /* number of blocks written on current tape */ 112 int tapeno; /* current tape number */ 113 int density; /* density in bytes/0.1" */ 114 int ntrec = NTREC; /* # tape blocks in each tape record */ 115 int cartridge; /* Assume non-cartridge tape */ 116 long dev_bsize = 1; /* recalculated below */ 117 long blocksperfile; /* output blocks per file */ 118 const char *host; /* remote host (if any) */ 119 int readcache = -1; /* read cache size (in readblksize blks) */ 120 int readblksize = -1; /* read block size */ 121 char default_time_string[] = "%T %Z"; /* default timestamp string */ 122 char *time_string = default_time_string; /* timestamp string */ 123 124 static long numarg(const char *, long, long); 125 static void obsolete(int *, char **[]); 126 static void usage(void); 127 128 int 129 main(int argc, char *argv[]) 130 { 131 ino_t ino; 132 int dirty; 133 union dinode *dp; 134 struct fstab *dt; 135 struct statvfs *mntinfo, fsbuf; 136 char *map, *cp; 137 int ch; 138 int i, anydirskipped, bflag = 0, Tflag = 0, Fflag = 0, honorlevel = 1; 139 int snap_internal = 0; 140 ino_t maxino; 141 time_t tnow, date; 142 int dirc; 143 char *mountpoint; 144 int just_estimate = 0; 145 char labelstr[LBLSIZE]; 146 char buf[MAXPATHLEN], rbuf[MAXPATHLEN]; 147 char *new_time_format; 148 char *snap_backup = NULL; 149 150 spcl.c_date = 0; 151 (void)time(&tnow); 152 spcl.c_date = tnow; 153 tzset(); /* set up timezone for strftime */ 154 if ((new_time_format = getenv("TIMEFORMAT")) != NULL) 155 time_string = new_time_format; 156 157 tsize = 0; /* Default later, based on 'c' option for cart tapes */ 158 if ((tape = getenv("TAPE")) == NULL) 159 tape = _PATH_DEFTAPE; 160 dumpdates = _PATH_DUMPDATES; 161 temp = _PATH_DTMP; 162 strcpy(labelstr, "none"); /* XXX safe strcpy. */ 163 if (TP_BSIZE / DEV_BSIZE == 0 || TP_BSIZE % DEV_BSIZE != 0) 164 quit("TP_BSIZE must be a multiple of DEV_BSIZE"); 165 level = '0'; 166 timestamp = 0; 167 168 if (argc < 2) 169 usage(); 170 171 obsolete(&argc, &argv); 172 while ((ch = getopt(argc, argv, 173 "0123456789aB:b:cd:D:eFf:h:ik:l:L:nr:s:StT:uU:Wwx:X")) != -1) 174 switch (ch) { 175 /* dump level */ 176 case '0': case '1': case '2': case '3': case '4': 177 case '5': case '6': case '7': case '8': case '9': 178 level = ch; 179 break; 180 181 case 'a': /* `auto-size', Write to EOM. */ 182 unlimited = 1; 183 break; 184 185 case 'B': /* blocks per output file */ 186 blocksperfile = numarg("blocks per file", 1L, 0L); 187 break; 188 189 case 'b': /* blocks per tape write */ 190 ntrec = numarg("blocks per write", 1L, 1000L); 191 bflag = 1; 192 break; 193 194 case 'c': /* Tape is cart. not 9-track */ 195 cartridge = 1; 196 break; 197 198 case 'd': /* density, in bits per inch */ 199 density = numarg("density", 10L, 327670L) / 10; 200 if (density >= 625 && !bflag) 201 ntrec = HIGHDENSITYTREC; 202 break; 203 204 case 'D': /* specify alt. dumpdates file */ 205 dumpdates = optarg; 206 break; 207 208 case 'e': /* eject full tapes */ 209 eflag = 1; 210 break; 211 212 case 'F': /* files-to-dump is an fs image */ 213 Fflag = 1; 214 break; 215 216 case 'f': /* output file */ 217 tape = optarg; 218 break; 219 220 case 'h': 221 honorlevel = numarg("honor level", 0L, 10L); 222 break; 223 224 case 'i': /* "true incremental" regardless level */ 225 level = 'i'; 226 trueinc = 1; 227 break; 228 229 case 'k': 230 readblksize = numarg("read block size", 0, 64) * 1024; 231 break; 232 233 case 'l': /* autoload after eject full tapes */ 234 eflag = 1; 235 lflag = numarg("timeout (in seconds)", 1, 0); 236 break; 237 238 case 'L': 239 /* 240 * Note that although there are LBLSIZE characters, 241 * the last must be '\0', so the limit on strlen() 242 * is really LBLSIZE-1. 243 */ 244 if (strlcpy(labelstr, optarg, sizeof(labelstr)) 245 >= sizeof(labelstr)) { 246 msg( 247 "WARNING Label `%s' is larger than limit of %lu characters.\n", 248 optarg, 249 (unsigned long)sizeof(labelstr) - 1); 250 msg("WARNING: Using truncated label `%s'.\n", 251 labelstr); 252 } 253 break; 254 case 'n': /* notify operators */ 255 notify = 1; 256 break; 257 258 case 'r': /* read cache size */ 259 readcache = numarg("read cache size", 0, 512); 260 break; 261 262 case 's': /* tape size, feet */ 263 tsize = numarg("tape size", 1L, 0L) * 12 * 10; 264 break; 265 266 case 'S': /* exit after estimating # of tapes */ 267 just_estimate = 1; 268 break; 269 270 case 't': 271 timestamp = 1; 272 break; 273 274 case 'T': /* time of last dump */ 275 spcl.c_ddate = unctime(optarg); 276 if (spcl.c_ddate < 0) { 277 (void)fprintf(stderr, "bad time \"%s\"\n", 278 optarg); 279 exit(X_STARTUP); 280 } 281 Tflag = 1; 282 lastlevel = '?'; 283 break; 284 285 case 'u': /* update /etc/dumpdates */ 286 uflag = 1; 287 break; 288 289 case 'U': /* dump device in /etc/dumpdates */ 290 dumpdev = optarg; 291 break; 292 293 case 'W': /* what to do */ 294 case 'w': 295 lastdump(ch); 296 exit(X_FINOK); /* do nothing else */ 297 298 case 'x': 299 snap_backup = optarg; 300 break; 301 302 case 'X': 303 snap_internal = 1; 304 break; 305 306 default: 307 usage(); 308 } 309 argc -= optind; 310 argv += optind; 311 312 if (argc < 1) { 313 (void)fprintf(stderr, 314 "Must specify disk or image, or file list\n"); 315 exit(X_STARTUP); 316 } 317 318 319 /* 320 * determine if disk is a subdirectory, and setup appropriately 321 */ 322 getfstab(); /* /etc/fstab snarfed */ 323 disk = NULL; 324 disk_dev = NULL; 325 mountpoint = NULL; 326 dirc = 0; 327 for (i = 0; i < argc; i++) { 328 struct stat sb; 329 int error; 330 331 error = lstat(argv[i], &sb); 332 if (Fflag || (!error && (S_ISCHR(sb.st_mode) || S_ISBLK(sb.st_mode)))) { 333 if (error) 334 quite(errno, "can't stat %s", argv[i]); 335 disk = argv[i]; 336 multicheck: 337 if (dirc != 0) 338 quit("can't dump a disk or image at the same" 339 " time as a file list"); 340 break; 341 } 342 if ((dt = fstabsearch(argv[i])) != NULL) { 343 disk = argv[i]; 344 mountpoint = xstrdup(dt->fs_file); 345 goto multicheck; 346 } 347 if (statvfs(argv[i], &fsbuf) == -1) 348 quite(errno, "can't statvfs %s", argv[i]); 349 disk = fsbuf.f_mntfromname; 350 if (strcmp(argv[i], fsbuf.f_mntonname) == 0) 351 goto multicheck; 352 if (mountpoint == NULL) { 353 mountpoint = xstrdup(fsbuf.f_mntonname); 354 if (uflag) { 355 msg("Ignoring u flag for subdir dump\n"); 356 uflag = 0; 357 } 358 if (level > '0') { 359 msg("Subdir dump is done at level 0\n"); 360 level = '0'; 361 } 362 msg("Dumping sub files/directories from %s\n", 363 mountpoint); 364 } else { 365 if (strcmp(mountpoint, fsbuf.f_mntonname) != 0) 366 quit("%s is not on %s", argv[i], mountpoint); 367 } 368 msg("Dumping file/directory %s\n", argv[i]); 369 dirc++; 370 } 371 if (mountpoint) 372 free(mountpoint); 373 374 if (dirc == 0) { 375 argv++; 376 if (argc != 1) { 377 (void)fprintf(stderr, "Excess arguments to dump:"); 378 while (--argc) 379 (void)fprintf(stderr, " %s", *argv++); 380 (void)fprintf(stderr, "\n"); 381 exit(X_STARTUP); 382 } 383 } 384 if (Tflag && uflag) { 385 (void)fprintf(stderr, 386 "You cannot use the T and u flags together.\n"); 387 exit(X_STARTUP); 388 } 389 if (strcmp(tape, "-") == 0) { 390 pipeout++; 391 tape = "standard output"; 392 } 393 394 if (blocksperfile) 395 blocksperfile = blocksperfile / ntrec * ntrec; /* round down */ 396 else if (!unlimited) { 397 /* 398 * Determine how to default tape size and density 399 * 400 * density tape size 401 * 9-track 1600 bpi (160 bytes/.1") 2300 ft. 402 * 9-track 6250 bpi (625 bytes/.1") 2300 ft. 403 * cartridge 8000 bpi (100 bytes/.1") 1700 ft. 404 * (450*4 - slop) 405 */ 406 if (density == 0) 407 density = cartridge ? 100 : 160; 408 if (tsize == 0) 409 tsize = cartridge ? 1700L*120L : 2300L*120L; 410 } 411 412 /* LINTED 346 "call to 'strchr' effectively discards 'const'" */ 413 if ((cp = strchr(tape, ':')) != NULL) { 414 host = tape; 415 /* This is fine, because all the const strings don't have : */ 416 *cp++ = '\0'; 417 tape = cp; 418 #ifdef RDUMP 419 if (rmthost(host) == 0) 420 exit(X_STARTUP); 421 #else 422 (void)fprintf(stderr, "remote dump not enabled\n"); 423 exit(X_STARTUP); 424 #endif 425 } 426 427 if (signal(SIGHUP, SIG_IGN) != SIG_IGN) 428 signal(SIGHUP, sig); 429 if (signal(SIGTRAP, SIG_IGN) != SIG_IGN) 430 signal(SIGTRAP, sig); 431 if (signal(SIGFPE, SIG_IGN) != SIG_IGN) 432 signal(SIGFPE, sig); 433 if (signal(SIGBUS, SIG_IGN) != SIG_IGN) 434 signal(SIGBUS, sig); 435 #if 0 436 if (signal(SIGSEGV, SIG_IGN) != SIG_IGN) 437 signal(SIGSEGV, sig); 438 #endif 439 if (signal(SIGTERM, SIG_IGN) != SIG_IGN) 440 signal(SIGTERM, sig); 441 if (signal(SIGINT, interrupt) == SIG_IGN) 442 signal(SIGINT, SIG_IGN); 443 444 /* 445 * disk can be either the full special file name, or 446 * the file system name. 447 */ 448 mountpoint = NULL; 449 mntinfo = mntinfosearch(disk); 450 if ((dt = fstabsearch(disk)) != NULL) { 451 if (getfsspecname(buf, sizeof(buf), dt->fs_spec) == NULL) 452 quite(errno, "can't resolve mount %s (%s)", dt->fs_spec, 453 buf); 454 if (getdiskrawname(rbuf, sizeof(rbuf), buf) == NULL) 455 quite(errno, "can't get disk raw name for %s", buf); 456 disk = rbuf; 457 mountpoint = dt->fs_file; 458 msg("Found %s on %s in %s\n", disk, mountpoint, _PATH_FSTAB); 459 } else if (mntinfo != NULL) { 460 if (getdiskrawname(rbuf, sizeof(rbuf), mntinfo->f_mntfromname) 461 == NULL) 462 quite(errno, "can't get disk raw name for %s", 463 mntinfo->f_mntfromname); 464 disk = rbuf; 465 mountpoint = mntinfo->f_mntonname; 466 msg("Found %s on %s in mount table\n", disk, mountpoint); 467 } 468 if (mountpoint != NULL) { 469 if (dirc != 0) 470 (void)snprintf(spcl.c_filesys, sizeof(spcl.c_filesys), 471 "a subset of %s", mountpoint); 472 else 473 (void)strlcpy(spcl.c_filesys, mountpoint, 474 sizeof(spcl.c_filesys)); 475 } else if (Fflag) { 476 (void)strlcpy(spcl.c_filesys, "a file system image", 477 sizeof(spcl.c_filesys)); 478 } else { 479 (void)strlcpy(spcl.c_filesys, "an unlisted file system", 480 sizeof(spcl.c_filesys)); 481 } 482 (void)strlcpy(spcl.c_dev, disk, sizeof(spcl.c_dev)); 483 (void)strlcpy(spcl.c_label, labelstr, sizeof(spcl.c_label)); 484 (void)gethostname(spcl.c_host, sizeof(spcl.c_host)); 485 spcl.c_host[sizeof(spcl.c_host) - 1] = '\0'; 486 487 if ((snap_backup != NULL || snap_internal) && mntinfo == NULL) { 488 msg("WARNING: Cannot use -x or -X on unmounted file system.\n"); 489 snap_backup = NULL; 490 snap_internal = 0; 491 } 492 493 #ifdef DUMP_LFS 494 sync(); 495 if (snap_backup != NULL || snap_internal) { 496 if (lfs_wrap_stop(mountpoint) < 0) { 497 msg("Cannot stop writing on %s\n", mountpoint); 498 exit(X_STARTUP); 499 } 500 } 501 if ((diskfd = open(disk, O_RDONLY)) < 0) { 502 msg("Cannot open %s\n", disk); 503 exit(X_STARTUP); 504 } 505 disk_dev = disk; 506 #else /* ! DUMP_LFS */ 507 if (snap_backup != NULL || snap_internal) { 508 diskfd = snap_open(mntinfo->f_mntonname, snap_backup, 509 &tnow, &disk_dev); 510 if (diskfd < 0) { 511 msg("Cannot open snapshot of %s\n", 512 mntinfo->f_mntonname); 513 exit(X_STARTUP); 514 } 515 spcl.c_date = tnow; 516 } else { 517 if ((diskfd = open(disk, O_RDONLY)) < 0) { 518 msg("Cannot open %s\n", disk); 519 exit(X_STARTUP); 520 } 521 disk_dev = disk; 522 } 523 sync(); 524 #endif /* ! DUMP_LFS */ 525 526 needswap = fs_read_sblock(sblock_buf); 527 528 /* true incremental is always a level 10 dump */ 529 spcl.c_level = trueinc? iswap32(10): iswap32(level - '0'); 530 spcl.c_type = iswap32(TS_TAPE); 531 spcl.c_date = iswap32(spcl.c_date); 532 spcl.c_ddate = iswap32(spcl.c_ddate); 533 if (!Tflag) 534 getdumptime(); /* /etc/dumpdates snarfed */ 535 536 date = iswap32(spcl.c_date); 537 msg("Date of this level %c dump: %s", level, 538 spcl.c_date == 0 ? "the epoch\n" : ctime(&date)); 539 date = iswap32(spcl.c_ddate); 540 msg("Date of last level %c dump: %s", lastlevel, 541 spcl.c_ddate == 0 ? "the epoch\n" : ctime(&date)); 542 msg("Dumping "); 543 if (snap_backup != NULL || snap_internal) 544 msgtail("a snapshot of "); 545 if (dirc != 0) 546 msgtail("a subset of "); 547 msgtail("%s (%s) ", disk, spcl.c_filesys); 548 if (host) 549 msgtail("to %s on host %s\n", tape, host); 550 else 551 msgtail("to %s\n", tape); 552 msg("Label: %s\n", labelstr); 553 554 ufsib = fs_parametrize(); 555 556 dev_bshift = ffs(dev_bsize) - 1; 557 if (dev_bsize != (1 << dev_bshift)) 558 quit("dev_bsize (%ld) is not a power of 2", dev_bsize); 559 tp_bshift = ffs(TP_BSIZE) - 1; 560 if (TP_BSIZE != (1 << tp_bshift)) 561 quit("TP_BSIZE (%d) is not a power of 2", TP_BSIZE); 562 maxino = fs_maxino(); 563 mapsize = roundup(howmany(maxino, NBBY), TP_BSIZE); 564 usedinomap = (char *)xcalloc((unsigned) mapsize, sizeof(char)); 565 dumpdirmap = (char *)xcalloc((unsigned) mapsize, sizeof(char)); 566 dumpinomap = (char *)xcalloc((unsigned) mapsize, sizeof(char)); 567 tapesize = 3 * (howmany(mapsize * sizeof(char), TP_BSIZE) + 1); 568 569 nonodump = iswap32(spcl.c_level) < honorlevel; 570 571 initcache(readcache, readblksize); 572 573 (void)signal(SIGINFO, statussig); 574 575 msg("mapping (Pass I) [regular files]\n"); 576 anydirskipped = mapfiles(maxino, &tapesize, mountpoint, 577 (dirc ? argv : NULL)); 578 579 msg("mapping (Pass II) [directories]\n"); 580 while (anydirskipped) { 581 anydirskipped = mapdirs(maxino, &tapesize); 582 } 583 584 if (pipeout || unlimited) { 585 tapesize += 10; /* 10 trailer blocks */ 586 msg("estimated %llu tape blocks.\n", 587 (unsigned long long)tapesize); 588 } else { 589 double fetapes; 590 591 if (blocksperfile) 592 fetapes = (double) tapesize / blocksperfile; 593 else if (cartridge) { 594 /* Estimate number of tapes, assuming streaming stops at 595 the end of each block written, and not in mid-block. 596 Assume no erroneous blocks; this can be compensated 597 for with an artificially low tape size. */ 598 fetapes = 599 ( (double) tapesize /* blocks */ 600 * TP_BSIZE /* bytes/block */ 601 * (1.0/density) /* 0.1" / byte */ 602 + 603 (double) tapesize /* blocks */ 604 * (1.0/ntrec) /* streaming-stops per block */ 605 * 15.48 /* 0.1" / streaming-stop */ 606 ) * (1.0 / tsize ); /* tape / 0.1" */ 607 } else { 608 /* Estimate number of tapes, for old fashioned 9-track 609 tape */ 610 int tenthsperirg = (density == 625) ? 3 : 7; 611 fetapes = 612 ( tapesize /* blocks */ 613 * TP_BSIZE /* bytes / block */ 614 * (1.0/density) /* 0.1" / byte */ 615 + 616 tapesize /* blocks */ 617 * (1.0/ntrec) /* IRG's / block */ 618 * tenthsperirg /* 0.1" / IRG */ 619 ) * (1.0 / tsize ); /* tape / 0.1" */ 620 } 621 etapes = fetapes; /* truncating assignment */ 622 etapes++; 623 /* count the dumped inodes map on each additional tape */ 624 tapesize += (etapes - 1) * 625 (howmany(mapsize * sizeof(char), TP_BSIZE) + 1); 626 tapesize += etapes + 10; /* headers + 10 trailer blks */ 627 msg("estimated %llu tape blocks on %3.2f tape(s).\n", 628 (unsigned long long)tapesize, fetapes); 629 } 630 /* 631 * If the user only wants an estimate of the number of 632 * tapes, exit now. 633 */ 634 if (just_estimate) 635 exit(X_FINOK); 636 637 /* 638 * Allocate tape buffer. 639 */ 640 if (!alloctape()) 641 quit("can't allocate tape buffers - try a smaller" 642 " blocking factor."); 643 644 startnewtape(1); 645 (void)time((time_t *)&(tstart_writing)); 646 xferrate = 0; 647 dumpmap(usedinomap, TS_CLRI, maxino - 1); 648 649 msg("dumping (Pass III) [directories]\n"); 650 dirty = 0; /* XXX just to get gcc to shut up */ 651 for (map = dumpdirmap, ino = 1; ino < maxino; ino++) { 652 if (((ino - 1) % NBBY) == 0) /* map is offset by 1 */ 653 dirty = *map++; 654 else 655 dirty >>= 1; 656 if ((dirty & 1) == 0) 657 continue; 658 /* 659 * Skip directory inodes deleted and maybe reallocated 660 */ 661 dp = getino(ino); 662 if ((DIP(dp, mode) & IFMT) != IFDIR) 663 continue; 664 (void)dumpino(dp, ino); 665 } 666 667 msg("dumping (Pass IV) [regular files]\n"); 668 for (map = dumpinomap, ino = 1; ino < maxino; ino++) { 669 int mode; 670 671 if (((ino - 1) % NBBY) == 0) /* map is offset by 1 */ 672 dirty = *map++; 673 else 674 dirty >>= 1; 675 if ((dirty & 1) == 0) 676 continue; 677 /* 678 * Skip inodes deleted and reallocated as directories. 679 */ 680 dp = getino(ino); 681 mode = DIP(dp, mode) & IFMT; 682 if (mode == IFDIR) 683 continue; 684 (void)dumpino(dp, ino); 685 } 686 687 spcl.c_type = iswap32(TS_END); 688 for (i = 0; i < ntrec; i++) 689 writeheader(maxino - 1); 690 if (pipeout) 691 msg("%lld tape blocks\n",(long long)iswap64(spcl.c_tapea)); 692 else 693 msg("%lld tape blocks on %d volume%s\n", 694 (long long)iswap64(spcl.c_tapea), iswap32(spcl.c_volume), 695 (iswap32(spcl.c_volume) == 1) ? "" : "s"); 696 tnow = do_stats(); 697 date = iswap32(spcl.c_date); 698 msg("Date of this level %c dump: %s", level, 699 spcl.c_date == 0 ? "the epoch\n" : ctime(&date)); 700 msg("Date this dump completed: %s", ctime(&tnow)); 701 msg("Average transfer rate: %d KB/s\n", xferrate / tapeno); 702 putdumptime(); 703 trewind(0); 704 broadcast("DUMP IS DONE!\a\a\n"); 705 #ifdef DUMP_LFS 706 lfs_wrap_go(); 707 #endif /* DUMP_LFS */ 708 msg("DUMP IS DONE\n"); 709 Exit(X_FINOK); 710 /* NOTREACHED */ 711 exit(X_FINOK); /* XXX: to satisfy gcc */ 712 } 713 714 static void 715 usage(void) 716 { 717 const char *prog = getprogname(); 718 719 (void)fprintf(stderr, 720 "usage: %s [-0123456789aceFinStuX] [-B records] [-b blocksize]\n" 721 " [-d density] [-f file] [-h level] [-k read-blocksize]\n" 722 " [-L label] [-l timeout] [-r cachesize] [-s feet]\n" 723 " [-T date] [-U dumpdev] [-x snap-backup] files-to-dump\n" 724 " %s [-W | -w]\n", prog, prog); 725 exit(X_STARTUP); 726 } 727 728 /* 729 * Pick up a numeric argument. It must be nonnegative and in the given 730 * range (except that a vmax of 0 means unlimited). 731 */ 732 static long 733 numarg(const char *meaning, long vmin, long vmax) 734 { 735 char *p; 736 long val; 737 738 val = strtol(optarg, &p, 10); 739 if (*p) 740 errx(X_STARTUP, "illegal %s -- %s", meaning, optarg); 741 if (val < vmin || (vmax && val > vmax)) 742 errx(X_STARTUP, "%s must be between %ld and %ld", 743 meaning, vmin, vmax); 744 return (val); 745 } 746 747 void 748 sig(int signo) 749 { 750 751 switch(signo) { 752 case SIGALRM: 753 case SIGBUS: 754 case SIGFPE: 755 case SIGHUP: 756 case SIGTERM: 757 case SIGTRAP: 758 if (pipeout) 759 quit("Signal on pipe: cannot recover"); 760 msg("Rewriting attempted as response to signal %s.\n", sys_siglist[signo]); 761 (void)fflush(stderr); 762 (void)fflush(stdout); 763 close_rewind(); 764 exit(X_REWRITE); 765 /* NOTREACHED */ 766 case SIGSEGV: 767 msg("SIGSEGV: ABORTING!\n"); 768 (void)signal(SIGSEGV, SIG_DFL); 769 (void)kill(0, SIGSEGV); 770 /* NOTREACHED */ 771 } 772 } 773 774 /* 775 * obsolete -- 776 * Change set of key letters and ordered arguments into something 777 * getopt(3) will like. 778 */ 779 static void 780 obsolete(int *argcp, char **argvp[]) 781 { 782 int argc, flags; 783 char *ap, **argv, *flagsp, **nargv, *p; 784 785 /* Setup. */ 786 argv = *argvp; 787 argc = *argcp; 788 789 /* Return if no arguments or first argument has leading dash. */ 790 ap = argv[1]; 791 if (argc == 1 || *ap == '-') 792 return; 793 794 /* Allocate space for new arguments. */ 795 *argvp = nargv = xmalloc((argc + 1) * sizeof(char *)); 796 p = flagsp = xmalloc(strlen(ap) + 2); 797 798 *nargv++ = *argv; 799 argv += 2; 800 801 for (flags = 0; *ap; ++ap) { 802 switch (*ap) { 803 case 'B': 804 case 'b': 805 case 'd': 806 case 'f': 807 case 'h': 808 case 's': 809 case 'T': 810 case 'x': 811 if (*argv == NULL) { 812 warnx("option requires an argument -- %c", *ap); 813 usage(); 814 } 815 nargv[0] = xmalloc(strlen(*argv) + 2 + 1); 816 nargv[0][0] = '-'; 817 nargv[0][1] = *ap; 818 (void)strcpy(&nargv[0][2], *argv); /* XXX safe strcpy */ 819 ++argv; 820 ++nargv; 821 break; 822 default: 823 if (!flags) { 824 *p++ = '-'; 825 flags = 1; 826 } 827 *p++ = *ap; 828 break; 829 } 830 } 831 832 /* Terminate flags. */ 833 if (flags) { 834 *p = '\0'; 835 *nargv++ = flagsp; 836 } else 837 free(flagsp); 838 839 /* Copy remaining arguments. */ 840 while ((*nargv++ = *argv++) != NULL) 841 ; 842 843 /* Update argument count. */ 844 *argcp = nargv - *argvp - 1; 845 } 846 847 848 void * 849 xcalloc(size_t number, size_t size) 850 { 851 void *p; 852 853 p = calloc(number, size); 854 if (p == NULL) 855 quite(errno, "Can't allocate %zu bytes", size * number); 856 return (p); 857 } 858 859 void * 860 xmalloc(size_t size) 861 { 862 void *p; 863 864 p = malloc(size); 865 if (p == NULL) 866 quite(errno, "Can't allocate %zu bytes", size); 867 return (p); 868 } 869 870 char * 871 xstrdup(const char *str) 872 { 873 char *p; 874 875 p = strdup(str); 876 if (p == NULL) 877 quite(errno, "Can't copy %s", str); 878 return (p); 879 } 880