1/* $NetBSD: main.c,v 1.78 2020/12/03 08:25:57 kre 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 40static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 5/1/95"; 41#else 42__RCSID("$NetBSD: main.c,v 1.78 2020/12/03 08:25:57 kre 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 72union u_spcl u_spcl; 73struct ufsi *ufsib; 74int mapsize; 75char *usedinomap; 76char *dumpdirmap; 77char *dumpinomap; 78char *disk; 79char *disk_dev; 80const char *tape; 81const char *dumpdates; 82const char *temp; 83char lastlevel; 84char level; 85int uflag; 86const char *dumpdev; 87int eflag; 88int lflag; 89int diskfd; 90int tapefd; 91int pipeout; 92int trueinc; 93ino_t curino; 94int newtape; 95u_int64_t tapesize; 96long tsize; 97long asize; 98int etapes; 99int nonodump; 100int unlimited; 101time_t tstart_writing; 102time_t tstart_volume; 103int xferrate; 104char sblock_buf[MAXBSIZE]; 105int dev_bshift; 106int tp_bshift; 107int needswap; 108 109int timestamp; /* print message timestamps */ 110int notify; /* notify operator flag */ 111u_int64_t blockswritten; /* number of blocks written on current tape */ 112int tapeno; /* current tape number */ 113int density; /* density in bytes/0.1" */ 114int ntrec = NTREC; /* # tape blocks in each tape record */ 115int cartridge; /* Assume non-cartridge tape */ 116long dev_bsize = 1; /* recalculated below */ 117long blocksperfile; /* output blocks per file */ 118const char *host; /* remote host (if any) */ 119int readcache = -1; /* read cache size (in readblksize blks) */ 120int readblksize = -1; /* read block size */ 121char default_time_string[] = "%T %Z"; /* default timestamp string */ 122char *time_string = default_time_string; /* timestamp string */ 123 124static long numarg(const char *, long, long); 125static void obsolete(int *, char **[]); 126static void usage(void); 127 128int 129main(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 if ((cp = strchr(tape, ':')) != NULL) { 413 host = tape; 414 /* This is fine, because all the const strings don't have : */ 415 *cp++ = '\0'; 416 tape = cp; 417#ifdef RDUMP 418 if (rmthost(host) == 0) 419 exit(X_STARTUP); 420#else 421 (void)fprintf(stderr, "remote dump not enabled\n"); 422 exit(X_STARTUP); 423#endif 424 } 425 426 if (signal(SIGHUP, SIG_IGN) != SIG_IGN) 427 signal(SIGHUP, sig); 428 if (signal(SIGTRAP, SIG_IGN) != SIG_IGN) 429 signal(SIGTRAP, sig); 430 if (signal(SIGFPE, SIG_IGN) != SIG_IGN) 431 signal(SIGFPE, sig); 432 if (signal(SIGBUS, SIG_IGN) != SIG_IGN) 433 signal(SIGBUS, sig); 434#if 0 435 if (signal(SIGSEGV, SIG_IGN) != SIG_IGN) 436 signal(SIGSEGV, sig); 437#endif 438 if (signal(SIGTERM, SIG_IGN) != SIG_IGN) 439 signal(SIGTERM, sig); 440 if (signal(SIGINT, interrupt) == SIG_IGN) 441 signal(SIGINT, SIG_IGN); 442 443 /* 444 * disk can be either the full special file name, or 445 * the file system name. 446 */ 447 mountpoint = NULL; 448 mntinfo = mntinfosearch(disk); 449 if ((dt = fstabsearch(disk)) != NULL) { 450 if (getfsspecname(buf, sizeof(buf), dt->fs_spec) == NULL) 451 quite(errno, "can't resolve mount %s (%s)", dt->fs_spec, 452 buf); 453 if (getdiskrawname(rbuf, sizeof(rbuf), buf) == NULL) 454 quite(errno, "can't get disk raw name for %s", buf); 455 disk = rbuf; 456 mountpoint = dt->fs_file; 457 msg("Found %s on %s in %s\n", disk, mountpoint, _PATH_FSTAB); 458 } else if (mntinfo != NULL) { 459 if (getdiskrawname(rbuf, sizeof(rbuf), mntinfo->f_mntfromname) 460 == NULL) 461 quite(errno, "can't get disk raw name for %s", 462 mntinfo->f_mntfromname); 463 disk = rbuf; 464 mountpoint = mntinfo->f_mntonname; 465 msg("Found %s on %s in mount table\n", disk, mountpoint); 466 } 467 if (mountpoint != NULL) { 468 if (dirc != 0) 469 (void)snprintf(spcl.c_filesys, sizeof(spcl.c_filesys), 470 "a subset of %s", mountpoint); 471 else 472 (void)strlcpy(spcl.c_filesys, mountpoint, 473 sizeof(spcl.c_filesys)); 474 } else if (Fflag) { 475 (void)strlcpy(spcl.c_filesys, "a file system image", 476 sizeof(spcl.c_filesys)); 477 } else { 478 (void)strlcpy(spcl.c_filesys, "an unlisted file system", 479 sizeof(spcl.c_filesys)); 480 } 481 (void)strlcpy(spcl.c_dev, disk, sizeof(spcl.c_dev)); 482 (void)strlcpy(spcl.c_label, labelstr, sizeof(spcl.c_label)); 483 (void)gethostname(spcl.c_host, sizeof(spcl.c_host)); 484 spcl.c_host[sizeof(spcl.c_host) - 1] = '\0'; 485 486 if ((snap_backup != NULL || snap_internal) && mntinfo == NULL) { 487 msg("WARNING: Cannot use -x or -X on unmounted file system.\n"); 488 snap_backup = NULL; 489 snap_internal = 0; 490 } 491 492#ifdef DUMP_LFS 493 sync(); 494 if (snap_backup != NULL || snap_internal) { 495 if (lfs_wrap_stop(mountpoint) < 0) { 496 msg("Cannot stop writing on %s\n", mountpoint); 497 exit(X_STARTUP); 498 } 499 } 500 if ((diskfd = open(disk, O_RDONLY)) < 0) { 501 msg("Cannot open %s\n", disk); 502 exit(X_STARTUP); 503 } 504 disk_dev = disk; 505#else /* ! DUMP_LFS */ 506 if (snap_backup != NULL || snap_internal) { 507 diskfd = snap_open(mntinfo->f_mntonname, snap_backup, 508 &tnow, &disk_dev); 509 if (diskfd < 0) { 510 msg("Cannot open snapshot of %s\n", 511 mntinfo->f_mntonname); 512 exit(X_STARTUP); 513 } 514 spcl.c_date = tnow; 515 } else { 516 if ((diskfd = open(disk, O_RDONLY)) < 0) { 517 msg("Cannot open %s\n", disk); 518 exit(X_STARTUP); 519 } 520 disk_dev = disk; 521 } 522 sync(); 523#endif /* ! DUMP_LFS */ 524 525 needswap = fs_read_sblock(sblock_buf); 526 527 /* true incremental is always a level 10 dump */ 528 spcl.c_level = trueinc? iswap32(10): iswap32(level - '0'); 529 spcl.c_type = iswap32(TS_TAPE); 530 spcl.c_date = iswap32(spcl.c_date); 531 spcl.c_ddate = iswap32(spcl.c_ddate); 532 if (!Tflag) 533 getdumptime(); /* /etc/dumpdates snarfed */ 534 535 date = iswap32(spcl.c_date); 536 msg("Date of this level %c dump: %s", level, 537 spcl.c_date == 0 ? "the epoch\n" : ctime(&date)); 538 date = iswap32(spcl.c_ddate); 539 msg("Date of last level %c dump: %s", lastlevel, 540 spcl.c_ddate == 0 ? "the epoch\n" : ctime(&date)); 541 msg("Dumping "); 542 if (snap_backup != NULL || snap_internal) 543 msgtail("a snapshot of "); 544 if (dirc != 0) 545 msgtail("a subset of "); 546 msgtail("%s (%s) ", disk, spcl.c_filesys); 547 if (host) 548 msgtail("to %s on host %s\n", tape, host); 549 else 550 msgtail("to %s\n", tape); 551 msg("Label: %s\n", labelstr); 552 553 ufsib = fs_parametrize(); 554 555 dev_bshift = ffs(dev_bsize) - 1; 556 if (dev_bsize != (1 << dev_bshift)) 557 quit("dev_bsize (%ld) is not a power of 2", dev_bsize); 558 tp_bshift = ffs(TP_BSIZE) - 1; 559 if (TP_BSIZE != (1 << tp_bshift)) 560 quit("TP_BSIZE (%d) is not a power of 2", TP_BSIZE); 561 maxino = fs_maxino(); 562 mapsize = roundup(howmany(maxino, NBBY), TP_BSIZE); 563 usedinomap = (char *)xcalloc((unsigned) mapsize, sizeof(char)); 564 dumpdirmap = (char *)xcalloc((unsigned) mapsize, sizeof(char)); 565 dumpinomap = (char *)xcalloc((unsigned) mapsize, sizeof(char)); 566 tapesize = 3 * (howmany(mapsize * sizeof(char), TP_BSIZE) + 1); 567 568 nonodump = iswap32(spcl.c_level) < honorlevel; 569 570 initcache(readcache, readblksize); 571 572 (void)signal(SIGINFO, statussig); 573 574 msg("mapping (Pass I) [regular files]\n"); 575 anydirskipped = mapfiles(maxino, &tapesize, mountpoint, 576 (dirc ? argv : NULL)); 577 578 msg("mapping (Pass II) [directories]\n"); 579 while (anydirskipped) { 580 anydirskipped = mapdirs(maxino, &tapesize); 581 } 582 583 if (pipeout || unlimited) { 584 tapesize += 10; /* 10 trailer blocks */ 585 msg("estimated %llu tape blocks.\n", 586 (unsigned long long)tapesize); 587 } else { 588 double fetapes; 589 590 if (blocksperfile) 591 fetapes = (double) tapesize / blocksperfile; 592 else if (cartridge) { 593 /* Estimate number of tapes, assuming streaming stops at 594 the end of each block written, and not in mid-block. 595 Assume no erroneous blocks; this can be compensated 596 for with an artificially low tape size. */ 597 fetapes = 598 ( (double) tapesize /* blocks */ 599 * TP_BSIZE /* bytes/block */ 600 * (1.0/density) /* 0.1" / byte */ 601 + 602 (double) tapesize /* blocks */ 603 * (1.0/ntrec) /* streaming-stops per block */ 604 * 15.48 /* 0.1" / streaming-stop */ 605 ) * (1.0 / tsize ); /* tape / 0.1" */ 606 } else { 607 /* Estimate number of tapes, for old fashioned 9-track 608 tape */ 609 int tenthsperirg = (density == 625) ? 3 : 7; 610 fetapes = 611 ( tapesize /* blocks */ 612 * TP_BSIZE /* bytes / block */ 613 * (1.0/density) /* 0.1" / byte */ 614 + 615 tapesize /* blocks */ 616 * (1.0/ntrec) /* IRG's / block */ 617 * tenthsperirg /* 0.1" / IRG */ 618 ) * (1.0 / tsize ); /* tape / 0.1" */ 619 } 620 etapes = fetapes; /* truncating assignment */ 621 etapes++; 622 /* count the dumped inodes map on each additional tape */ 623 tapesize += (etapes - 1) * 624 (howmany(mapsize * sizeof(char), TP_BSIZE) + 1); 625 tapesize += etapes + 10; /* headers + 10 trailer blks */ 626 msg("estimated %llu tape blocks on %3.2f tape(s).\n", 627 (unsigned long long)tapesize, fetapes); 628 } 629 /* 630 * If the user only wants an estimate of the number of 631 * tapes, exit now. 632 */ 633 if (just_estimate) 634 exit(X_FINOK); 635 636 /* 637 * Allocate tape buffer. 638 */ 639 if (!alloctape()) 640 quit("can't allocate tape buffers - try a smaller" 641 " blocking factor."); 642 643 startnewtape(1); 644 (void)time((time_t *)&(tstart_writing)); 645 xferrate = 0; 646 dumpmap(usedinomap, TS_CLRI, maxino - 1); 647 648 msg("dumping (Pass III) [directories]\n"); 649 dirty = 0; /* XXX just to get gcc to shut up */ 650 for (map = dumpdirmap, ino = 1; ino < maxino; ino++) { 651 if (((ino - 1) % NBBY) == 0) /* map is offset by 1 */ 652 dirty = *map++; 653 else 654 dirty >>= 1; 655 if ((dirty & 1) == 0) 656 continue; 657 /* 658 * Skip directory inodes deleted and maybe reallocated 659 */ 660 dp = getino(ino); 661 if ((DIP(dp, mode) & IFMT) != IFDIR) 662 continue; 663 (void)dumpino(dp, ino); 664 } 665 666 msg("dumping (Pass IV) [regular files]\n"); 667 for (map = dumpinomap, ino = 1; ino < maxino; ino++) { 668 int mode; 669 670 if (((ino - 1) % NBBY) == 0) /* map is offset by 1 */ 671 dirty = *map++; 672 else 673 dirty >>= 1; 674 if ((dirty & 1) == 0) 675 continue; 676 /* 677 * Skip inodes deleted and reallocated as directories. 678 */ 679 dp = getino(ino); 680 mode = DIP(dp, mode) & IFMT; 681 if (mode == IFDIR) 682 continue; 683 (void)dumpino(dp, ino); 684 } 685 686 spcl.c_type = iswap32(TS_END); 687 for (i = 0; i < ntrec; i++) 688 writeheader(maxino - 1); 689 if (pipeout) 690 msg("%lld tape blocks\n",(long long)iswap64(spcl.c_tapea)); 691 else 692 msg("%lld tape blocks on %d volume%s\n", 693 (long long)iswap64(spcl.c_tapea), iswap32(spcl.c_volume), 694 (iswap32(spcl.c_volume) == 1) ? "" : "s"); 695 tnow = do_stats(); 696 date = iswap32(spcl.c_date); 697 msg("Date of this level %c dump: %s", level, 698 spcl.c_date == 0 ? "the epoch\n" : ctime(&date)); 699 msg("Date this dump completed: %s", ctime(&tnow)); 700 msg("Average transfer rate: %d KB/s\n", xferrate / tapeno); 701 putdumptime(); 702 trewind(0); 703 broadcast("DUMP IS DONE!\a\a\n"); 704#ifdef DUMP_LFS 705 lfs_wrap_go(); 706#endif /* DUMP_LFS */ 707 msg("DUMP IS DONE\n"); 708 Exit(X_FINOK); 709 /* NOTREACHED */ 710 exit(X_FINOK); /* XXX: to satisfy gcc */ 711} 712 713static void 714usage(void) 715{ 716 const char *prog = getprogname(); 717 718 (void)fprintf(stderr, 719"usage: %s [-0123456789aceFinStuX] [-B records] [-b blocksize]\n" 720" [-d density] [-f file] [-h level] [-k read-blocksize]\n" 721" [-L label] [-l timeout] [-r cachesize] [-s feet]\n" 722" [-T date] [-U dumpdev] [-x snap-backup] files-to-dump\n" 723" %s [-W | -w]\n", prog, prog); 724 exit(X_STARTUP); 725} 726 727/* 728 * Pick up a numeric argument. It must be nonnegative and in the given 729 * range (except that a vmax of 0 means unlimited). 730 */ 731static long 732numarg(const char *meaning, long vmin, long vmax) 733{ 734 char *p; 735 long val; 736 737 val = strtol(optarg, &p, 10); 738 if (*p) 739 errx(X_STARTUP, "illegal %s -- %s", meaning, optarg); 740 if (val < vmin || (vmax && val > vmax)) 741 errx(X_STARTUP, "%s must be between %ld and %ld", 742 meaning, vmin, vmax); 743 return (val); 744} 745 746void 747sig(int signo) 748{ 749 750 switch(signo) { 751 case SIGALRM: 752 case SIGBUS: 753 case SIGFPE: 754 case SIGHUP: 755 case SIGTERM: 756 case SIGTRAP: 757 if (pipeout) 758 quit("Signal on pipe: cannot recover"); 759 msg("Rewriting attempted as response to signal %s.\n", sys_siglist[signo]); 760 (void)fflush(stderr); 761 (void)fflush(stdout); 762 close_rewind(); 763 exit(X_REWRITE); 764 /* NOTREACHED */ 765 case SIGSEGV: 766 msg("SIGSEGV: ABORTING!\n"); 767 (void)signal(SIGSEGV, SIG_DFL); 768 (void)kill(0, SIGSEGV); 769 /* NOTREACHED */ 770 } 771} 772 773/* 774 * obsolete -- 775 * Change set of key letters and ordered arguments into something 776 * getopt(3) will like. 777 */ 778static void 779obsolete(int *argcp, char **argvp[]) 780{ 781 int argc, flags; 782 char *ap, **argv, *flagsp, **nargv, *p; 783 784 /* Setup. */ 785 argv = *argvp; 786 argc = *argcp; 787 788 /* Return if no arguments or first argument has leading dash. */ 789 ap = argv[1]; 790 if (argc == 1 || *ap == '-') 791 return; 792 793 /* Allocate space for new arguments. */ 794 *argvp = nargv = xmalloc((argc + 1) * sizeof(char *)); 795 p = flagsp = xmalloc(strlen(ap) + 2); 796 797 *nargv++ = *argv; 798 argv += 2; 799 800 for (flags = 0; *ap; ++ap) { 801 switch (*ap) { 802 case 'B': 803 case 'b': 804 case 'd': 805 case 'f': 806 case 'h': 807 case 's': 808 case 'T': 809 case 'x': 810 if (*argv == NULL) { 811 warnx("option requires an argument -- %c", *ap); 812 usage(); 813 } 814 nargv[0] = xmalloc(strlen(*argv) + 2 + 1); 815 nargv[0][0] = '-'; 816 nargv[0][1] = *ap; 817 (void)strcpy(&nargv[0][2], *argv); /* XXX safe strcpy */ 818 ++argv; 819 ++nargv; 820 break; 821 default: 822 if (!flags) { 823 *p++ = '-'; 824 flags = 1; 825 } 826 *p++ = *ap; 827 break; 828 } 829 } 830 831 /* Terminate flags. */ 832 if (flags) { 833 *p = '\0'; 834 *nargv++ = flagsp; 835 } else 836 free(flagsp); 837 838 /* Copy remaining arguments. */ 839 while ((*nargv++ = *argv++) != NULL) 840 ; 841 842 /* Update argument count. */ 843 *argcp = nargv - *argvp - 1; 844} 845 846 847void * 848xcalloc(size_t number, size_t size) 849{ 850 void *p; 851 852 p = calloc(number, size); 853 if (p == NULL) 854 quite(errno, "Can't allocate %zu bytes", size * number); 855 return (p); 856} 857 858void * 859xmalloc(size_t size) 860{ 861 void *p; 862 863 p = malloc(size); 864 if (p == NULL) 865 quite(errno, "Can't allocate %zu bytes", size); 866 return (p); 867} 868 869char * 870xstrdup(const char *str) 871{ 872 char *p; 873 874 p = strdup(str); 875 if (p == NULL) 876 quite(errno, "Can't copy %s", str); 877 return (p); 878} 879