static long nodemap(__pmnsNode *p) { int n; if (p == NULL) return -1; for (n = 0; n < nodecnt; n++) { if (_map[n] == p) return n; } printf("%s: fatal error, cannot map node addr " PRINTF_P_PFX "%p\n", pmProgname, p); dumpmap(); exit(1); }
int main(int argc, char *argv[]) { struct stat sb; ino_t ino; int dirty; union dinode *dp; struct fstab *dt; char *map, *mntpt; int ch, mode, mntflags; int i, anydirskipped, bflag = 0, Tflag = 0, honorlevel = 1; int just_estimate = 0; ino_t maxino; char *tmsg; spcl.c_date = _time_to_time64(time(NULL)); tsize = 0; /* Default later, based on 'c' option for cart tapes */ dumpdates = _PATH_DUMPDATES; popenout = NULL; tape = NULL; temp = _PATH_DTMP; if (TP_BSIZE / DEV_BSIZE == 0 || TP_BSIZE % DEV_BSIZE != 0) quit("TP_BSIZE must be a multiple of DEV_BSIZE\n"); level = 0; rsync_friendly = 0; if (argc < 2) usage(); obsolete(&argc, &argv); while ((ch = getopt(argc, argv, "0123456789aB:b:C:cD:d:f:h:LnP:RrSs:T:uWw")) != -1) switch (ch) { /* dump level */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': level = 10 * level + ch - '0'; break; case 'a': /* `auto-size', Write to EOM. */ unlimited = 1; break; case 'B': /* blocks per output file */ blocksperfile = numarg("number of blocks per file", 1L, 0L); break; case 'b': /* blocks per tape write */ ntrec = numarg("number of blocks per write", 1L, 1000L); break; case 'C': cachesize = numarg("cachesize", 0, 0) * 1024 * 1024; break; case 'c': /* Tape is cart. not 9-track */ cartridge = 1; break; case 'D': dumpdates = optarg; break; case 'd': /* density, in bits per inch */ density = numarg("density", 10L, 327670L) / 10; if (density >= 625 && !bflag) ntrec = HIGHDENSITYTREC; break; case 'f': /* output file */ if (popenout != NULL) errx(X_STARTUP, "You cannot use the P and f " "flags together.\n"); tape = optarg; break; case 'h': honorlevel = numarg("honor level", 0L, 10L); break; case 'L': snapdump = 1; break; case 'n': /* notify operators */ notify = 1; break; case 'P': if (tape != NULL) errx(X_STARTUP, "You cannot use the P and f " "flags together.\n"); popenout = optarg; break; case 'r': /* store slightly less data to be friendly to rsync */ if (rsync_friendly < 1) rsync_friendly = 1; break; case 'R': /* store even less data to be friendlier to rsync */ if (rsync_friendly < 2) rsync_friendly = 2; break; case 'S': /* exit after estimating # of tapes */ just_estimate = 1; break; case 's': /* tape size, feet */ tsize = numarg("tape size", 1L, 0L) * 12 * 10; break; case 'T': /* time of last dump */ spcl.c_ddate = unctime(optarg); if (spcl.c_ddate < 0) { (void)fprintf(stderr, "bad time \"%s\"\n", optarg); exit(X_STARTUP); } Tflag = 1; lastlevel = -1; break; case 'u': /* update /etc/dumpdates */ uflag = 1; break; case 'W': /* what to do */ case 'w': lastdump(ch); exit(X_FINOK); /* do nothing else */ default: usage(); } argc -= optind; argv += optind; if (argc < 1) { (void)fprintf(stderr, "Must specify disk or file system\n"); exit(X_STARTUP); } disk = *argv++; argc--; if (argc >= 1) { (void)fprintf(stderr, "Unknown arguments to dump:"); while (argc--) (void)fprintf(stderr, " %s", *argv++); (void)fprintf(stderr, "\n"); exit(X_STARTUP); } if (rsync_friendly && (level > 0)) { (void)fprintf(stderr, "%s %s\n", "rsync friendly options", "can be used only with level 0 dumps."); exit(X_STARTUP); } if (Tflag && uflag) { (void)fprintf(stderr, "You cannot use the T and u flags together.\n"); exit(X_STARTUP); } if (popenout) { tape = "child pipeline process"; } else if (tape == NULL && (tape = getenv("TAPE")) == NULL) tape = _PATH_DEFTAPE; if (strcmp(tape, "-") == 0) { pipeout++; tape = "standard output"; } if (blocksperfile) blocksperfile = blocksperfile / ntrec * ntrec; /* round down */ else if (!unlimited) { /* * Determine how to default tape size and density * * density tape size * 9-track 1600 bpi (160 bytes/.1") 2300 ft. * 9-track 6250 bpi (625 bytes/.1") 2300 ft. * cartridge 8000 bpi (100 bytes/.1") 1700 ft. * (450*4 - slop) * hilit19 hits again: " */ if (density == 0) density = cartridge ? 100 : 160; if (tsize == 0) tsize = cartridge ? 1700L*120L : 2300L*120L; } if (strchr(tape, ':')) { host = tape; tape = strchr(host, ':'); *tape++ = '\0'; #ifdef RDUMP if (strchr(tape, '\n')) { (void)fprintf(stderr, "invalid characters in tape\n"); exit(X_STARTUP); } if (rmthost(host) == 0) exit(X_STARTUP); #else (void)fprintf(stderr, "remote dump not enabled\n"); exit(X_STARTUP); #endif } (void)setuid(getuid()); /* rmthost() is the only reason to be setuid */ if (signal(SIGHUP, SIG_IGN) != SIG_IGN) signal(SIGHUP, sig); if (signal(SIGTRAP, SIG_IGN) != SIG_IGN) signal(SIGTRAP, sig); if (signal(SIGFPE, SIG_IGN) != SIG_IGN) signal(SIGFPE, sig); if (signal(SIGBUS, SIG_IGN) != SIG_IGN) signal(SIGBUS, sig); if (signal(SIGSEGV, SIG_IGN) != SIG_IGN) signal(SIGSEGV, sig); if (signal(SIGTERM, SIG_IGN) != SIG_IGN) signal(SIGTERM, sig); if (signal(SIGINT, interrupt) == SIG_IGN) signal(SIGINT, SIG_IGN); dump_getfstab(); /* /etc/fstab snarfed */ /* * disk can be either the full special file name, * the suffix of the special file name, * the special name missing the leading '/', * the file system name with or without the leading '/'. */ dt = fstabsearch(disk); if (dt != NULL) { disk = rawname(dt->fs_spec); if (disk == NULL) errx(X_STARTUP, "%s: unknown file system", dt->fs_spec); (void)strncpy(spcl.c_dev, dt->fs_spec, NAMELEN); (void)strncpy(spcl.c_filesys, dt->fs_file, NAMELEN); } else { (void)strncpy(spcl.c_dev, disk, NAMELEN); (void)strncpy(spcl.c_filesys, "an unlisted file system", NAMELEN); } spcl.c_dev[NAMELEN-1]='\0'; spcl.c_filesys[NAMELEN-1]='\0'; if ((mntpt = getmntpt(disk, &mntflags)) != 0) { if (mntflags & MNT_RDONLY) { if (snapdump != 0) { msg("WARNING: %s\n", "-L ignored for read-only filesystem."); snapdump = 0; } } else if (snapdump == 0) { msg("WARNING: %s\n", "should use -L when dumping live read-write " "filesystems!"); } else { char snapname[BUFSIZ], snapcmd[BUFSIZ]; snprintf(snapname, sizeof snapname, "%s/.snap", mntpt); if ((stat(snapname, &sb) < 0) || !S_ISDIR(sb.st_mode)) { msg("WARNING: %s %s\n", "-L requested but snapshot location", snapname); msg(" %s: %s\n", "is not a directory", "dump downgraded, -L ignored"); snapdump = 0; } else { snprintf(snapname, sizeof snapname, "%s/.snap/dump_snapshot", mntpt); snprintf(snapcmd, sizeof snapcmd, "%s %s %s", _PATH_MKSNAP_FFS, mntpt, snapname); unlink(snapname); if (system(snapcmd) != 0) errx(X_STARTUP, "Cannot create %s: %s\n", snapname, strerror(errno)); if ((diskfd = open(snapname, O_RDONLY)) < 0) { unlink(snapname); errx(X_STARTUP, "Cannot open %s: %s\n", snapname, strerror(errno)); } unlink(snapname); if (fstat(diskfd, &sb) != 0) err(X_STARTUP, "%s: stat", snapname); spcl.c_date = _time_to_time64(sb.st_mtime); } } } else if (snapdump != 0) { msg("WARNING: Cannot use -L on an unmounted filesystem.\n"); snapdump = 0; } if (snapdump == 0) { if ((diskfd = open(disk, O_RDONLY)) < 0) err(X_STARTUP, "Cannot open %s", disk); if (fstat(diskfd, &sb) != 0) err(X_STARTUP, "%s: stat", disk); if (S_ISDIR(sb.st_mode)) errx(X_STARTUP, "%s: unknown file system", disk); } (void)strcpy(spcl.c_label, "none"); (void)gethostname(spcl.c_host, NAMELEN); spcl.c_level = level; spcl.c_type = TS_TAPE; if (rsync_friendly) { /* don't store real dump times */ spcl.c_date = 0; spcl.c_ddate = 0; } if (spcl.c_date == 0) { tmsg = "the epoch\n"; } else { time_t t = _time64_to_time(spcl.c_date); tmsg = ctime(&t); } msg("Date of this level %d dump: %s", level, tmsg); if (!Tflag && (!rsync_friendly)) getdumptime(); /* /etc/dumpdates snarfed */ if (spcl.c_ddate == 0) { tmsg = "the epoch\n"; } else { time_t t = _time64_to_time(spcl.c_ddate); tmsg = ctime(&t); } if (lastlevel < 0) msg("Date of last (level unknown) dump: %s", tmsg); else msg("Date of last level %d dump: %s", lastlevel, tmsg); msg("Dumping %s%s ", snapdump ? "snapshot of ": "", disk); if (dt != NULL) msgtail("(%s) ", dt->fs_file); if (host) msgtail("to %s on host %s\n", tape, host); else msgtail("to %s\n", tape); sync(); sblock = (struct fs *)sblock_buf; for (i = 0; sblock_try[i] != -1; i++) { sblock->fs_fsize = SBLOCKSIZE; /* needed in bread */ bread(sblock_try[i] >> dev_bshift, (char *) sblock, SBLOCKSIZE); if ((sblock->fs_magic == FS_UFS1_MAGIC || (sblock->fs_magic == FS_UFS2_MAGIC && sblock->fs_sblockloc == sblock_try[i])) && sblock->fs_bsize <= MAXBSIZE && sblock->fs_bsize >= sizeof(struct fs)) break; } if (sblock_try[i] == -1) quit("Cannot find file system superblock\n"); dev_bsize = sblock->fs_fsize / fsbtodb(sblock, 1); dev_bshift = ffs(dev_bsize) - 1; if (dev_bsize != (1 << dev_bshift)) quit("dev_bsize (%ld) is not a power of 2", dev_bsize); tp_bshift = ffs(TP_BSIZE) - 1; if (TP_BSIZE != (1 << tp_bshift)) quit("TP_BSIZE (%d) is not a power of 2", TP_BSIZE); maxino = sblock->fs_ipg * sblock->fs_ncg; mapsize = roundup(howmany(maxino, CHAR_BIT), TP_BSIZE); usedinomap = (char *)calloc((unsigned) mapsize, sizeof(char)); dumpdirmap = (char *)calloc((unsigned) mapsize, sizeof(char)); dumpinomap = (char *)calloc((unsigned) mapsize, sizeof(char)); tapesize = 3 * (howmany(mapsize * sizeof(char), TP_BSIZE) + 1); nonodump = spcl.c_level < honorlevel; passno = 1; setproctitle("%s: pass 1: regular files", disk); msg("mapping (Pass I) [regular files]\n"); anydirskipped = mapfiles(maxino, &tapesize); passno = 2; setproctitle("%s: pass 2: directories", disk); msg("mapping (Pass II) [directories]\n"); while (anydirskipped) { anydirskipped = mapdirs(maxino, &tapesize); } if (pipeout || unlimited) { tapesize += 10; /* 10 trailer blocks */ msg("estimated %ld tape blocks.\n", tapesize); } else { double fetapes; if (blocksperfile) fetapes = (double) tapesize / blocksperfile; else if (cartridge) { /* Estimate number of tapes, assuming streaming stops at the end of each block written, and not in mid-block. Assume no erroneous blocks; this can be compensated for with an artificially low tape size. */ fetapes = ( (double) tapesize /* blocks */ * TP_BSIZE /* bytes/block */ * (1.0/density) /* 0.1" / byte " */ + (double) tapesize /* blocks */ * (1.0/ntrec) /* streaming-stops per block */ * 15.48 /* 0.1" / streaming-stop " */ ) * (1.0 / tsize ); /* tape / 0.1" " */ } else { /* Estimate number of tapes, for old fashioned 9-track tape */ int tenthsperirg = (density == 625) ? 3 : 7; fetapes = ( (double) tapesize /* blocks */ * TP_BSIZE /* bytes / block */ * (1.0/density) /* 0.1" / byte " */ + (double) tapesize /* blocks */ * (1.0/ntrec) /* IRG's / block */ * tenthsperirg /* 0.1" / IRG " */ ) * (1.0 / tsize ); /* tape / 0.1" " */ } etapes = fetapes; /* truncating assignment */ etapes++; /* count the dumped inodes map on each additional tape */ tapesize += (etapes - 1) * (howmany(mapsize * sizeof(char), TP_BSIZE) + 1); tapesize += etapes + 10; /* headers + 10 trailer blks */ msg("estimated %ld tape blocks on %3.2f tape(s).\n", tapesize, fetapes); } /* * If the user only wants an estimate of the number of * tapes, exit now. */ if (just_estimate) exit(0); /* * Allocate tape buffer. */ if (!alloctape()) quit( "can't allocate tape buffers - try a smaller blocking factor.\n"); startnewtape(1); (void)time((time_t *)&(tstart_writing)); dumpmap(usedinomap, TS_CLRI, maxino - 1); passno = 3; setproctitle("%s: pass 3: directories", disk); msg("dumping (Pass III) [directories]\n"); dirty = 0; /* XXX just to get gcc to shut up */ for (map = dumpdirmap, ino = 1; ino < maxino; ino++) { if (((ino - 1) % CHAR_BIT) == 0) /* map is offset by 1 */ dirty = *map++; else dirty >>= 1; if ((dirty & 1) == 0) continue; /* * Skip directory inodes deleted and maybe reallocated */ dp = getino(ino, &mode); if (mode != IFDIR) continue; (void)dumpino(dp, ino); } passno = 4; setproctitle("%s: pass 4: regular files", disk); msg("dumping (Pass IV) [regular files]\n"); for (map = dumpinomap, ino = 1; ino < maxino; ino++) { if (((ino - 1) % CHAR_BIT) == 0) /* map is offset by 1 */ dirty = *map++; else dirty >>= 1; if ((dirty & 1) == 0) continue; /* * Skip inodes deleted and reallocated as directories. */ dp = getino(ino, &mode); if (mode == IFDIR) continue; (void)dumpino(dp, ino); } (void)time((time_t *)&(tend_writing)); spcl.c_type = TS_END; for (i = 0; i < ntrec; i++) writeheader(maxino - 1); if (pipeout) msg("DUMP: %jd tape blocks\n", (intmax_t)spcl.c_tapea); else msg("DUMP: %jd tape blocks on %d volume%s\n", (intmax_t)spcl.c_tapea, spcl.c_volume, (spcl.c_volume == 1) ? "" : "s"); /* report dump performance, avoid division through zero */ if (tend_writing - tstart_writing == 0) msg("finished in less than a second\n"); else msg("finished in %jd seconds, throughput %jd KBytes/sec\n", (intmax_t)tend_writing - tstart_writing, (intmax_t)(spcl.c_tapea / (tend_writing - tstart_writing))); putdumptime(); trewind(); broadcast("DUMP IS DONE!\a\a\n"); msg("DUMP IS DONE\n"); Exit(X_FINOK); /* NOTREACHED */ }
int main(int argc, char *argv[]) { ino_t ino; int dirty; union dinode *dp; struct fstab *dt; struct statvfs *mntinfo, fsbuf; char *map, *cp; int ch; int i, anydirskipped, bflag = 0, Tflag = 0, Fflag = 0, honorlevel = 1; int snap_internal = 0; ino_t maxino; time_t tnow, date; int dirc; char *mountpoint; int just_estimate = 0; char labelstr[LBLSIZE]; char buf[MAXPATHLEN], rbuf[MAXPATHLEN]; char *new_time_format; char *snap_backup = NULL; spcl.c_date = 0; (void)time(&tnow); spcl.c_date = tnow; tzset(); /* set up timezone for strftime */ if ((new_time_format = getenv("TIMEFORMAT")) != NULL) time_string = new_time_format; tsize = 0; /* Default later, based on 'c' option for cart tapes */ if ((tape = getenv("TAPE")) == NULL) tape = _PATH_DEFTAPE; dumpdates = _PATH_DUMPDATES; temp = _PATH_DTMP; strcpy(labelstr, "none"); /* XXX safe strcpy. */ if (TP_BSIZE / DEV_BSIZE == 0 || TP_BSIZE % DEV_BSIZE != 0) quit("TP_BSIZE must be a multiple of DEV_BSIZE\n"); level = '0'; timestamp = 0; if (argc < 2) usage(); obsolete(&argc, &argv); while ((ch = getopt(argc, argv, "0123456789aB:b:cd:eFf:h:ik:l:L:nr:s:StT:uWwx:X")) != -1) switch (ch) { /* dump level */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': level = ch; break; case 'a': /* `auto-size', Write to EOM. */ unlimited = 1; break; case 'B': /* blocks per output file */ blocksperfile = numarg("blocks per file", 1L, 0L); break; case 'b': /* blocks per tape write */ ntrec = numarg("blocks per write", 1L, 1000L); bflag = 1; break; case 'c': /* Tape is cart. not 9-track */ cartridge = 1; break; case 'd': /* density, in bits per inch */ density = numarg("density", 10L, 327670L) / 10; if (density >= 625 && !bflag) ntrec = HIGHDENSITYTREC; break; case 'e': /* eject full tapes */ eflag = 1; break; case 'F': /* files-to-dump is an fs image */ Fflag = 1; break; case 'f': /* output file */ tape = optarg; break; case 'h': honorlevel = numarg("honor level", 0L, 10L); break; case 'i': /* "true incremental" regardless level */ level = 'i'; trueinc = 1; break; case 'k': readblksize = numarg("read block size", 0, 64) * 1024; break; case 'l': /* autoload after eject full tapes */ eflag = 1; lflag = numarg("timeout (in seconds)", 1, 0); break; case 'L': /* * Note that although there are LBLSIZE characters, * the last must be '\0', so the limit on strlen() * is really LBLSIZE-1. */ if (strlcpy(labelstr, optarg, sizeof(labelstr)) >= sizeof(labelstr)) { msg( "WARNING Label `%s' is larger than limit of %lu characters.\n", optarg, (unsigned long)sizeof(labelstr) - 1); msg("WARNING: Using truncated label `%s'.\n", labelstr); } break; case 'n': /* notify operators */ notify = 1; break; case 'r': /* read cache size */ readcache = numarg("read cache size", 0, 512); break; case 's': /* tape size, feet */ tsize = numarg("tape size", 1L, 0L) * 12 * 10; break; case 'S': /* exit after estimating # of tapes */ just_estimate = 1; break; case 't': timestamp = 1; break; case 'T': /* time of last dump */ spcl.c_ddate = unctime(optarg); if (spcl.c_ddate < 0) { (void)fprintf(stderr, "bad time \"%s\"\n", optarg); exit(X_STARTUP); } Tflag = 1; lastlevel = '?'; break; case 'u': /* update /etc/dumpdates */ uflag = 1; break; case 'W': /* what to do */ case 'w': lastdump(ch); exit(X_FINOK); /* do nothing else */ case 'x': snap_backup = optarg; break; case 'X': snap_internal = 1; break; default: usage(); } argc -= optind; argv += optind; if (argc < 1) { (void)fprintf(stderr, "Must specify disk or image, or file list\n"); exit(X_STARTUP); } /* * determine if disk is a subdirectory, and setup appropriately */ getfstab(); /* /etc/fstab snarfed */ disk = NULL; disk_dev = NULL; mountpoint = NULL; dirc = 0; for (i = 0; i < argc; i++) { struct stat sb; int error; error = lstat(argv[i], &sb); if (Fflag || (!error && (S_ISCHR(sb.st_mode) || S_ISBLK(sb.st_mode)))) { if (error) quit("Cannot stat %s: %s\n", argv[i], strerror(errno)); disk = argv[i]; multicheck: if (dirc != 0) quit("Can't dump a disk or image at the same time as a file list\n"); break; } if ((dt = fstabsearch(argv[i])) != NULL) { disk = argv[i]; mountpoint = xstrdup(dt->fs_file); goto multicheck; } if (statvfs(argv[i], &fsbuf) == -1) quit("Cannot statvfs %s: %s\n", argv[i], strerror(errno)); disk = fsbuf.f_mntfromname; if (strcmp(argv[i], fsbuf.f_mntonname) == 0) goto multicheck; if (mountpoint == NULL) { mountpoint = xstrdup(fsbuf.f_mntonname); if (uflag) { msg("Ignoring u flag for subdir dump\n"); uflag = 0; } if (level > '0') { msg("Subdir dump is done at level 0\n"); level = '0'; } msg("Dumping sub files/directories from %s\n", mountpoint); } else { if (strcmp(mountpoint, fsbuf.f_mntonname) != 0) quit("%s is not on %s\n", argv[i], mountpoint); } msg("Dumping file/directory %s\n", argv[i]); dirc++; } if (mountpoint) free(mountpoint); if (dirc == 0) { argv++; if (argc != 1) { (void)fprintf(stderr, "Excess arguments to dump:"); while (--argc) (void)fprintf(stderr, " %s", *argv++); (void)fprintf(stderr, "\n"); exit(X_STARTUP); } } if (Tflag && uflag) { (void)fprintf(stderr, "You cannot use the T and u flags together.\n"); exit(X_STARTUP); } if (strcmp(tape, "-") == 0) { pipeout++; tape = "standard output"; } if (blocksperfile) blocksperfile = blocksperfile / ntrec * ntrec; /* round down */ else if (!unlimited) { /* * Determine how to default tape size and density * * density tape size * 9-track 1600 bpi (160 bytes/.1") 2300 ft. * 9-track 6250 bpi (625 bytes/.1") 2300 ft. * cartridge 8000 bpi (100 bytes/.1") 1700 ft. * (450*4 - slop) */ if (density == 0) density = cartridge ? 100 : 160; if (tsize == 0) tsize = cartridge ? 1700L*120L : 2300L*120L; } if ((cp = strchr(tape, ':')) != NULL) { host = tape; /* This is fine, because all the const strings don't have : */ *cp++ = '\0'; tape = cp; #ifdef RDUMP if (rmthost(host) == 0) exit(X_STARTUP); #else (void)fprintf(stderr, "remote dump not enabled\n"); exit(X_STARTUP); #endif } if (signal(SIGHUP, SIG_IGN) != SIG_IGN) signal(SIGHUP, sig); if (signal(SIGTRAP, SIG_IGN) != SIG_IGN) signal(SIGTRAP, sig); if (signal(SIGFPE, SIG_IGN) != SIG_IGN) signal(SIGFPE, sig); if (signal(SIGBUS, SIG_IGN) != SIG_IGN) signal(SIGBUS, sig); #if 0 if (signal(SIGSEGV, SIG_IGN) != SIG_IGN) signal(SIGSEGV, sig); #endif if (signal(SIGTERM, SIG_IGN) != SIG_IGN) signal(SIGTERM, sig); if (signal(SIGINT, interrupt) == SIG_IGN) signal(SIGINT, SIG_IGN); /* * disk can be either the full special file name, or * the file system name. */ mountpoint = NULL; mntinfo = mntinfosearch(disk); if ((dt = fstabsearch(disk)) != NULL) { if (getfsspecname(buf, sizeof(buf), dt->fs_spec) == NULL) quit("%s (%s)", buf, strerror(errno)); if (getdiskrawname(rbuf, sizeof(rbuf), buf) == NULL) quit("Can't get disk raw name for `%s' (%s)", buf, strerror(errno)); disk = rbuf; mountpoint = dt->fs_file; msg("Found %s on %s in %s\n", disk, mountpoint, _PATH_FSTAB); } else if (mntinfo != NULL) { if (getdiskrawname(rbuf, sizeof(rbuf), mntinfo->f_mntfromname) == NULL) quit("Can't get disk raw name for `%s' (%s)", mntinfo->f_mntfromname, strerror(errno)); disk = rbuf; mountpoint = mntinfo->f_mntonname; msg("Found %s on %s in mount table\n", disk, mountpoint); } if (mountpoint != NULL) { if (dirc != 0) (void)snprintf(spcl.c_filesys, sizeof(spcl.c_filesys), "a subset of %s", mountpoint); else (void)strlcpy(spcl.c_filesys, mountpoint, sizeof(spcl.c_filesys)); } else if (Fflag) { (void)strlcpy(spcl.c_filesys, "a file system image", sizeof(spcl.c_filesys)); } else { (void)strlcpy(spcl.c_filesys, "an unlisted file system", sizeof(spcl.c_filesys)); } (void)strlcpy(spcl.c_dev, disk, sizeof(spcl.c_dev)); (void)strlcpy(spcl.c_label, labelstr, sizeof(spcl.c_label)); (void)gethostname(spcl.c_host, sizeof(spcl.c_host)); spcl.c_host[sizeof(spcl.c_host) - 1] = '\0'; if ((snap_backup != NULL || snap_internal) && mntinfo == NULL) { msg("WARNING: Cannot use -x or -X on unmounted file system.\n"); snap_backup = NULL; snap_internal = 0; } #ifdef DUMP_LFS sync(); if (snap_backup != NULL || snap_internal) { if (lfs_wrap_stop(mountpoint) < 0) { msg("Cannot stop writing on %s\n", mountpoint); exit(X_STARTUP); } } if ((diskfd = open(disk, O_RDONLY)) < 0) { msg("Cannot open %s\n", disk); exit(X_STARTUP); } disk_dev = disk; #else /* ! DUMP_LFS */ if (snap_backup != NULL || snap_internal) { diskfd = snap_open(mntinfo->f_mntonname, snap_backup, &tnow, &disk_dev); if (diskfd < 0) { msg("Cannot open snapshot of %s\n", mntinfo->f_mntonname); exit(X_STARTUP); } spcl.c_date = tnow; } else { if ((diskfd = open(disk, O_RDONLY)) < 0) { msg("Cannot open %s\n", disk); exit(X_STARTUP); } disk_dev = disk; } sync(); #endif /* ! DUMP_LFS */ needswap = fs_read_sblock(sblock_buf); /* true incremental is always a level 10 dump */ spcl.c_level = trueinc? iswap32(10): iswap32(level - '0'); spcl.c_type = iswap32(TS_TAPE); spcl.c_date = iswap32(spcl.c_date); spcl.c_ddate = iswap32(spcl.c_ddate); if (!Tflag) getdumptime(); /* /etc/dumpdates snarfed */ date = iswap32(spcl.c_date); msg("Date of this level %c dump: %s", level, spcl.c_date == 0 ? "the epoch\n" : ctime(&date)); date = iswap32(spcl.c_ddate); msg("Date of last level %c dump: %s", lastlevel, spcl.c_ddate == 0 ? "the epoch\n" : ctime(&date)); msg("Dumping "); if (snap_backup != NULL || snap_internal) msgtail("a snapshot of "); if (dirc != 0) msgtail("a subset of "); msgtail("%s (%s) ", disk, spcl.c_filesys); if (host) msgtail("to %s on host %s\n", tape, host); else msgtail("to %s\n", tape); msg("Label: %s\n", labelstr); ufsib = fs_parametrize(); dev_bshift = ffs(dev_bsize) - 1; if (dev_bsize != (1 << dev_bshift)) quit("dev_bsize (%ld) is not a power of 2", dev_bsize); tp_bshift = ffs(TP_BSIZE) - 1; if (TP_BSIZE != (1 << tp_bshift)) quit("TP_BSIZE (%d) is not a power of 2", TP_BSIZE); maxino = fs_maxino(); mapsize = roundup(howmany(maxino, NBBY), TP_BSIZE); usedinomap = (char *)xcalloc((unsigned) mapsize, sizeof(char)); dumpdirmap = (char *)xcalloc((unsigned) mapsize, sizeof(char)); dumpinomap = (char *)xcalloc((unsigned) mapsize, sizeof(char)); tapesize = 3 * (howmany(mapsize * sizeof(char), TP_BSIZE) + 1); nonodump = iswap32(spcl.c_level) < honorlevel; initcache(readcache, readblksize); (void)signal(SIGINFO, statussig); msg("mapping (Pass I) [regular files]\n"); anydirskipped = mapfiles(maxino, &tapesize, mountpoint, (dirc ? argv : NULL)); msg("mapping (Pass II) [directories]\n"); while (anydirskipped) { anydirskipped = mapdirs(maxino, &tapesize); } if (pipeout || unlimited) { tapesize += 10; /* 10 trailer blocks */ msg("estimated %llu tape blocks.\n", (unsigned long long)tapesize); } else { double fetapes; if (blocksperfile) fetapes = (double) tapesize / blocksperfile; else if (cartridge) { /* Estimate number of tapes, assuming streaming stops at the end of each block written, and not in mid-block. Assume no erroneous blocks; this can be compensated for with an artificially low tape size. */ fetapes = ( (double) tapesize /* blocks */ * TP_BSIZE /* bytes/block */ * (1.0/density) /* 0.1" / byte */ + (double) tapesize /* blocks */ * (1.0/ntrec) /* streaming-stops per block */ * 15.48 /* 0.1" / streaming-stop */ ) * (1.0 / tsize ); /* tape / 0.1" */ } else { /* Estimate number of tapes, for old fashioned 9-track tape */ int tenthsperirg = (density == 625) ? 3 : 7; fetapes = ( tapesize /* blocks */ * TP_BSIZE /* bytes / block */ * (1.0/density) /* 0.1" / byte */ + tapesize /* blocks */ * (1.0/ntrec) /* IRG's / block */ * tenthsperirg /* 0.1" / IRG */ ) * (1.0 / tsize ); /* tape / 0.1" */ } etapes = fetapes; /* truncating assignment */ etapes++; /* count the dumped inodes map on each additional tape */ tapesize += (etapes - 1) * (howmany(mapsize * sizeof(char), TP_BSIZE) + 1); tapesize += etapes + 10; /* headers + 10 trailer blks */ msg("estimated %llu tape blocks on %3.2f tape(s).\n", (unsigned long long)tapesize, fetapes); } /* * If the user only wants an estimate of the number of * tapes, exit now. */ if (just_estimate) exit(X_FINOK); /* * Allocate tape buffer. */ if (!alloctape()) quit("can't allocate tape buffers - try a smaller blocking factor.\n"); startnewtape(1); (void)time((time_t *)&(tstart_writing)); xferrate = 0; dumpmap(usedinomap, TS_CLRI, maxino - 1); msg("dumping (Pass III) [directories]\n"); dirty = 0; /* XXX just to get gcc to shut up */ for (map = dumpdirmap, ino = 1; ino < maxino; ino++) { if (((ino - 1) % NBBY) == 0) /* map is offset by 1 */ dirty = *map++; else dirty >>= 1; if ((dirty & 1) == 0) continue; /* * Skip directory inodes deleted and maybe reallocated */ dp = getino(ino); if ((DIP(dp, mode) & IFMT) != IFDIR) continue; (void)dumpino(dp, ino); } msg("dumping (Pass IV) [regular files]\n"); for (map = dumpinomap, ino = 1; ino < maxino; ino++) { int mode; if (((ino - 1) % NBBY) == 0) /* map is offset by 1 */ dirty = *map++; else dirty >>= 1; if ((dirty & 1) == 0) continue; /* * Skip inodes deleted and reallocated as directories. */ dp = getino(ino); mode = DIP(dp, mode) & IFMT; if (mode == IFDIR) continue; (void)dumpino(dp, ino); } spcl.c_type = iswap32(TS_END); for (i = 0; i < ntrec; i++) writeheader(maxino - 1); if (pipeout) msg("%lld tape blocks\n",(long long)iswap64(spcl.c_tapea)); else msg("%lld tape blocks on %d volume%s\n", (long long)iswap64(spcl.c_tapea), iswap32(spcl.c_volume), (iswap32(spcl.c_volume) == 1) ? "" : "s"); tnow = do_stats(); date = iswap32(spcl.c_date); msg("Date of this level %c dump: %s", level, spcl.c_date == 0 ? "the epoch\n" : ctime(&date)); msg("Date this dump completed: %s", ctime(&tnow)); msg("Average transfer rate: %d KB/s\n", xferrate / tapeno); putdumptime(); trewind(0); broadcast("DUMP IS DONE!\a\a\n"); #ifdef DUMP_LFS lfs_wrap_go(); #endif /* DUMP_LFS */ msg("DUMP IS DONE\n"); Exit(X_FINOK); /* NOTREACHED */ exit(X_FINOK); /* XXX: to satisfy gcc */ }
/* * Dump passes 3 and 4. * * Dump the contents of an inode to tape. */ void dumpino(union dinode *dp, ino_t ino) { int ind_level, cnt; off_t size; char buf[TP_BSIZE]; if (newtape) { newtape = 0; dumpmap(dumpinomap, TS_BITS, ino); } CLRINO(ino, dumpinomap); if (sblock->fs_magic == FS_UFS1_MAGIC) { spcl.c_mode = dp->dp1.di_mode; spcl.c_size = dp->dp1.di_size; spcl.c_old_atime = (time_t)dp->dp1.di_atime; spcl.c_atime = dp->dp1.di_atime; spcl.c_atimensec = dp->dp1.di_atimensec; spcl.c_old_mtime = (time_t)dp->dp1.di_mtime; spcl.c_mtime = dp->dp1.di_mtime; spcl.c_mtimensec = dp->dp1.di_mtimensec; spcl.c_birthtime = 0; spcl.c_birthtimensec = 0; spcl.c_rdev = dp->dp1.di_rdev; spcl.c_file_flags = dp->dp1.di_flags; spcl.c_uid = dp->dp1.di_uid; spcl.c_gid = dp->dp1.di_gid; } else { spcl.c_mode = dp->dp2.di_mode; spcl.c_size = dp->dp2.di_size; spcl.c_atime = dp->dp2.di_atime; spcl.c_atimensec = dp->dp2.di_atimensec; spcl.c_mtime = dp->dp2.di_mtime; spcl.c_mtimensec = dp->dp2.di_mtimensec; spcl.c_birthtime = dp->dp2.di_birthtime; spcl.c_birthtimensec = dp->dp2.di_birthnsec; spcl.c_rdev = dp->dp2.di_rdev; spcl.c_file_flags = dp->dp2.di_flags; spcl.c_uid = dp->dp2.di_uid; spcl.c_gid = dp->dp2.di_gid; } spcl.c_type = TS_INODE; spcl.c_count = 0; switch (DIP(dp, di_mode) & S_IFMT) { case 0: /* * Freed inode. */ return; case IFLNK: /* * Check for short symbolic link. */ if (DIP(dp, di_size) > 0 && #ifdef FS_44INODEFMT (DIP(dp, di_size) < sblock->fs_maxsymlinklen || (sblock->fs_maxsymlinklen == 0 && DIP(dp, di_blocks) == 0))) { #else DIP(dp, di_blocks) == 0) { #endif void *shortlink; spcl.c_addr[0] = 1; spcl.c_count = 1; writeheader(ino); if (sblock->fs_magic == FS_UFS1_MAGIC) shortlink = dp->dp1.di_shortlink; else shortlink = dp->dp2.di_shortlink; memcpy(buf, shortlink, DIP(dp, di_size)); buf[DIP(dp, di_size)] = '\0'; writerec(buf, 0); return; } /* FALLTHROUGH */ case IFDIR: case IFREG: if (DIP(dp, di_size) > 0) break; /* FALLTHROUGH */ case IFIFO: case IFSOCK: case IFCHR: case IFBLK: writeheader(ino); return; default: msg("Warning: undefined file type 0%o\n", DIP(dp, di_mode) & IFMT); return; } if (DIP(dp, di_size) > NDADDR * sblock->fs_bsize) cnt = NDADDR * sblock->fs_frag; else cnt = howmany(DIP(dp, di_size), sblock->fs_fsize); if (sblock->fs_magic == FS_UFS1_MAGIC) ufs1_blksout(&dp->dp1.di_db[0], cnt, ino); else ufs2_blksout(&dp->dp2.di_db[0], cnt, ino); if ((size = DIP(dp, di_size) - NDADDR * sblock->fs_bsize) <= 0) return; for (ind_level = 0; ind_level < NIADDR; ind_level++) { dmpindir(ino, DIP(dp, di_ib[ind_level]), ind_level, &size); if (size <= 0) return; } }
int main(int argc, char **argv) { struct stat sb; ufs1_ino_t ino; int dirty; struct ufs1_dinode *dp; struct fstab *dt; char *map; int ch; int i, anydirskipped, bflag = 0, Tflag = 0, honorlevel = 1; int just_estimate = 0; ufs1_ino_t maxino; spcl.c_date = 0; time((time_t *)&spcl.c_date); tsize = 0; /* Default later, based on 'c' option for cart tapes */ if ((tape = getenv("TAPE")) == NULL) tape = _PATH_DEFTAPE; dumpdates = _PATH_DUMPDATES; temp = _PATH_DTMP; if (TP_BSIZE / DEV_BSIZE == 0 || TP_BSIZE % DEV_BSIZE != 0) quit("TP_BSIZE must be a multiple of DEV_BSIZE\n"); level = '0'; if (argc < 2) usage(); obsolete(&argc, &argv); #ifdef KERBEROS #define optstring "0123456789aB:b:cd:f:h:kns:ST:uWwD:C:" #else #define optstring "0123456789aB:b:cd:f:h:ns:ST:uWwD:C:" #endif while ((ch = getopt(argc, argv, optstring)) != -1) #undef optstring switch (ch) { /* dump level */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': level = ch; break; case 'a': /* `auto-size', Write to EOM. */ unlimited = 1; break; case 'B': /* blocks per output file */ blocksperfile = numarg("number of blocks per file", 1L, 0L); break; case 'b': /* blocks per tape write */ ntrec = numarg("number of blocks per write", 1L, 1000L); break; case 'c': /* Tape is cart. not 9-track */ cartridge = 1; break; case 'd': /* density, in bits per inch */ density = numarg("density", 10L, 327670L) / 10; if (density >= 625 && !bflag) ntrec = HIGHDENSITYTREC; break; case 'f': /* output file */ tape = optarg; break; case 'D': dumpdates = optarg; break; case 'C': cachesize = numarg("cachesize", 0, 0) * 1024 * 1024; break; case 'h': honorlevel = numarg("honor level", 0L, 10L); break; #ifdef KERBEROS case 'k': dokerberos = 1; break; #endif case 'n': /* notify operators */ notify = 1; break; case 's': /* tape size, feet */ tsize = numarg("tape size", 1L, 0L) * 12 * 10; break; case 'S': /* exit after estimating # of tapes */ just_estimate = 1; break; case 'T': /* time of last dump */ spcl.c_ddate = unctime(optarg); if (spcl.c_ddate < 0) { fprintf(stderr, "bad time \"%s\"\n", optarg); exit(X_STARTUP); } Tflag = 1; lastlevel = '?'; break; case 'u': /* update /etc/dumpdates */ uflag = 1; break; case 'W': /* what to do */ case 'w': lastdump(ch); exit(X_FINOK); /* do nothing else */ default: usage(); } argc -= optind; argv += optind; if (argc < 1) { fprintf(stderr, "Must specify disk or filesystem\n"); exit(X_STARTUP); } disk = *argv++; argc--; if (argc >= 1) { fprintf(stderr, "Unknown arguments to dump:"); while (argc--) fprintf(stderr, " %s", *argv++); fprintf(stderr, "\n"); exit(X_STARTUP); } if (Tflag && uflag) { fprintf(stderr, "You cannot use the T and u flags together.\n"); exit(X_STARTUP); } if (strcmp(tape, "-") == 0) { pipeout++; tape = "standard output"; } if (blocksperfile) blocksperfile = blocksperfile / ntrec * ntrec; /* round down */ else if (!unlimited) { /* * Determine how to default tape size and density * * density tape size * 9-track 1600 bpi (160 bytes/.1") 2300 ft. * 9-track 6250 bpi (625 bytes/.1") 2300 ft. * cartridge 8000 bpi (100 bytes/.1") 1700 ft. * (450*4 - slop) * hilit19 hits again: " */ if (density == 0) density = cartridge ? 100 : 160; if (tsize == 0) tsize = cartridge ? 1700L*120L : 2300L*120L; } if (strchr(tape, ':')) { char *ehost; if ((host = strdup(tape)) == NULL) err(1, "strdup failed"); ehost = strchr(host, ':'); *ehost++ = '\0'; tape = ehost; #ifdef RDUMP if (strchr(tape, '\n')) { fprintf(stderr, "invalid characters in tape\n"); exit(X_STARTUP); } if (rmthost(host) == 0) exit(X_STARTUP); #else fprintf(stderr, "remote dump not enabled\n"); exit(X_STARTUP); #endif } setuid(getuid()); /* rmthost() is the only reason to be setuid */ if (signal(SIGHUP, SIG_IGN) != SIG_IGN) signal(SIGHUP, sig); if (signal(SIGTRAP, SIG_IGN) != SIG_IGN) signal(SIGTRAP, sig); if (signal(SIGFPE, SIG_IGN) != SIG_IGN) signal(SIGFPE, sig); if (signal(SIGBUS, SIG_IGN) != SIG_IGN) signal(SIGBUS, sig); if (signal(SIGSEGV, SIG_IGN) != SIG_IGN) signal(SIGSEGV, sig); if (signal(SIGTERM, SIG_IGN) != SIG_IGN) signal(SIGTERM, sig); if (signal(SIGINT, interrupt) == SIG_IGN) signal(SIGINT, SIG_IGN); dump_getfstab(); /* /etc/fstab snarfed */ /* * disk can be either the full special file name, * the suffix of the special file name, * the special name missing the leading '/', * the file system name with or without the leading '/'. */ dt = fstabsearch(disk); if (dt != NULL) { disk = rawname(dt->fs_spec); strncpy(spcl.c_dev, dt->fs_spec, NAMELEN); strncpy(spcl.c_filesys, dt->fs_file, NAMELEN); } else { strncpy(spcl.c_dev, disk, NAMELEN); strncpy(spcl.c_filesys, "an unlisted file system", NAMELEN); } spcl.c_dev[NAMELEN-1]='\0'; spcl.c_filesys[NAMELEN-1]='\0'; strcpy(spcl.c_label, "none"); gethostname(spcl.c_host, NAMELEN); spcl.c_level = level - '0'; spcl.c_type = TS_TAPE; if (!Tflag) getdumptime(); /* /etc/dumpdates snarfed */ msg("Date of this level %c dump: %s", level, spcl.c_date == 0 ? "the epoch\n" : ctime((const time_t *)&spcl.c_date)); msg("Date of last level %c dump: %s", lastlevel, spcl.c_ddate == 0 ? "the epoch\n" : ctime((const time_t *)&spcl.c_ddate)); msg("Dumping %s ", disk); if (dt != NULL) msgtail("(%s) ", dt->fs_file); if (host) msgtail("to %s on host %s\n", tape, host); else msgtail("to %s\n", tape); if ((diskfd = open(disk, O_RDONLY)) < 0) err(X_STARTUP, "Cannot open %s", disk); if (fstat(diskfd, &sb) != 0) err(X_STARTUP, "%s: stat", disk); if (S_ISDIR(sb.st_mode)) errx(X_STARTUP, "%s: unknown file system", disk); sync(); sblock = (struct fs *)sblock_buf; bread(SBOFF, (char *) sblock, SBSIZE); if (sblock->fs_magic != FS_MAGIC) quit("bad sblock magic number\n"); dev_bsize = sblock->fs_fsize / fsbtodb(sblock, 1); dev_bshift = ffs(dev_bsize) - 1; if (dev_bsize != (1 << dev_bshift)) quit("dev_bsize (%ld) is not a power of 2", dev_bsize); tp_bshift = ffs(TP_BSIZE) - 1; if (TP_BSIZE != (1 << tp_bshift)) quit("TP_BSIZE (%d) is not a power of 2", TP_BSIZE); #ifdef FS_44INODEFMT if (sblock->fs_inodefmt >= FS_44INODEFMT) spcl.c_flags |= DR_NEWINODEFMT; #endif maxino = sblock->fs_ipg * sblock->fs_ncg; mapsize = roundup(howmany(maxino, NBBY), TP_BSIZE); usedinomap = (char *)calloc((unsigned) mapsize, sizeof(char)); dumpdirmap = (char *)calloc((unsigned) mapsize, sizeof(char)); dumpinomap = (char *)calloc((unsigned) mapsize, sizeof(char)); tapesize = 3 * (howmany(mapsize * sizeof(char), TP_BSIZE) + 1); nonodump = spcl.c_level < honorlevel; passno = 1; setproctitle("%s: pass 1: regular files", disk); msg("mapping (Pass I) [regular files]\n"); anydirskipped = mapfiles(maxino, &tapesize); passno = 2; setproctitle("%s: pass 2: directories", disk); msg("mapping (Pass II) [directories]\n"); while (anydirskipped) { anydirskipped = mapdirs(maxino, &tapesize); } if (pipeout || unlimited) { tapesize += 10; /* 10 trailer blocks */ msg("estimated %ld tape blocks.\n", tapesize); } else { double fetapes; if (blocksperfile) fetapes = (double) tapesize / blocksperfile; else if (cartridge) { /* Estimate number of tapes, assuming streaming stops at the end of each block written, and not in mid-block. Assume no erroneous blocks; this can be compensated for with an artificially low tape size. */ fetapes = ( (double) tapesize /* blocks */ * TP_BSIZE /* bytes/block */ * (1.0/density) /* 0.1" / byte " */ + (double) tapesize /* blocks */ * (1.0/ntrec) /* streaming-stops per block */ * 15.48 /* 0.1" / streaming-stop " */ ) * (1.0 / tsize ); /* tape / 0.1" " */ } else { /* Estimate number of tapes, for old fashioned 9-track tape */ int tenthsperirg = (density == 625) ? 3 : 7; fetapes = ( (double) tapesize /* blocks */ * TP_BSIZE /* bytes / block */ * (1.0/density) /* 0.1" / byte " */ + (double) tapesize /* blocks */ * (1.0/ntrec) /* IRG's / block */ * tenthsperirg /* 0.1" / IRG " */ ) * (1.0 / tsize ); /* tape / 0.1" " */ } etapes = fetapes; /* truncating assignment */ etapes++; /* count the dumped inodes map on each additional tape */ tapesize += (etapes - 1) * (howmany(mapsize * sizeof(char), TP_BSIZE) + 1); tapesize += etapes + 10; /* headers + 10 trailer blks */ msg("estimated %ld tape blocks on %3.2f tape(s).\n", tapesize, fetapes); } /* * If the user only wants an estimate of the number of * tapes, exit now. */ if (just_estimate) exit(0); /* * Allocate tape buffer. */ if (!alloctape()) quit( "can't allocate tape buffers - try a smaller blocking factor.\n"); startnewtape(1); time((time_t *)&(tstart_writing)); dumpmap(usedinomap, TS_CLRI, maxino - 1); passno = 3; setproctitle("%s: pass 3: directories", disk); msg("dumping (Pass III) [directories]\n"); dirty = 0; /* XXX just to get gcc to shut up */ for (map = dumpdirmap, ino = 1; ino < maxino; ino++) { if (((ino - 1) % NBBY) == 0) /* map is offset by 1 */ dirty = *map++; else dirty >>= 1; if ((dirty & 1) == 0) continue; /* * Skip directory inodes deleted and maybe reallocated */ dp = getino(ino); if ((dp->di_mode & IFMT) != IFDIR) continue; dumpino(dp, ino); } passno = 4; setproctitle("%s: pass 4: regular files", disk); msg("dumping (Pass IV) [regular files]\n"); for (map = dumpinomap, ino = 1; ino < maxino; ino++) { int mode; if (((ino - 1) % NBBY) == 0) /* map is offset by 1 */ dirty = *map++; else dirty >>= 1; if ((dirty & 1) == 0) continue; /* * Skip inodes deleted and reallocated as directories. */ dp = getino(ino); mode = dp->di_mode & IFMT; if (mode == IFDIR) continue; dumpino(dp, ino); } time(&tend_writing); spcl.c_type = TS_END; for (i = 0; i < ntrec; i++) writeheader(maxino - 1); if (pipeout) msg("DUMP: %ld tape blocks\n", (long)spcl.c_tapea); else msg("DUMP: %ld tape blocks on %d volume%s\n", (long)spcl.c_tapea, spcl.c_volume, (spcl.c_volume == 1) ? "" : "s"); /* report dump performance, avoid division through zero */ if (tend_writing - tstart_writing == 0) msg("finished in less than a second\n"); else msg("finished in %ld seconds, throughput %ld KBytes/sec\n", (long)(tend_writing - tstart_writing), spcl.c_tapea / (tend_writing - tstart_writing)); putdumptime(); trewind(); broadcast("DUMP IS DONE!\a\a\n"); msg("DUMP IS DONE\n"); Exit(X_FINOK); /* NOTREACHED */ }
int main(int argc, char *argv[]) { ino_t ino; int dirty; union dinode *dp; struct fstab *dt; char *map; int ch, mode; struct tm then; struct statfs fsbuf; int i, anydirskipped, bflag = 0, Tflag = 0, honorlevel = 1; ino_t maxino; time_t t; int dirlist; char *toplevel, *str, *mount_point = NULL; spcl.c_date = (int64_t)time(NULL); tsize = 0; /* Default later, based on 'c' option for cart tapes */ if ((tape = getenv("TAPE")) == NULL) tape = _PATH_DEFTAPE; dumpdates = _PATH_DUMPDATES; temp = _PATH_DTMP; if (TP_BSIZE / DEV_BSIZE == 0 || TP_BSIZE % DEV_BSIZE != 0) quit("TP_BSIZE must be a multiple of DEV_BSIZE\n"); level = '0'; if (argc < 2) usage(); obsolete(&argc, &argv); while ((ch = getopt(argc, argv, "0123456789aB:b:cd:f:h:ns:T:uWw")) != -1) switch (ch) { /* dump level */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': level = ch; break; case 'B': /* blocks per output file */ blocksperfile = numarg("blocks per file", 1L, 0L); break; case 'b': /* blocks per tape write */ ntrec = numarg("blocks per write", 1L, 1000L); if (ntrec > maxbsize/1024) { msg("Please choose a blocksize <= %dKB\n", maxbsize/1024); exit(X_STARTUP); } bflag = 1; break; case 'c': /* Tape is cart. not 9-track */ cartridge = 1; break; case 'd': /* density, in bits per inch */ density = numarg("density", 10L, 327670L) / 10; if (density >= 625 && !bflag) ntrec = HIGHDENSITYTREC; break; case 'f': /* output file */ tape = optarg; break; case 'h': honorlevel = numarg("honor level", 0L, 10L); break; case 'n': /* notify operators */ notify = 1; break; case 's': /* tape size, feet */ tsize = numarg("tape size", 1L, 0L) * 12 * 10; break; case 'T': /* time of last dump */ str = strptime(optarg, "%a %b %e %H:%M:%S %Y", &then); then.tm_isdst = -1; if (str == NULL || (*str != '\n' && *str != '\0')) spcl.c_ddate = -1; else spcl.c_ddate = (int64_t)mktime(&then); if (spcl.c_ddate < 0) { (void)fprintf(stderr, "bad time \"%s\"\n", optarg); exit(X_STARTUP); } Tflag = 1; lastlevel = '?'; break; case 'u': /* update /etc/dumpdates */ uflag = 1; break; case 'W': /* what to do */ case 'w': lastdump(ch); exit(X_FINOK); /* do nothing else */ break; case 'a': /* `auto-size', Write to EOM. */ unlimited = 1; break; default: usage(); } argc -= optind; argv += optind; if (argc < 1) { (void)fprintf(stderr, "Must specify disk or filesystem\n"); exit(X_STARTUP); } /* * determine if disk is a subdirectory, and setup appropriately */ dirlist = 0; toplevel = NULL; for (i = 0; i < argc; i++) { struct stat sb; if (lstat(argv[i], &sb) == -1) { msg("Cannot lstat %s: %s\n", argv[i], strerror(errno)); exit(X_STARTUP); } if (!S_ISDIR(sb.st_mode) && !S_ISREG(sb.st_mode)) break; if (statfs(argv[i], &fsbuf) == -1) { msg("Cannot statfs %s: %s\n", argv[i], strerror(errno)); exit(X_STARTUP); } if (strcmp(argv[i], fsbuf.f_mntonname) == 0) { if (dirlist != 0) { msg("Can't dump a mountpoint and a filelist\n"); exit(X_STARTUP); } break; /* exit if sole mountpoint */ } if (!disk) { if ((toplevel = strdup(fsbuf.f_mntonname)) == NULL) { msg("Cannot malloc diskname\n"); exit(X_STARTUP); } disk = toplevel; if (uflag) { msg("Ignoring u flag for subdir dump\n"); uflag = 0; } if (level > '0') { msg("Subdir dump is done at level 0\n"); level = '0'; } msg("Dumping sub files/directories from %s\n", disk); } else { if (strcmp(disk, fsbuf.f_mntonname) != 0) { msg("%s is not on %s\n", argv[i], disk); exit(X_STARTUP); } } msg("Dumping file/directory %s\n", argv[i]); dirlist++; } if (dirlist == 0) { disk = *argv++; if (argc != 1) { (void)fputs("Excess arguments to dump:", stderr); while (--argc) { (void)putc(' ', stderr); (void)fputs(*argv++, stderr); } (void)putc('\n', stderr); exit(X_STARTUP); } } if (Tflag && uflag) { (void)fprintf(stderr, "You cannot use the T and u flags together.\n"); exit(X_STARTUP); } if (strcmp(tape, "-") == 0) { pipeout++; tape = "standard output"; } if (blocksperfile) blocksperfile = blocksperfile / ntrec * ntrec; /* round down */ else if (!unlimited) { /* * Determine how to default tape size and density * * density tape size * 9-track 1600 bpi (160 bytes/.1") 2300 ft. * 9-track 6250 bpi (625 bytes/.1") 2300 ft. * cartridge 8000 bpi (100 bytes/.1") 1700 ft. * (450*4 - slop) */ if (density == 0) density = cartridge ? 100 : 160; if (tsize == 0) tsize = cartridge ? 1700L*120L : 2300L*120L; } if (strchr(tape, ':')) { host = tape; tape = strchr(host, ':'); *tape++ = '\0'; #ifdef RDUMP if (rmthost(host) == 0) exit(X_STARTUP); #else (void)fprintf(stderr, "remote dump not enabled\n"); exit(X_STARTUP); #endif } if (signal(SIGHUP, SIG_IGN) != SIG_IGN) signal(SIGHUP, sig); if (signal(SIGTRAP, SIG_IGN) != SIG_IGN) signal(SIGTRAP, sig); if (signal(SIGFPE, SIG_IGN) != SIG_IGN) signal(SIGFPE, sig); if (signal(SIGBUS, SIG_IGN) != SIG_IGN) signal(SIGBUS, sig); if (signal(SIGSEGV, SIG_IGN) != SIG_IGN) signal(SIGSEGV, sig); if (signal(SIGTERM, SIG_IGN) != SIG_IGN) signal(SIGTERM, sig); if (signal(SIGINT, interrupt) == SIG_IGN) signal(SIGINT, SIG_IGN); getfstab(); /* /etc/fstab snarfed */ /* * disk can be either the full special file name, * the suffix of the special file name, * the special name missing the leading '/', * the file system name with or without the leading '/'. */ if (!statfs(disk, &fsbuf) && !strcmp(fsbuf.f_mntonname, disk)) { /* mounted disk? */ disk = rawname(fsbuf.f_mntfromname); if (!disk) { (void)fprintf(stderr, "cannot get raw name for %s\n", fsbuf.f_mntfromname); exit(X_STARTUP); } mount_point = fsbuf.f_mntonname; (void)strlcpy(spcl.c_dev, fsbuf.f_mntfromname, sizeof(spcl.c_dev)); if (dirlist != 0) { (void)snprintf(spcl.c_filesys, sizeof(spcl.c_filesys), "a subset of %s", mount_point); } else { (void)strlcpy(spcl.c_filesys, mount_point, sizeof(spcl.c_filesys)); } } else if ((dt = fstabsearch(disk)) != NULL) { /* in fstab? */ disk = rawname(dt->fs_spec); mount_point = dt->fs_file; (void)strlcpy(spcl.c_dev, dt->fs_spec, sizeof(spcl.c_dev)); if (dirlist != 0) { (void)snprintf(spcl.c_filesys, sizeof(spcl.c_filesys), "a subset of %s", mount_point); } else { (void)strlcpy(spcl.c_filesys, mount_point, sizeof(spcl.c_filesys)); } } else { /* must be a device */ (void)strlcpy(spcl.c_dev, disk, sizeof(spcl.c_dev)); (void)strlcpy(spcl.c_filesys, "an unlisted file system", sizeof(spcl.c_filesys)); } (void)strlcpy(spcl.c_label, "none", sizeof(spcl.c_label)); (void)gethostname(spcl.c_host, sizeof(spcl.c_host)); spcl.c_level = level - '0'; spcl.c_type = TS_TAPE; if (!Tflag) getdumptime(); /* /etc/dumpdates snarfed */ t = (time_t)spcl.c_date; msg("Date of this level %c dump: %s", level, t == 0 ? "the epoch\n" : ctime(&t)); t = (time_t)spcl.c_ddate; msg("Date of last level %c dump: %s", lastlevel, t == 0 ? "the epoch\n" : ctime(&t)); msg("Dumping %s ", disk); if (mount_point != NULL) msgtail("(%s) ", mount_point); if (host) msgtail("to %s on host %s\n", tape, host); else msgtail("to %s\n", tape); if ((diskfd = open(disk, O_RDONLY)) < 0) { msg("Cannot open %s\n", disk); exit(X_STARTUP); } sync(); sblock = (struct fs *)sblock_buf; for (i = 0; sblock_try[i] != -1; i++) { ssize_t n = pread(diskfd, sblock, SBLOCKSIZE, (off_t)sblock_try[i]); if (n == SBLOCKSIZE && (sblock->fs_magic == FS_UFS1_MAGIC || (sblock->fs_magic == FS_UFS2_MAGIC && sblock->fs_sblockloc == sblock_try[i])) && sblock->fs_bsize <= MAXBSIZE && sblock->fs_bsize >= sizeof(struct fs)) break; } if (sblock_try[i] == -1) quit("Cannot find filesystem superblock\n"); dev_bsize = sblock->fs_fsize / fsbtodb(sblock, 1); dev_bshift = ffs(dev_bsize) - 1; if (dev_bsize != (1 << dev_bshift)) quit("dev_bsize (%d) is not a power of 2\n", dev_bsize); tp_bshift = ffs(TP_BSIZE) - 1; if (TP_BSIZE != (1 << tp_bshift)) quit("TP_BSIZE (%d) is not a power of 2\n", TP_BSIZE); #ifdef FS_44INODEFMT if (sblock->fs_magic == FS_UFS2_MAGIC || sblock->fs_inodefmt >= FS_44INODEFMT) spcl.c_flags |= DR_NEWINODEFMT; #endif maxino = sblock->fs_ipg * sblock->fs_ncg; mapsize = roundup(howmany(maxino, NBBY), TP_BSIZE); usedinomap = (char *)calloc((unsigned) mapsize, sizeof(char)); dumpdirmap = (char *)calloc((unsigned) mapsize, sizeof(char)); dumpinomap = (char *)calloc((unsigned) mapsize, sizeof(char)); tapesize = 3 * (howmany(mapsize * sizeof(char), TP_BSIZE) + 1); nonodump = spcl.c_level < honorlevel; (void)signal(SIGINFO, statussig); msg("mapping (Pass I) [regular files]\n"); anydirskipped = mapfiles(maxino, &tapesize, toplevel, (dirlist ? argv : NULL)); msg("mapping (Pass II) [directories]\n"); while (anydirskipped) { anydirskipped = mapdirs(maxino, &tapesize); } if (pipeout || unlimited) { tapesize += 10; /* 10 trailer blocks */ msg("estimated %lld tape blocks.\n", tapesize); } else { double fetapes; if (blocksperfile) fetapes = (double) tapesize / blocksperfile; else if (cartridge) { /* Estimate number of tapes, assuming streaming stops at the end of each block written, and not in mid-block. Assume no erroneous blocks; this can be compensated for with an artificially low tape size. */ fetapes = ( tapesize /* blocks */ * TP_BSIZE /* bytes/block */ * (1.0/density) /* 0.1" / byte */ + tapesize /* blocks */ * (1.0/ntrec) /* streaming-stops per block */ * 15.48 /* 0.1" / streaming-stop */ ) * (1.0 / tsize ); /* tape / 0.1" */ } else { /* Estimate number of tapes, for old fashioned 9-track tape */ int tenthsperirg = (density == 625) ? 3 : 7; fetapes = ( tapesize /* blocks */ * TP_BSIZE /* bytes / block */ * (1.0/density) /* 0.1" / byte */ + tapesize /* blocks */ * (1.0/ntrec) /* IRG's / block */ * tenthsperirg /* 0.1" / IRG */ ) * (1.0 / tsize ); /* tape / 0.1" */ } etapes = fetapes; /* truncating assignment */ etapes++; /* count the dumped inodes map on each additional tape */ tapesize += (etapes - 1) * (howmany(mapsize * sizeof(char), TP_BSIZE) + 1); tapesize += etapes + 10; /* headers + 10 trailer blks */ msg("estimated %lld tape blocks on %3.2f tape(s).\n", tapesize, fetapes); } /* * Allocate tape buffer. */ if (!alloctape()) quit("can't allocate tape buffers - try a smaller blocking factor.\n"); startnewtape(1); (void)time((time_t *)&(tstart_writing)); xferrate = 0; dumpmap(usedinomap, TS_CLRI, maxino - 1); msg("dumping (Pass III) [directories]\n"); dirty = 0; /* XXX just to get gcc to shut up */ for (map = dumpdirmap, ino = 1; ino < maxino; ino++) { if (((ino - 1) % NBBY) == 0) /* map is offset by 1 */ dirty = *map++; else dirty >>= 1; if ((dirty & 1) == 0) continue; /* * Skip directory inodes deleted and maybe reallocated */ dp = getino(ino, &mode); if (mode != IFDIR) continue; (void)dumpino(dp, ino); } msg("dumping (Pass IV) [regular files]\n"); for (map = dumpinomap, ino = 1; ino < maxino; ino++) { if (((ino - 1) % NBBY) == 0) /* map is offset by 1 */ dirty = *map++; else dirty >>= 1; if ((dirty & 1) == 0) continue; /* * Skip inodes deleted and reallocated as directories. */ dp = getino(ino, &mode); if (mode == IFDIR) continue; (void)dumpino(dp, ino); } spcl.c_type = TS_END; for (i = 0; i < ntrec; i++) writeheader(maxino - 1); if (pipeout) msg("%lld tape blocks\n", spcl.c_tapea); else msg("%lld tape blocks on %d volume%s\n", spcl.c_tapea, spcl.c_volume, (spcl.c_volume == 1) ? "" : "s"); t = (time_t)spcl.c_date; msg("Date of this level %c dump: %s", level, t == 0 ? "the epoch\n" : ctime(&t)); t = do_stats(); msg("Date this dump completed: %s", ctime(&t)); msg("Average transfer rate: %ld KB/s\n", xferrate / tapeno); putdumptime(); trewind(); broadcast("DUMP IS DONE!\7\7\n"); msg("DUMP IS DONE\n"); Exit(X_FINOK); /* NOTREACHED */ }
/* * Dump passes 3 and 4. * * Dump the contents of an inode to tape. */ void dumpino(struct ufs1_dinode *dp, ufs1_ino_t ino) { int ind_level, cnt; fsizeT size; char buf[TP_BSIZE]; if (newtape) { newtape = 0; dumpmap(dumpinomap, TS_BITS, ino); } CLRINO(ino, dumpinomap); spcl.c_dinode = *dp; spcl.c_type = TS_INODE; spcl.c_count = 0; switch (dp->di_mode & S_IFMT) { case 0: /* * Freed inode. */ return; case S_IFLNK: /* * Check for short symbolic link. */ #ifdef FS_44INODEFMT if (dp->di_size > 0 && dp->di_size < (unsigned)sblock->fs_maxsymlinklen) { spcl.c_addr[0] = 1; spcl.c_count = 1; writeheader(ino); memmove(buf, dp->di_shortlink, (u_long)dp->di_size); buf[dp->di_size] = '\0'; writerec(buf, 0); return; } #endif /* fall through */ case S_IFDIR: case S_IFREG: if (dp->di_size > 0) break; /* fall through */ case S_IFIFO: case S_IFSOCK: case S_IFCHR: case S_IFBLK: writeheader(ino); return; default: msg("Warning: undefined file type 0%o\n", dp->di_mode & IFMT); return; } if (dp->di_size > NDADDR * (unsigned)sblock->fs_bsize) cnt = NDADDR * sblock->fs_frag; else cnt = howmany(dp->di_size, sblock->fs_fsize); blksout(&dp->di_db[0], cnt, ino); if ((size = dp->di_size - NDADDR * sblock->fs_bsize) <= 0) return; for (ind_level = 0; ind_level < NIADDR; ind_level++) { dmpindir(ino, dp->di_ib[ind_level], ind_level, &size); if (size <= 0) return; } }
/* * Dump passes 3 and 4. * * Dump the contents of an inode to tape. */ void dumpino(union dinode *dp, ino_t ino) { int ind_level, cnt, last, added; off_t size; char buf[TP_BSIZE]; if (newtape) { newtape = 0; dumpmap(dumpinomap, TS_BITS, ino); } CLRINO(ino, dumpinomap); /* * Zero out the size of a snapshot so that it will be dumped * as a zero length file. */ if ((DIP(dp, di_flags) & SF_SNAPSHOT) != 0) { DIP_SET(dp, di_size, 0); DIP_SET(dp, di_flags, DIP(dp, di_flags) & ~SF_SNAPSHOT); } if (sblock->fs_magic == FS_UFS1_MAGIC) { spcl.c_mode = dp->dp1.di_mode; spcl.c_size = dp->dp1.di_size; spcl.c_extsize = 0; spcl.c_atime = _time32_to_time(dp->dp1.di_atime); spcl.c_atimensec = dp->dp1.di_atimensec; spcl.c_mtime = _time32_to_time(dp->dp1.di_mtime); spcl.c_mtimensec = dp->dp1.di_mtimensec; spcl.c_birthtime = 0; spcl.c_birthtimensec = 0; spcl.c_rdev = dp->dp1.di_rdev; spcl.c_file_flags = dp->dp1.di_flags; spcl.c_uid = dp->dp1.di_uid; spcl.c_gid = dp->dp1.di_gid; } else { spcl.c_mode = dp->dp2.di_mode; spcl.c_size = dp->dp2.di_size; spcl.c_extsize = dp->dp2.di_extsize; spcl.c_atime = _time64_to_time(dp->dp2.di_atime); spcl.c_atimensec = dp->dp2.di_atimensec; spcl.c_mtime = _time64_to_time(dp->dp2.di_mtime); spcl.c_mtimensec = dp->dp2.di_mtimensec; spcl.c_birthtime = _time64_to_time(dp->dp2.di_birthtime); spcl.c_birthtimensec = dp->dp2.di_birthnsec; spcl.c_rdev = dp->dp2.di_rdev; spcl.c_file_flags = dp->dp2.di_flags; spcl.c_uid = dp->dp2.di_uid; spcl.c_gid = dp->dp2.di_gid; } spcl.c_type = TS_INODE; spcl.c_count = 0; switch (DIP(dp, di_mode) & S_IFMT) { case 0: /* * Freed inode. */ return; case S_IFLNK: /* * Check for short symbolic link. */ if (DIP(dp, di_size) > 0 && DIP(dp, di_size) < sblock->fs_maxsymlinklen) { spcl.c_addr[0] = 1; spcl.c_count = 1; added = appendextdata(dp); writeheader(ino); if (sblock->fs_magic == FS_UFS1_MAGIC) memmove(buf, (caddr_t)dp->dp1.di_db, (u_long)DIP(dp, di_size)); else memmove(buf, (caddr_t)dp->dp2.di_db, (u_long)DIP(dp, di_size)); buf[DIP(dp, di_size)] = '\0'; writerec(buf, 0); writeextdata(dp, ino, added); return; } /* FALLTHROUGH */ case S_IFDIR: case S_IFREG: if (DIP(dp, di_size) > 0) break; /* FALLTHROUGH */ case S_IFIFO: case S_IFSOCK: case S_IFCHR: case S_IFBLK: added = appendextdata(dp); writeheader(ino); writeextdata(dp, ino, added); return; default: msg("Warning: undefined file type 0%o\n", DIP(dp, di_mode) & IFMT); return; } if (DIP(dp, di_size) > NDADDR * sblock->fs_bsize) { cnt = NDADDR * sblock->fs_frag; last = 0; } else { cnt = howmany(DIP(dp, di_size), sblock->fs_fsize); last = 1; } if (sblock->fs_magic == FS_UFS1_MAGIC) ufs1_blksout(&dp->dp1.di_db[0], cnt, ino); else ufs2_blksout(dp, &dp->dp2.di_db[0], cnt, ino, last); if ((size = DIP(dp, di_size) - NDADDR * sblock->fs_bsize) <= 0) return; for (ind_level = 0; ind_level < NIADDR; ind_level++) { dmpindir(dp, ino, DIP(dp, di_ib[ind_level]), ind_level, &size); if (size <= 0) return; } }