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
main(int argc,char * argv[])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
usage(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
numarg(const char * meaning,long vmin,long vmax)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
sig(int signo)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
obsolete(int * argcp,char ** argvp[])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 *
xcalloc(size_t number,size_t size)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 *
xmalloc(size_t size)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 *
xstrdup(const char * str)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