]> git.wh0rd.org Git - dump.git/blob - dump/main.c
64bit and glibc 2.2.2 fixes.
[dump.git] / dump / main.c
1 /*
2  *      Ported to Linux's Second Extended File System as part of the
3  *      dump and restore backup suit
4  *      Remy Card <card@Linux.EU.Org>, 1994-1997
5  *      Stelian Pop <pop@noos.fr>, 1999-2000
6  *      Stelian Pop <pop@noos.fr> - AlcĂ´ve <www.alcove.fr>, 2000
7  */
8
9 /*-
10  * Copyright (c) 1980, 1991, 1993, 1994
11  *      The Regents of the University of California.  All rights reserved.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. All advertising materials mentioning features or use of this software
22  *    must display the following acknowledgement:
23  *      This product includes software developed by the University of
24  *      California, Berkeley and its contributors.
25  * 4. Neither the name of the University nor the names of its contributors
26  *    may be used to endorse or promote products derived from this software
27  *    without specific prior written permission.
28  *
29  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39  * SUCH DAMAGE.
40  */
41
42 #ifndef lint
43 static const char rcsid[] =
44         "$Id: main.c,v 1.34 2001/02/22 10:57:40 stelian Exp $";
45 #endif /* not lint */
46
47 #include <config.h>
48 #include <sys/param.h>
49 #include <sys/time.h>
50 #ifdef __linux__
51 #include <linux/ext2_fs.h>
52 #include <time.h>
53 #include <sys/stat.h>
54 #include <bsdcompat.h>
55 #else
56 #ifdef sunos
57 #include <sys/vnode.h>
58
59 #include <ufs/inode.h>
60 #include <ufs/fs.h>
61 #else
62 #include <ufs/ufs/dinode.h>
63 #include <ufs/ffs/fs.h>
64 #endif
65 #endif
66
67 #include <protocols/dumprestore.h>
68
69 #include <ctype.h>
70 #include <compaterr.h>
71 #include <fcntl.h>
72 #include <fstab.h>
73 #include <signal.h>
74 #include <stdio.h>
75 #include <stdlib.h>
76 #include <string.h>
77 #include <unistd.h>
78
79 #ifdef __linux__
80 #include <ext2fs/ext2fs.h>
81 #endif
82
83 #include "dump.h"
84 #include "pathnames.h"
85 #include "bylabel.h"
86
87 #ifndef SBOFF
88 #define SBOFF (SBLOCK * DEV_BSIZE)
89 #endif
90
91 int     notify = 0;     /* notify operator flag */
92 int     blockswritten = 0;      /* number of blocks written on current tape */
93 int     tapeno = 0;     /* current tape number */
94 int     density = 0;    /* density in bytes/0.1" " <- this is for hilit19 */
95 int     ntrec = NTREC;  /* # tape blocks in each tape record */
96 int     cartridge = 0;  /* Assume non-cartridge tape */
97 int     dokerberos = 0; /* Use Kerberos authentication */
98 long    dev_bsize = 1;  /* recalculated below */
99 long    blocksperfile;  /* output blocks per file */
100 char    *host = NULL;   /* remote host (if any) */
101 int     sizest = 0;     /* return size estimate only */
102 int     compressed = 0; /* use zlib to compress the output */
103 long long bytes_written = 0; /* total bytes written */
104 long    uncomprblks = 0;/* uncompressed blocks written */
105
106 #ifdef  __linux__
107 char    *__progname;
108 #endif
109
110 int     maxbsize = 64*1024;     /* XXX MAXBSIZE from sys/param.h */
111 static long numarg __P((const char *, long, long));
112 static void obsolete __P((int *, char **[]));
113 static void usage __P((void));
114
115 ino_t iexclude_list[IEXCLUDE_MAXNUM];   /* the inode exclude list */
116 int iexclude_num = 0;                   /* number of elements in the list */
117
118 int
119 main(int argc, char *argv[])
120 {
121         register ino_t ino;
122         register int dirty;
123         register struct dinode *dp;
124         register struct fstab *dt;
125         register char *map;
126         register int ch;
127         int i, anydirskipped, bflag = 0, Tflag = 0, honorlevel = 1;
128         ino_t maxino;
129 #ifdef  __linux__
130         errcode_t retval;
131         char directory[MAXPATHLEN];
132         char pathname[MAXPATHLEN];
133 #endif
134         time_t tnow;
135         char *diskparam;
136
137         spcl.c_label[0] = '\0';
138         spcl.c_date = 0;
139 #ifdef  __linux__
140         (void)time4(&spcl.c_date);
141 #else
142         (void)time((time_t *)&spcl.c_date);
143 #endif
144
145 #ifdef __linux__
146         __progname = argv[0];
147         directory[0] = 0;
148         initialize_ext2_error_table();
149 #endif
150
151         tsize = 0;      /* Default later, based on 'c' option for cart tapes */
152         unlimited = 1;
153         eot_script = NULL;
154         if ((tapeprefix = getenv("TAPE")) == NULL)
155                 tapeprefix = _PATH_DEFTAPE;
156         dumpdates = _PATH_DUMPDATES;
157         if (TP_BSIZE / DEV_BSIZE == 0 || TP_BSIZE % DEV_BSIZE != 0)
158                 quit("TP_BSIZE must be a multiple of DEV_BSIZE\n");
159         level = '0';
160
161         if (argc < 2)
162                 usage();
163
164         obsolete(&argc, &argv);
165 #ifdef KERBEROS
166 #ifdef HAVE_ZLIB
167 #define optstring "0123456789aB:b:cd:e:f:F:h:kL:Mns:ST:uWwz"
168 #else
169 #define optstring "0123456789aB:b:cd:e:f:F:h:kL:Mns:ST:uWw"
170 #endif /* HAVE_ZLIB */
171 #else
172 #ifdef HAVE_ZLIB
173 #define optstring "0123456789aB:b:cd:e:f:F:h:L:Mns:ST:uWwz"
174 #else
175 #define optstring "0123456789aB:b:cd:e:f:F:h:L:Mns:ST:uWw"
176 #endif /* HAVE_ZLIB */
177 #endif /* KERBEROS */
178         while ((ch = getopt(argc, argv, optstring)) != -1)
179 #undef optstring
180                 switch (ch) {
181                 /* dump level */
182                 case '0': case '1': case '2': case '3': case '4':
183                 case '5': case '6': case '7': case '8': case '9':
184                         level = ch;
185                         break;
186
187                 case 'a':               /* `auto-size', Write to EOM. */
188                         unlimited = 1;
189                         break;
190
191                 case 'B':               /* blocks per output file */
192                         unlimited = 0;
193                         blocksperfile = numarg("number of blocks per file",
194                             1L, 0L);
195                         break;
196
197                 case 'b':               /* blocks per tape write */
198                         ntrec = numarg("number of blocks per write",
199                             1L, 1000L);
200                         if (ntrec > maxbsize/1024) {
201                                 msg("Please choose a blocksize <= %dKB\n",
202                                         maxbsize/1024);
203                                 exit(X_STARTUP);
204                         }
205                         bflag = 1;
206                         break;
207
208                 case 'c':               /* Tape is cart. not 9-track */
209                         unlimited = 0;
210                         cartridge = 1;
211                         break;
212
213                 case 'd':               /* density, in bits per inch */
214                         unlimited = 0;
215                         density = numarg("density", 10L, 327670L) / 10;
216                         if (density >= 625 && !bflag)
217                                 ntrec = HIGHDENSITYTREC;
218                         break;
219                         
220                                         /* 04-Feb-00 ILC */
221                 case 'e':               /* exclude an inode */
222                         if (iexclude_num == IEXCLUDE_MAXNUM) {
223                                 (void)fprintf(stderr, "Too many -e options\n");
224                                 exit(X_STARTUP);
225                         }
226                         iexclude_list[iexclude_num++] = numarg("inode to exclude",0L,0L);
227                         if (iexclude_list[iexclude_num-1] <= ROOTINO) {
228                                 (void)fprintf(stderr, "Cannot exclude inode %ld\n", (long)iexclude_list[iexclude_num-1]);
229                                 exit(X_STARTUP);
230                         }
231                         msg("Added %d to exclude list\n",
232                             iexclude_list[iexclude_num-1]);
233                         break;
234
235                 case 'f':               /* output file */
236                         tapeprefix = optarg;
237                         break;
238
239                 case 'F':               /* end of tape script */
240                         eot_script = optarg;
241                         break;
242
243                 case 'h':
244                         honorlevel = numarg("honor level", 0L, 10L);
245                         break;
246
247 #ifdef KERBEROS
248                 case 'k':
249                         dokerberos = 1;
250                         break;
251 #endif
252
253                 case 'L':
254                         /*
255                          * Note that although there are LBLSIZE characters,
256                          * the last must be '\0', so the limit on strlen()
257                          * is really LBLSIZE-1.
258                          */
259                         strncpy(spcl.c_label, optarg, LBLSIZE);
260                         spcl.c_label[LBLSIZE-1] = '\0';
261                         if (strlen(optarg) > LBLSIZE-1) {
262                                 msg(
263                 "WARNING Label `%s' is larger than limit of %d characters.\n",
264                                     optarg, LBLSIZE-1);
265                                 msg("WARNING: Using truncated label `%s'.\n",
266                                     spcl.c_label);
267                         }
268                         break;
269
270                 case 'M':               /* multi-volume flag */
271                         Mflag = 1;
272                         break;
273
274                 case 'n':               /* notify operators */
275                         notify = 1;
276                         break;
277
278                 case 's':               /* tape size, feet */
279                         unlimited = 0;
280                         tsize = numarg("tape size", 1L, 0L) * 12 * 10;
281                         break;
282
283                 case 'S':
284                         sizest = 1;     /* return size estimate only */
285                         break;
286
287                 case 'T':               /* time of last dump */
288                         spcl.c_ddate = unctime(optarg);
289                         if (spcl.c_ddate < 0) {
290                                 (void)fprintf(stderr, "bad time \"%s\"\n",
291                                     optarg);
292                                 exit(X_STARTUP);
293                         }
294                         Tflag = 1;
295                         lastlevel = '?';
296                         break;
297
298                 case 'u':               /* update dumpdates */
299                         uflag = 1;
300                         break;
301
302                 case 'W':               /* what to do */
303                 case 'w':
304                         lastdump(ch);
305                         exit(X_FINOK);  /* do nothing else */
306 #ifdef HAVE_ZLIB
307                 case 'z':
308                         compressed = 1;
309                         break;
310 #endif /* HAVE_ZLIB */
311
312                 default:
313                         usage();
314                 }
315         argc -= optind;
316         argv += optind;
317
318         if (argc < 1) {
319                 (void)fprintf(stderr, "Must specify disk or filesystem\n");
320                 exit(X_STARTUP);
321         }
322         diskparam = *argv++;
323         if (strlen(diskparam) >= MAXPATHLEN) {
324                 (void)fprintf(stderr, "Disk or filesystem name too long: %s\n", diskparam);
325                 exit(X_STARTUP);
326         }
327         argc--;
328         if (argc >= 1) {
329                 (void)fprintf(stderr, "Unknown arguments to dump:");
330                 while (argc--)
331                         (void)fprintf(stderr, " %s", *argv++);
332                 (void)fprintf(stderr, "\n");
333                 exit(X_STARTUP);
334         }
335         if (Tflag && uflag) {
336                 (void)fprintf(stderr,
337                     "You cannot use the T and u flags together.\n");
338                 exit(X_STARTUP);
339         }
340         if (strcmp(tapeprefix, "-") == 0) {
341                 pipeout++;
342                 tapeprefix = "standard output";
343         }
344
345         if (blocksperfile)
346                 blocksperfile = blocksperfile / ntrec * ntrec; /* round down */
347         else if (!unlimited) {
348                 /*
349                  * Determine how to default tape size and density
350                  *
351                  *              density                         tape size
352                  * 9-track      1600 bpi (160 bytes/.1")        2300 ft.
353                  * 9-track      6250 bpi (625 bytes/.1")        2300 ft.
354                  * cartridge    8000 bpi (100 bytes/.1")        1700 ft.
355                  *                                              (450*4 - slop)
356                  * hilit19 hits again: "
357                  */
358                 if (density == 0)
359                         density = cartridge ? 100 : 160;
360                 if (tsize == 0)
361                         tsize = cartridge ? 1700L*120L : 2300L*120L;
362         }
363
364         if (strchr(tapeprefix, ':')) {
365                 host = tapeprefix;
366                 tapeprefix = strchr(host, ':');
367                 *tapeprefix++ = '\0';
368 #ifdef RDUMP
369                 if (index(tapeprefix, '\n')) {
370                     (void)fprintf(stderr, "invalid characters in tape\n");
371                     exit(X_STARTUP);
372                 }
373                 if (rmthost(host) == 0)
374                         exit(X_STARTUP);
375 #else
376                 (void)fprintf(stderr, "remote dump not enabled\n");
377                 exit(X_STARTUP);
378 #endif
379         }
380         (void)setuid(getuid()); /* rmthost() is the only reason to be setuid */
381
382         if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
383                 signal(SIGHUP, sig);
384         if (signal(SIGTRAP, SIG_IGN) != SIG_IGN)
385                 signal(SIGTRAP, sig);
386         if (signal(SIGFPE, SIG_IGN) != SIG_IGN)
387                 signal(SIGFPE, sig);
388         if (signal(SIGBUS, SIG_IGN) != SIG_IGN)
389                 signal(SIGBUS, sig);
390         if (signal(SIGSEGV, SIG_IGN) != SIG_IGN)
391                 signal(SIGSEGV, sig);
392         if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
393                 signal(SIGTERM, sig);
394         if (signal(SIGINT, interrupt) == SIG_IGN)
395                 signal(SIGINT, SIG_IGN);
396         set_operators();        /* /etc/group snarfed */
397         getfstab();             /* /etc/fstab snarfed */
398
399         disk = get_device_name(diskparam);
400         if (!disk) {            /* null means the disk is some form
401                                    of LABEL= or UID= but it was not
402                                    found */
403                 msg("Cannot find a disk having %s\n", diskparam);
404                 exit(X_STARTUP);
405         }
406         /*
407          *      disk may end in / and this can confuse
408          *      fstabsearch.
409          */
410         if (strlen(disk) > 1 && disk[strlen(disk) - 1] == '/')
411                 disk[strlen(disk) - 1] = '\0';
412         /*
413          *      disk can be either the full special file name,
414          *      the suffix of the special file name,
415          *      the special name missing the leading '/',
416          *      the file system name with or without the leading '/'.
417          */
418         if ((dt = fstabsearch(disk)) != NULL) {
419                 disk = rawname(dt->fs_spec);
420                 (void)strncpy(spcl.c_dev, dt->fs_spec, NAMELEN);
421                 (void)strncpy(spcl.c_filesys, dt->fs_file, NAMELEN);
422         } else {
423 #ifdef  __linux__
424 #ifdef  HAVE_REALPATH
425                 if (realpath(disk, pathname) == NULL)
426 #endif
427                         strcpy(pathname, disk);
428                 /*
429                  * The argument could be now a mountpoint of
430                  * a filesystem specified in fstab. Search for it.
431                  */
432                 if ((dt = fstabsearch(pathname)) != NULL) {
433                         disk = rawname(dt->fs_spec);
434                         (void)strncpy(spcl.c_dev, dt->fs_spec, NAMELEN);
435                         (void)strncpy(spcl.c_filesys, dt->fs_file, NAMELEN);
436                 } else {
437                         /*
438                          * The argument was not found in the fstab
439                          * assume that this is a subtree and search for it
440                          */
441                         dt = fstabsearchdir(pathname, directory);
442                         if (dt != NULL) {
443                                 char name[MAXPATHLEN];
444                                 (void)strncpy(spcl.c_dev, dt->fs_spec, NAMELEN);
445                                 (void)snprintf(name, sizeof(name), "%s (dir %s)",
446                                               dt->fs_file, directory);
447                                 (void)strncpy(spcl.c_filesys, name, NAMELEN);
448                                 disk = rawname(dt->fs_spec);
449                         } else {
450                                 (void)strncpy(spcl.c_dev, disk, NAMELEN);
451                                 (void)strncpy(spcl.c_filesys, "an unlisted file system",
452                                     NAMELEN);
453                         }
454                 }
455 #else
456                 (void)strncpy(spcl.c_dev, disk, NAMELEN);
457                 (void)strncpy(spcl.c_filesys, "an unlisted file system",
458                     NAMELEN);
459 #endif
460         }
461
462         if (directory[0] != 0) {
463                 if (level != '0') {
464                         (void)fprintf(stderr, "Only level 0 dumps are allowed on a subdirectory\n");
465                         exit(X_STARTUP);
466                 }
467                 if (uflag) {
468                         (void)fprintf(stderr, "You can't update the dumpdates file when dumping a subdirectory\n");
469                         exit(X_STARTUP);
470                 }
471         }
472         spcl.c_dev[NAMELEN-1] = '\0';
473         spcl.c_filesys[NAMELEN-1] = '\0';
474         (void)gethostname(spcl.c_host, NAMELEN);
475         spcl.c_host[NAMELEN-1] = '\0';
476         spcl.c_level = level - '0';
477         spcl.c_type = TS_TAPE;
478         if (!Tflag)
479                 getdumptime(uflag);             /* dumpdates snarfed */
480
481         if (spcl.c_ddate == 0 && spcl.c_level) {
482                 msg("WARNING: There is no inferior level dump on this filesystem\n"); 
483                 msg("WARNING: Assuming a level 0 dump by default\n");
484                 level = '0';
485                 spcl.c_level = 0;
486         }
487
488         if (Mflag)
489                 snprintf(tape, MAXPATHLEN, "%s%03d", tapeprefix, tapeno + 1);
490         else
491                 strncpy(tape, tapeprefix, MAXPATHLEN);
492         tape[MAXPATHLEN - 1] = '\0';
493
494         if (!sizest) {
495
496                 msg("Date of this level %c dump: %s", level,
497 #ifdef  __linux__
498                         spcl.c_date == 0 ? "the epoch\n" : ctime4(&spcl.c_date));
499 #else
500                         spcl.c_date == 0 ? "the epoch\n" : ctime(&spcl.c_date));
501 #endif
502                 msg("Date of last level %c dump: %s", lastlevel,
503 #ifdef  __linux__
504                         spcl.c_ddate == 0 ? "the epoch\n" : ctime4(&spcl.c_ddate));
505 #else
506                         spcl.c_ddate == 0 ? "the epoch\n" : ctime(&spcl.c_ddate));
507 #endif
508                 msg("Dumping %s (%s) ", disk, spcl.c_filesys);
509                 if (host)
510                         msgtail("to %s on host %s\n", tape, host);
511                 else
512                         msgtail("to %s\n", tape);
513         } /* end of size estimate */
514
515 #ifdef  __linux__
516         retval = dump_fs_open(disk, &fs);
517         if (retval) {
518                 com_err(disk, retval, "while opening filesystem");
519                 if (retval == EXT2_ET_REV_TOO_HIGH)
520                         printf ("Get a newer version of dump!\n");
521                 exit(X_STARTUP);
522         }
523         if (fs->super->s_rev_level > DUMP_CURRENT_REV) {
524                 com_err(disk, retval, "while opening filesystem");
525                 printf ("Get a newer version of dump!\n");
526                 exit(X_STARTUP);
527         }
528         if ((diskfd = open(disk, O_RDONLY)) < 0) {
529                 msg("Cannot open %s\n", disk);
530                 exit(X_STARTUP);
531         }
532         /* if no user label specified, use ext2 filesystem label if available */
533         if (spcl.c_label[0] == '\0') {
534                 const char *lbl;
535                 if ( (lbl = get_device_label(disk)) != NULL) {
536                         strncpy(spcl.c_label, lbl, LBLSIZE);
537                         spcl.c_label[LBLSIZE-1] = '\0';
538                 }
539                 else
540                         strcpy(spcl.c_label, "none");   /* safe strcpy. */
541         }
542         sync();
543         dev_bsize = DEV_BSIZE;
544         dev_bshift = ffs(dev_bsize) - 1;
545         if (dev_bsize != (1 << dev_bshift))
546                 quit("dev_bsize (%d) is not a power of 2", dev_bsize);
547         tp_bshift = ffs(TP_BSIZE) - 1;
548         if (TP_BSIZE != (1 << tp_bshift))
549                 quit("TP_BSIZE (%d) is not a power of 2", TP_BSIZE);
550         maxino = fs->super->s_inodes_count;
551 #if     0
552         spcl.c_flags |= DR_NEWINODEFMT;
553 #endif
554 #else   /* __linux __*/
555         if ((diskfd = open(disk, O_RDONLY)) < 0) {
556                 msg("Cannot open %s\n", disk);
557                 exit(X_STARTUP);
558         }
559         sync();
560         sblock = (struct fs *)sblock_buf;
561         bread(SBOFF, (char *) sblock, SBSIZE);
562         if (sblock->fs_magic != FS_MAGIC)
563                 quit("bad sblock magic number\n");
564         dev_bsize = sblock->fs_fsize / fsbtodb(sblock, 1);
565         dev_bshift = ffs(dev_bsize) - 1;
566         if (dev_bsize != (1 << dev_bshift))
567                 quit("dev_bsize (%d) is not a power of 2", dev_bsize);
568         tp_bshift = ffs(TP_BSIZE) - 1;
569         if (TP_BSIZE != (1 << tp_bshift))
570                 quit("TP_BSIZE (%d) is not a power of 2", TP_BSIZE);
571 #ifdef FS_44INODEFMT
572         if (sblock->fs_inodefmt >= FS_44INODEFMT)
573                 spcl.c_flags |= DR_NEWINODEFMT;
574 #endif
575         maxino = sblock->fs_ipg * sblock->fs_ncg;
576 #endif  /* __linux__ */
577         mapsize = roundup(howmany(maxino, NBBY), TP_BSIZE);
578         usedinomap = (char *)calloc((unsigned) mapsize, sizeof(char));
579         dumpdirmap = (char *)calloc((unsigned) mapsize, sizeof(char));
580         dumpinomap = (char *)calloc((unsigned) mapsize, sizeof(char));
581         if (usedinomap == NULL || dumpdirmap == NULL || dumpinomap == NULL)
582                 quit("out of memory allocating inode maps\n");
583         tapesize = 2 * (howmany(mapsize * sizeof(char), TP_BSIZE) + 1);
584
585         nonodump = spcl.c_level < honorlevel;
586
587         msg("Label: %s\n", spcl.c_label);
588
589 #if defined(SIGINFO)
590         (void)signal(SIGINFO, statussig);
591 #endif
592
593         if (!sizest)
594                 msg("mapping (Pass I) [regular files]\n");
595 #ifdef  __linux__
596         if (directory[0] == 0)
597                 anydirskipped = mapfiles(maxino, &tapesize);
598         else
599                 anydirskipped = mapfilesfromdir(maxino, &tapesize, directory);
600 #else
601         anydirskipped = mapfiles(maxino, &tapesize);
602 #endif
603
604         if (!sizest)
605                 msg("mapping (Pass II) [directories]\n");
606         while (anydirskipped) {
607                 anydirskipped = mapdirs(maxino, &tapesize);
608         }
609
610         if (sizest) {
611                 printf("%.0f\n", ((double)tapesize + 11) * TP_BSIZE);
612                 exit(X_FINOK);
613         } /* stop here for size estimate */
614
615         if (pipeout || unlimited) {
616                 tapesize += 11; /* 10 trailer blocks + 1 map header */
617                 msg("estimated %ld tape blocks.\n", tapesize);
618         } else {
619                 double fetapes;
620
621                 if (blocksperfile)
622                         fetapes = (double) tapesize / blocksperfile;
623                 else if (cartridge) {
624                         /* Estimate number of tapes, assuming streaming stops at
625                            the end of each block written, and not in mid-block.
626                            Assume no erroneous blocks; this can be compensated
627                            for with an artificially low tape size. */
628                         fetapes =
629                         (         (double) tapesize     /* blocks */
630                                 * TP_BSIZE      /* bytes/block */
631                                 * (1.0/density) /* 0.1" / byte " */
632                           +
633                                   (double) tapesize     /* blocks */
634                                 * (1.0/ntrec)   /* streaming-stops per block */
635                                 * 15.48         /* 0.1" / streaming-stop " */
636                         ) * (1.0 / tsize );     /* tape / 0.1" " */
637                 } else {
638                         /* Estimate number of tapes, for old fashioned 9-track
639                            tape */
640                         int tenthsperirg = (density == 625) ? 3 : 7;
641                         fetapes =
642                         (         (double) tapesize     /* blocks */
643                                 * TP_BSIZE      /* bytes / block */
644                                 * (1.0/density) /* 0.1" / byte " */
645                           +
646                                   (double) tapesize     /* blocks */
647                                 * (1.0/ntrec)   /* IRG's / block */
648                                 * tenthsperirg  /* 0.1" / IRG " */
649                         ) * (1.0 / tsize );     /* tape / 0.1" " */
650                 }
651                 etapes = fetapes;               /* truncating assignment */
652                 etapes++;
653                 /* count the dumped inodes map on each additional tape */
654                 tapesize += (etapes - 1) *
655                         (howmany(mapsize * sizeof(char), TP_BSIZE) + 1);
656                 tapesize += etapes + 10;        /* headers + 10 trailer blks */
657                 msg("estimated %ld tape blocks on %3.2f tape(s).\n",
658                     tapesize, fetapes);
659         }
660
661         /*
662          * Allocate tape buffer.
663          */
664         if (!alloctape())
665                 quit(
666         "can't allocate tape buffers - try a smaller blocking factor.\n");
667
668         startnewtape(1);
669 #ifdef __linux__
670         (void)time4(&(tstart_writing));
671 #else
672         (void)time((time_t *)&(tstart_writing));
673 #endif
674         dumpmap(usedinomap, TS_CLRI, maxino - 1);
675
676         msg("dumping (Pass III) [directories]\n");
677         dirty = 0;              /* XXX just to get gcc to shut up */
678         for (map = dumpdirmap, ino = 1; ino < maxino; ino++) {
679                 if (((ino - 1) % NBBY) == 0)    /* map is offset by 1 */
680                         dirty = *map++;
681                 else
682                         dirty >>= 1;
683                 if ((dirty & 1) == 0)
684                         continue;
685                 /*
686                  * Skip directory inodes deleted and maybe reallocated
687                  */
688                 dp = getino(ino);
689                 if ((dp->di_mode & IFMT) != IFDIR)
690                         continue;
691 #ifdef  __linux__
692                 /*
693                  * Skip directory inodes deleted and not yes reallocated...
694                  */
695                 if (dp->di_nlink == 0 || dp->di_dtime != 0)
696                         continue;
697                 (void)dumpdirino(dp, ino);
698 #else
699                 (void)dumpino(dp, ino);
700 #endif
701         }
702
703         msg("dumping (Pass IV) [regular files]\n");
704         for (map = dumpinomap, ino = 1; ino < maxino; ino++) {
705                 if (((ino - 1) % NBBY) == 0)    /* map is offset by 1 */
706                         dirty = *map++;
707                 else
708                         dirty >>= 1;
709                 if ((dirty & 1) == 0)
710                         continue;
711                 /*
712                  * Skip inodes deleted and reallocated as directories.
713                  */
714                 dp = getino(ino);
715                 if ((dp->di_mode & IFMT) == IFDIR)
716                         continue;
717 #ifdef __linux__
718                 /*
719                  * No need to check here for deleted and not yes reallocated inodes
720                  * since this is done in dumpino().
721                  */
722 #endif
723                 (void)dumpino(dp, ino);
724         }
725
726 #ifdef __linux__
727         (void)time4(&(tend_writing));
728 #else
729         (void)time((time_t *)&(tend_writing));
730 #endif
731         spcl.c_type = TS_END;
732         for (i = 0; i < ntrec; i++)
733                 writeheader(maxino - 1);
734
735         tnow = trewind();
736
737         if (pipeout)
738                 msg("%ld tape blocks (%.2fMB)\n", spcl.c_tapea,
739                         ((double)spcl.c_tapea * TP_BSIZE / 1048576));
740         else
741                 msg("%ld tape blocks (%.2fMB) on %d volume(s)\n",
742                     spcl.c_tapea, 
743                     ((double)spcl.c_tapea * TP_BSIZE / 1048576),
744                     spcl.c_volume);
745
746         /* report dump performance, avoid division through zero */
747         if (tend_writing - tstart_writing == 0)
748                 msg("finished in less than a second\n");
749         else
750                 msg("finished in %d seconds, throughput %d KBytes/sec\n",
751                     tend_writing - tstart_writing,
752                     spcl.c_tapea / (tend_writing - tstart_writing));
753
754         putdumptime();
755 #ifdef __linux__
756         msg("Date of this level %c dump: %s", level,
757                 spcl.c_date == 0 ? "the epoch\n" : ctime4(&spcl.c_date));
758 #else
759         msg("Date of this level %c dump: %s", level,
760                 spcl.c_date == 0 ? "the epoch\n" : ctime(&spcl.c_date));
761 #endif
762         msg("Date this dump completed:  %s", ctime(&tnow));
763
764         msg("Average transfer rate: %ld KB/s\n", xferrate / tapeno);
765         if (compressed) {
766                 long tapekb = bytes_written / 1024;
767                 double rate = .0005 + (double) spcl.c_tapea / tapekb;
768                 msg("Wrote %ldKB uncompressed, %ldKB compressed,"
769                         " compression ratio %1.3f\n",
770                         spcl.c_tapea, tapekb, rate);
771         }
772
773         broadcast("DUMP IS DONE!\7\7\n");
774         msg("DUMP IS DONE\n");
775         Exit(X_FINOK);
776         /* NOTREACHED */
777         return 0;       /* gcc - shut up */
778 }
779
780 static void
781 usage(void)
782 {
783         char white[MAXPATHLEN];
784         int i;
785         
786         strncpy(white, __progname, MAXPATHLEN-1);
787         white[MAXPATHLEN-1] = '\0';
788         for (i=0; i<MAXPATHLEN; ++i)
789                 if (white[i] != '\0') white[i] = ' ';
790
791         fprintf(stderr,
792                 "%s %s\n", __progname, _DUMP_VERSION);
793         fprintf(stderr,
794                 "usage:\t%s [-0123456789ac"
795 #ifdef KERBEROS
796                 "k"
797 #endif
798                 "MnSu"
799 #ifdef HAVE_ZLIB
800                 "z"
801 #endif
802                 "] [-B records] [-b blocksize] [-d density]\n"
803                 "\t%s [-e inode#] [-f file] [-h level] [-s feet] [-T date] filesystem\n"
804                 "\t%s [-W | -w]\n", __progname, white, __progname);
805         exit(X_STARTUP);
806 }
807
808 /*
809  * Pick up a numeric argument.  It must be nonnegative and in the given
810  * range (except that a vmax of 0 means unlimited).
811  */
812 static long
813 numarg(const char *meaning, long vmin, long vmax)
814 {
815         char *p;
816         long val;
817
818         val = strtol(optarg, &p, 10);
819         if (*p)
820                 errx(X_STARTUP, "illegal %s -- %s", meaning, optarg);
821         if (val < vmin || (vmax && val > vmax))
822                 errx(X_STARTUP, "%s must be between %ld and %ld", meaning, vmin, vmax);
823         return (val);
824 }
825
826 void
827 sig(int signo)
828 {
829         switch(signo) {
830         case SIGALRM:
831         case SIGBUS:
832         case SIGFPE:
833         case SIGHUP:
834         case SIGTERM:
835         case SIGTRAP:
836                 if (pipeout)
837                         quit("Signal on pipe: cannot recover\n");
838                 msg("Rewriting attempted as response to unknown signal.\n");
839                 (void)fflush(stderr);
840                 (void)fflush(stdout);
841                 close_rewind();
842                 exit(X_REWRITE);
843                 /* NOTREACHED */
844         case SIGSEGV:
845                 msg("SIGSEGV: ABORTING!\n");
846                 (void)signal(SIGSEGV, SIG_DFL);
847                 (void)kill(0, SIGSEGV);
848                 /* NOTREACHED */
849         }
850 }
851
852 char *
853 rawname(char *cp)
854 {
855 #ifdef  __linux__
856         return cp;
857 #else   /* __linux__ */
858         static char rawbuf[MAXPATHLEN];
859         char *dp = strrchr(cp, '/');
860
861         if (dp == NULL)
862                 return (NULL);
863         *dp = '\0';
864         (void)strncpy(rawbuf, cp, MAXPATHLEN - 1);
865         rawbuf[MAXPATHLEN-1] = '\0';
866         *dp = '/';
867         (void)strncat(rawbuf, "/r", MAXPATHLEN - 1 - strlen(rawbuf));
868         (void)strncat(rawbuf, dp + 1, MAXPATHLEN - 1 - strlen(rawbuf));
869         return (rawbuf);
870 #endif  /* __linux__ */
871 }
872
873 /*
874  * obsolete --
875  *      Change set of key letters and ordered arguments into something
876  *      getopt(3) will like.
877  */
878 static void
879 obsolete(int *argcp, char **argvp[])
880 {
881         int argc, flags;
882         char *ap, **argv, *flagsp=NULL, **nargv, *p=NULL;
883
884         /* Setup. */
885         argv = *argvp;
886         argc = *argcp;
887
888         /* Return if no arguments or first argument has leading dash. */
889         ap = argv[1];
890         if (argc == 1 || *ap == '-')
891                 return;
892
893         /* Allocate space for new arguments. */
894         if ((*argvp = nargv = malloc((argc + 1) * sizeof(char *))) == NULL ||
895             (p = flagsp = malloc(strlen(ap) + 2)) == NULL)
896                 err(X_STARTUP, "malloc new args");
897
898         *nargv++ = *argv;
899         argv += 2;
900
901         for (flags = 0; *ap; ++ap) {
902                 switch (*ap) {
903                 case 'B':
904                 case 'b':
905                 case 'd':
906                 case 'e':
907                 case 'f':
908                 case 'F':
909                 case 'h':
910                 case 'L':
911                 case 's':
912                 case 'T':
913                         if (*argv == NULL) {
914                                 warnx("option requires an argument -- %c", *ap);
915                                 usage();
916                         }
917                         if ((nargv[0] = malloc(strlen(*argv) + 2 + 1)) == NULL)
918                                 err(X_STARTUP, "malloc arg");
919                         nargv[0][0] = '-';
920                         nargv[0][1] = *ap;
921                         (void)strcpy(&nargv[0][2], *argv);
922                         ++argv;
923                         ++nargv;
924                         break;
925                 default:
926                         if (!flags) {
927                                 *p++ = '-';
928                                 flags = 1;
929                         }
930                         *p++ = *ap;
931                         break;
932                 }
933         }
934
935         /* Terminate flags. */
936         if (flags) {
937                 *p = '\0';
938                 *nargv++ = flagsp;
939         }
940
941         /* Copy remaining arguments. */
942         while ((*nargv++ = *argv++));
943
944         /* Update argument count. */
945         *argcp = nargv - *argvp - 1;
946 }