386BSD 0.1 development
authorWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Thu, 25 Apr 1991 00:55:04 +0000 (16:55 -0800)
committerWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Thu, 25 Apr 1991 00:55:04 +0000 (16:55 -0800)
Work on file usr/src/sbin/dump/dumpitime.c
Work on file usr/src/sbin/dump/dumpmain.c
Work on file usr/src/sbin/dump/dumpoptr.c
Work on file usr/src/sbin/dump/dumprmt.c
Work on file usr/src/sbin/dump/dumptape.c
Work on file usr/src/sbin/dump/pathnames.h
Work on file usr/src/sbin/dump/dumptraverse.c
Work on file usr/src/sbin/dump/rdump.8
Work on file usr/src/sbin/dump/unctime.c

Co-Authored-By: Lynne Greer Jolitz <ljolitz@cardio.ucsf.edu>
Synthesized-from: 386BSD-0.1

usr/src/sbin/dump/dumpitime.c [new file with mode: 0644]
usr/src/sbin/dump/dumpmain.c [new file with mode: 0644]
usr/src/sbin/dump/dumpoptr.c [new file with mode: 0644]
usr/src/sbin/dump/dumprmt.c [new file with mode: 0644]
usr/src/sbin/dump/dumptape.c [new file with mode: 0644]
usr/src/sbin/dump/dumptraverse.c [new file with mode: 0644]
usr/src/sbin/dump/pathnames.h [new file with mode: 0644]
usr/src/sbin/dump/rdump.8 [new file with mode: 0644]
usr/src/sbin/dump/unctime.c [new file with mode: 0644]

diff --git a/usr/src/sbin/dump/dumpitime.c b/usr/src/sbin/dump/dumpitime.c
new file mode 100644 (file)
index 0000000..dc03861
--- /dev/null
@@ -0,0 +1,249 @@
+/*-
+ * Copyright (c) 1980 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)dumpitime.c        5.7 (Berkeley) 3/7/91";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <ufs/dir.h>
+#include <ufs/dinode.h>
+#include <fcntl.h>
+#include <protocols/dumprestore.h>
+#include <errno.h>
+#include <stdio.h>
+#ifdef __STDC__
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#endif
+#include "dump.h"
+
+struct dumpdates **ddatev = 0;
+int    nddates = 0;
+int    ddates_in = 0;
+struct dumptime *dthead = 0;
+
+void   readdumptimes();
+int    getrecord();
+int    makedumpdate();
+
+static void dumprecout();
+
+void
+initdumptimes()
+{
+       FILE *df;
+
+       if ((df = fopen(dumpdates, "r")) == NULL) {
+               if (errno == ENOENT) {
+                       msg("WARNING: no file `%s'\n", dumpdates);
+                       return;
+               }
+               quit("cannot read %s: %s\n", dumpdates, strerror(errno));
+               /* NOTREACHED */
+       }
+       (void) flock(fileno(df), LOCK_SH);
+       readdumptimes(df);
+       fclose(df);
+}
+
+void
+readdumptimes(df)
+       FILE *df;
+{
+       register int i;
+       register struct dumptime *dtwalk;
+
+       for (;;) {
+               dtwalk = (struct dumptime *)calloc(1, sizeof (struct dumptime));
+               if (getrecord(df, &(dtwalk->dt_value)) < 0)
+                       break;
+               nddates++;
+               dtwalk->dt_next = dthead;
+               dthead = dtwalk;
+       }
+
+       ddates_in = 1;
+       /*
+        *      arrayify the list, leaving enough room for the additional
+        *      record that we may have to add to the ddate structure
+        */
+       ddatev = (struct dumpdates **)
+               calloc(nddates + 1, sizeof (struct dumpdates *));
+       dtwalk = dthead;
+       for (i = nddates - 1; i >= 0; i--, dtwalk = dtwalk->dt_next)
+               ddatev[i] = &dtwalk->dt_value;
+}
+
+void
+getdumptime()
+{
+       register struct dumpdates *ddp;
+       register int i;
+       char *fname;
+
+       fname = disk;
+#ifdef FDEBUG
+       msg("Looking for name %s in dumpdates = %s for level = %c\n",
+               fname, dumpdates, level);
+#endif
+       spcl.c_ddate = 0;
+       lastlevel = '0';
+
+       initdumptimes();
+       /*
+        *      Go find the entry with the same name for a lower increment
+        *      and older date
+        */
+       ITITERATE(i, ddp) {
+               if (strncmp(fname, ddp->dd_name, sizeof (ddp->dd_name)) != 0)
+                       continue;
+               if (ddp->dd_level >= level)
+                       continue;
+               if (ddp->dd_ddate <= spcl.c_ddate)
+                       continue;
+               spcl.c_ddate = ddp->dd_ddate;
+               lastlevel = ddp->dd_level;
+       }
+}
+
+void
+putdumptime()
+{
+       FILE *df;
+       register struct dumpdates *dtwalk;
+       register int i;
+       int fd;
+       char *fname;
+
+       if(uflag == 0)
+               return;
+       if ((df = fopen(dumpdates, "r+")) == NULL)
+               quit("cannot rewrite %s: %s\n", dumpdates, strerror(errno));
+       fd = fileno(df);
+       (void) flock(fd, LOCK_EX);
+       fname = disk;
+       free(ddatev);
+       ddatev = 0;
+       nddates = 0;
+       dthead = 0;
+       ddates_in = 0;
+       readdumptimes(df);
+       if (fseek(df, 0L, 0) < 0)
+               quit("fseek: %s\n", strerror(errno));
+       spcl.c_ddate = 0;
+       ITITERATE(i, dtwalk) {
+               if (strncmp(fname, dtwalk->dd_name,
+                               sizeof (dtwalk->dd_name)) != 0)
+                       continue;
+               if (dtwalk->dd_level != level)
+                       continue;
+               goto found;
+       }
+       /*
+        *      construct the new upper bound;
+        *      Enough room has been allocated.
+        */
+       dtwalk = ddatev[nddates] =
+               (struct dumpdates *)calloc(1, sizeof(struct dumpdates));
+       nddates += 1;
+  found:
+       (void) strncpy(dtwalk->dd_name, fname, sizeof (dtwalk->dd_name));
+       dtwalk->dd_level = level;
+       dtwalk->dd_ddate = spcl.c_date;
+
+       ITITERATE(i, dtwalk) {
+               dumprecout(df, dtwalk);
+       }
+       if (fflush(df))
+               quit("%s: %s\n", dumpdates, strerror(errno));
+       if (ftruncate(fd, ftell(df)))
+               quit("ftruncate (%s): %s\n", dumpdates, strerror(errno));
+       (void) fclose(df);
+       msg("level %c dump on %s", level,
+               spcl.c_date == 0 ? "the epoch\n" : ctime(&spcl.c_date));
+}
+
+static void
+dumprecout(file, what)
+       FILE *file;
+       struct dumpdates *what;
+{
+
+       if (fprintf(file, DUMPOUTFMT,
+                   what->dd_name,
+                   what->dd_level,
+                   ctime(&what->dd_ddate)) < 0)
+               quit("%s: %s\n", dumpdates, strerror(errno));
+}
+
+int    recno;
+int
+getrecord(df, ddatep)
+       FILE *df;
+       struct dumpdates *ddatep;
+{
+       char buf[BUFSIZ];
+
+       recno = 0;
+       if ( (fgets(buf, BUFSIZ, df)) != buf)
+               return(-1);
+       recno++;
+       if (makedumpdate(ddatep, buf) < 0)
+               msg("Unknown intermediate format in %s, line %d\n",
+                       dumpdates, recno);
+
+#ifdef FDEBUG
+       msg("getrecord: %s %c %s", ddatep->dd_name, ddatep->dd_level,
+           ddatep->dd_ddate == 0 ? "the epoch\n" : ctime(&ddatep->dd_ddate));
+#endif
+       return(0);
+}
+
+time_t unctime();
+
+int
+makedumpdate(ddp, buf)
+       struct dumpdates *ddp;
+       char *buf;
+{
+       char un_buf[128];
+
+       sscanf(buf, DUMPINFMT, ddp->dd_name, &ddp->dd_level, un_buf);
+       ddp->dd_ddate = unctime(un_buf);
+       if (ddp->dd_ddate < 0)
+               return(-1);
+       return(0);
+}
diff --git a/usr/src/sbin/dump/dumpmain.c b/usr/src/sbin/dump/dumpmain.c
new file mode 100644 (file)
index 0000000..402a540
--- /dev/null
@@ -0,0 +1,496 @@
+/*-
+ * Copyright (c) 1980, 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1980, 1991 The Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)dumpmain.c 5.16 (Berkeley) 4/24/91";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <ufs/dir.h>
+#include <ufs/dinode.h>
+#include <ufs/fs.h>
+#include <protocols/dumprestore.h>
+#include <signal.h>
+#ifdef __STDC__
+#include <time.h>
+#endif
+#include <fcntl.h>
+#include <fstab.h>
+#include <stdio.h>
+#ifdef __STDC__
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#endif
+#include "dump.h"
+#include "pathnames.h"
+
+int    notify = 0;     /* notify operator flag */
+int    blockswritten = 0;      /* number of blocks written on current tape */
+int    tapeno = 0;     /* current tape number */
+int    density = 0;    /* density in bytes/0.1" */
+int    ntrec = NTREC;  /* # tape blocks in each tape record */
+int    cartridge = 0;  /* Assume non-cartridge tape */
+long   dev_bsize = 1;  /* recalculated below */
+long   blocksperfile;  /* output blocks per file */
+#ifdef RDUMP
+char   *host;
+int    rmthost();
+#endif
+
+main(argc, argv)
+       int argc;
+       char **argv;
+{
+       register ino_t ino;
+       register int dirty; 
+       register struct dinode *dp;
+       register struct fstab *dt;
+       register char *map;
+       register char *cp;
+       int i, anydirskipped, bflag = 0;
+       float fetapes;
+       ino_t maxino;
+
+       time(&(spcl.c_date));
+
+       tsize = 0;      /* Default later, based on 'c' option for cart tapes */
+       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';
+       argv++;
+       argc -= 2;
+       for (cp = *argv++; *cp; cp++) {
+               switch (*cp) {
+               case '-':
+                       continue;
+
+               case 'w':
+                       lastdump('w');  /* tell us only what has to be done */
+                       exit(0);
+
+               case 'W':               /* what to do */
+                       lastdump('W');  /* tell us state of what is done */
+                       exit(0);        /* do nothing else */
+
+               case 'f':               /* output file */
+                       if (argc < 1)
+                               break;
+                       tape = *argv++;
+                       argc--;
+                       continue;
+
+               case 'd':               /* density, in bits per inch */
+                       if (argc < 1)
+                               break;
+                       density = atoi(*argv) / 10;
+                       if (density < 1) {
+                               fprintf(stderr, "bad density \"%s\"\n", *argv);
+                               Exit(X_ABORT);
+                       }
+                       argc--;
+                       argv++;
+                       if (density >= 625 && !bflag)
+                               ntrec = HIGHDENSITYTREC;
+                       continue;
+
+               case 's':               /* tape size, feet */
+                       if (argc < 1)
+                               break;
+                       tsize = atol(*argv);
+                       if (tsize < 1) {
+                               fprintf(stderr, "bad size \"%s\"\n", *argv);
+                               Exit(X_ABORT);
+                       }
+                       argc--;
+                       argv++;
+                       tsize *= 12 * 10;
+                       continue;
+
+               case 'b':               /* blocks per tape write */
+                       if (argc < 1)
+                               break;
+                       bflag++;
+                       ntrec = atoi(*argv);
+                       if (ntrec < 1) {
+                               fprintf(stderr, "%s \"%s\"\n",
+                                   "bad number of blocks per write ", *argv);
+                               Exit(X_ABORT);
+                       }
+                       argc--;
+                       argv++;
+                       continue;
+
+               case 'B':               /* blocks per output file */
+                       if (argc < 1)
+                               break;
+                       blocksperfile = atol(*argv);
+                       if (blocksperfile < 1) {
+                               fprintf(stderr, "%s \"%s\"\n",
+                                   "bad number of blocks per file ", *argv);
+                               Exit(X_ABORT);
+                       }
+                       argc--;
+                       argv++;
+                       continue;
+
+               case 'c':               /* Tape is cart. not 9-track */
+                       cartridge++;
+                       continue;
+
+               case '0':               /* dump level */
+               case '1':
+               case '2':
+               case '3':
+               case '4':
+               case '5':
+               case '6':
+               case '7':
+               case '8':
+               case '9':
+                       level = *cp;
+                       continue;
+
+               case 'u':               /* update /etc/dumpdates */
+                       uflag++;
+                       continue;
+
+               case 'n':               /* notify operators */
+                       notify++;
+                       continue;
+
+               default:
+                       fprintf(stderr, "bad key '%c'\n", *cp);
+                       Exit(X_ABORT);
+               }
+               fprintf(stderr, "missing argument to '%c'\n", *cp);
+               Exit(X_ABORT);
+       }
+       if (argc < 1) {
+               fprintf(stderr, "Must specify disk or filesystem\n");
+               Exit(X_ABORT);
+       } else {
+               disk = *argv++;
+               argc--;
+       }
+       if (argc >= 1) {
+               fprintf(stderr, "Unknown arguments to dump:");
+               while (argc--)
+                       fprintf(stderr, " %s", *argv++);
+               fprintf(stderr, "\n");
+               Exit(X_ABORT);
+       }
+       if (strcmp(tape, "-") == 0) {
+               pipeout++;
+               tape = "standard output";
+       }
+
+       if (blocksperfile)
+               blocksperfile = blocksperfile / ntrec * ntrec; /* round down */
+       else {
+               /*
+                * 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;
+       }
+
+#ifdef RDUMP
+       { char *index();
+         host = tape;
+         tape = index(host, ':');
+         if (tape == 0) {
+               msg("need keyletter ``f'' and device ``host:tape''\n");
+               exit(1);
+         }
+         *tape++ = 0;
+         if (rmthost(host) == 0)
+               exit(X_ABORT);
+       }
+       setuid(getuid());       /* rmthost() is the only reason to be setuid */
+#endif
+       if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
+               signal(SIGHUP, sighup);
+       if (signal(SIGTRAP, SIG_IGN) != SIG_IGN)
+               signal(SIGTRAP, sigtrap);
+       if (signal(SIGFPE, SIG_IGN) != SIG_IGN)
+               signal(SIGFPE, sigfpe);
+       if (signal(SIGBUS, SIG_IGN) != SIG_IGN)
+               signal(SIGBUS, sigbus);
+       if (signal(SIGSEGV, SIG_IGN) != SIG_IGN)
+               signal(SIGSEGV, sigsegv);
+       if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
+               signal(SIGTERM, sigterm);
+       
+
+       if (signal(SIGINT, interrupt) == SIG_IGN)
+               signal(SIGINT, SIG_IGN);
+
+       set_operators();        /* /etc/group snarfed */
+       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 != 0) {
+               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);
+       }
+       strcpy(spcl.c_label, "none");
+       gethostname(spcl.c_host, NAMELEN);
+       spcl.c_level = level - '0';
+       spcl.c_type = TS_TAPE;
+       getdumptime();          /* /etc/dumpdates snarfed */
+
+       msg("Date of this level %c dump: %s", level,
+               spcl.c_date == 0 ? "the epoch\n" : ctime(&spcl.c_date));
+       msg("Date of last level %c dump: %s", lastlevel,
+               spcl.c_ddate == 0 ? "the epoch\n" : ctime(&spcl.c_ddate));
+       msg("Dumping %s ", disk);
+       if (dt != 0)
+               msgtail("(%s) ", dt->fs_file);
+#ifdef RDUMP
+       msgtail("to %s on host %s\n", tape, host);
+#else
+       msgtail("to %s\n", tape);
+#endif
+
+       if ((diskfd = open(disk, O_RDONLY)) < 0) {
+               msg("Cannot open %s\n", disk);
+               Exit(X_ABORT);
+       }
+       sync();
+       sblock = (struct fs *)buf;
+       bread(SBOFF, 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 (%d) 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 - 1;
+       mapsize = roundup(howmany(sblock->fs_ipg * sblock->fs_ncg, NBBY),
+               TP_BSIZE);
+       usedinomap = (char *)calloc(mapsize, sizeof(char));
+       dumpdirmap = (char *)calloc(mapsize, sizeof(char));
+       dumpinomap = (char *)calloc(mapsize, sizeof(char));
+       tapesize = 3 * (howmany(mapsize * sizeof(char), TP_BSIZE) + 1);
+
+       msg("mapping (Pass I) [regular files]\n");
+       anydirskipped = mapfiles(maxino, &tapesize);
+
+       msg("mapping (Pass II) [directories]\n");
+       while (anydirskipped) {
+               anydirskipped = mapdirs(maxino, &tapesize);
+       }
+
+       if (pipeout)
+               tapesize += 10; /* 10 trailer blocks */
+       else {
+               if (blocksperfile)
+                       fetapes = (float) 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 */
+       }
+       if (pipeout)
+               msg("estimated %ld tape blocks.\n", tapesize);
+       else
+               msg("estimated %ld tape blocks on %3.2f tape(s).\n",
+                   tapesize, fetapes);
+
+       alloctape();                    /* Allocate tape buffer */
+
+       startnewtape();
+       time(&(tstart_writing));
+       dumpmap(usedinomap, TS_CLRI, maxino);
+
+       msg("dumping (Pass III) [directories]\n");
+       for (map = dumpdirmap, ino = 0; ino < maxino; ) {
+               if ((ino % NBBY) == 0)
+                       dirty = *map++;
+               else
+                       dirty >>= 1;
+               ino++;
+               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);
+       }
+
+       msg("dumping (Pass IV) [regular files]\n");
+       for (map = dumpinomap, ino = 0; ino < maxino; ) {
+               if ((ino % NBBY) == 0)
+                       dirty = *map++;
+               else
+                       dirty >>= 1;
+               ino++;
+               if ((dirty & 1) == 0)
+                       continue;
+               /*
+                * Skip inodes deleted and reallocated as directories.
+                */
+               dp = getino(ino);
+               if ((dp->di_mode & IFMT) == IFDIR)
+                       continue;
+               dumpino(dp, ino);
+       }
+
+       spcl.c_type = TS_END;
+#ifndef RDUMP
+       for (i = 0; i < ntrec; i++)
+               writeheader(maxino);
+#endif
+       if (pipeout)
+               msg("DUMP: %ld tape blocks\n",spcl.c_tapea);
+       else
+               msg("DUMP: %ld tape blocks on %d volumes(s)\n",
+                   spcl.c_tapea, spcl.c_volume);
+       msg("DUMP IS DONE\n");
+
+       putdumptime();
+#ifndef RDUMP
+       if (!pipeout) {
+               close(tapefd);
+               trewind();
+       }
+#else
+       for (i = 0; i < ntrec; i++)
+               writeheader(curino);
+       trewind();
+#endif
+       broadcast("DUMP IS DONE!\7\7\n");
+       Exit(X_FINOK);
+       /* NOTREACHED */
+}
+
+void
+sigAbort()
+{
+       if (pipeout)
+               quit("Unknown signal, cannot recover\n");
+       msg("Rewriting attempted as response to unknown signal.\n");
+       fflush(stderr);
+       fflush(stdout);
+       close_rewind();
+       exit(X_REWRITE);
+}
+
+void   sighup(){       msg("SIGHUP()  try rewriting\n"); sigAbort();}
+void   sigtrap(){      msg("SIGTRAP()  try rewriting\n"); sigAbort();}
+void   sigfpe(){       msg("SIGFPE()  try rewriting\n"); sigAbort();}
+void   sigbus(){       msg("SIGBUS()  try rewriting\n"); sigAbort();}
+void   sigsegv(){      msg("SIGSEGV()  ABORTING!\n"); abort();}
+void   sigalrm(){      msg("SIGALRM()  try rewriting\n"); sigAbort();}
+void   sigterm(){      msg("SIGTERM()  try rewriting\n"); sigAbort();}
+
+char *
+rawname(cp)
+       char *cp;
+{
+       static char rawbuf[32];
+       char *rindex();
+       char *dp = rindex(cp, '/');
+
+       if (dp == 0)
+               return (0);
+       *dp = 0;
+       strcpy(rawbuf, cp);
+       *dp = '/';
+       strcat(rawbuf, "/r");
+       strcat(rawbuf, dp+1);
+       return (rawbuf);
+}
diff --git a/usr/src/sbin/dump/dumpoptr.c b/usr/src/sbin/dump/dumpoptr.c
new file mode 100644 (file)
index 0000000..b9a0739
--- /dev/null
@@ -0,0 +1,557 @@
+/*-
+ * Copyright (c) 1980, 1988 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)dumpoptr.c 5.8 (Berkeley) 3/7/91";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <ufs/dir.h>
+#include <signal.h>
+#include <time.h>
+#include <fstab.h>
+#include <grp.h>
+#include <varargs.h>
+#include <utmp.h>
+#include <tzfile.h>
+#include <errno.h>
+#include <stdio.h>
+#ifdef __STDC__
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#endif
+#include "dump.h"
+#include "pathnames.h"
+
+static void alarmcatch();
+static void sendmes();
+
+/*
+ *     Query the operator; This previously-fascist piece of code
+ *     no longer requires an exact response.
+ *     It is intended to protect dump aborting by inquisitive
+ *     people banging on the console terminal to see what is
+ *     happening which might cause dump to croak, destroying
+ *     a large number of hours of work.
+ *
+ *     Every 2 minutes we reprint the message, alerting others
+ *     that dump needs attention.
+ */
+int    timeout;
+char   *attnmessage;           /* attention message */
+
+int
+query(question)
+       char    *question;
+{
+       char    replybuffer[64];
+       int     back, errcount;
+       FILE    *mytty;
+
+       if ((mytty = fopen(_PATH_TTY, "r")) == NULL)
+               quit("fopen on %s fails: %s\n", _PATH_TTY, strerror(errno));
+       attnmessage = question;
+       timeout = 0;
+       alarmcatch();
+       back = -1;
+       errcount = 0;
+       do {
+               if (fgets(replybuffer, 63, mytty) == NULL) {
+                       clearerr(mytty);
+                       if (++errcount > 30)    /* XXX  ugly */
+                               quit("excessive operator query failures\n");
+               } else if (replybuffer[0] == 'y' || replybuffer[0] == 'Y') {
+                       back = 1;
+               } else if (replybuffer[0] == 'n' || replybuffer[0] == 'N') {
+                       back = 0;
+               } else {
+                       (void) fprintf(stderr,
+                           "  DUMP: \"Yes\" or \"No\"?\n");
+                       (void) fprintf(stderr,
+                           "  DUMP: %s: (\"yes\" or \"no\") ", question);
+               }
+       } while (back < 0);
+
+       /*
+        *      Turn off the alarm, and reset the signal to trap out..
+        */
+       alarm(0);
+       if (signal(SIGALRM, sigalrm) == SIG_IGN)
+               signal(SIGALRM, SIG_IGN);
+       fclose(mytty);
+       return(back);
+}
+
+char lastmsg[100];
+
+/*
+ *     Alert the console operator, and enable the alarm clock to
+ *     sleep for 2 minutes in case nobody comes to satisfy dump
+ */
+static void
+alarmcatch()
+{
+       if (notify == 0) {
+               if (timeout == 0)
+                       (void) fprintf(stderr,
+                           "  DUMP: %s: (\"yes\" or \"no\") ",
+                           attnmessage);
+               else
+                       msgtail("\7\7");
+       } else {
+               if (timeout) {
+                       msgtail("\n");
+                       broadcast("");          /* just print last msg */
+               }
+               (void) fprintf(stderr,"  DUMP: %s: (\"yes\" or \"no\") ",
+                   attnmessage);
+       }
+       signal(SIGALRM, alarmcatch);
+       alarm(120);
+       timeout = 1;
+}
+
+/*
+ *     Here if an inquisitive operator interrupts the dump program
+ */
+void
+interrupt()
+{
+       msg("Interrupt received.\n");
+       if (query("Do you want to abort dump?"))
+               dumpabort();
+}
+
+/*
+ *     The following variables and routines manage alerting
+ *     operators to the status of dump.
+ *     This works much like wall(1) does.
+ */
+struct group *gp;
+
+/*
+ *     Get the names from the group entry "operator" to notify.
+ */    
+void
+set_operators()
+{
+       if (!notify)            /*not going to notify*/
+               return;
+       gp = getgrnam(OPGRENT);
+       endgrent();
+       if (gp == NULL) {
+               msg("No group entry for %s.\n", OPGRENT);
+               notify = 0;
+               return;
+       }
+}
+
+struct tm *localtime();
+struct tm *localclock;
+
+/*
+ *     We fork a child to do the actual broadcasting, so
+ *     that the process control groups are not messed up
+ */
+void
+broadcast(message)
+       char    *message;
+{
+       time_t          clock;
+       FILE    *f_utmp;
+       struct  utmp    utmp;
+       int     nusers;
+       char    **np;
+       int     pid, s;
+
+       if (!notify || gp == NULL)
+               return;
+
+       switch (pid = fork()) {
+       case -1:
+               return;
+       case 0:
+               break;
+       default:
+               while (wait(&s) != pid)
+                       continue;
+               return;
+       }
+
+       clock = time(0);
+       localclock = localtime(&clock);
+
+       if ((f_utmp = fopen(_PATH_UTMP, "r")) == NULL) {
+               msg("Cannot open %s: %s\n", _PATH_UTMP, strerror(errno));
+               return;
+       }
+
+       nusers = 0;
+       while (!feof(f_utmp)) {
+               if (fread(&utmp, sizeof (struct utmp), 1, f_utmp) != 1)
+                       break;
+               if (utmp.ut_name[0] == 0)
+                       continue;
+               nusers++;
+               for (np = gp->gr_mem; *np; np++) {
+                       if (strncmp(*np, utmp.ut_name, sizeof(utmp.ut_name)) != 0)
+                               continue;
+                       /*
+                        *      Do not send messages to operators on dialups
+                        */
+                       if (strncmp(utmp.ut_line, DIALUP, strlen(DIALUP)) == 0)
+                               continue;
+#ifdef DEBUG
+                       msg("Message to %s at %s\n", *np, utmp.ut_line);
+#endif
+                       sendmes(utmp.ut_line, message);
+               }
+       }
+       (void) fclose(f_utmp);
+       Exit(0);        /* the wait in this same routine will catch this */
+       /* NOTREACHED */
+}
+
+static void
+sendmes(tty, message)
+       char *tty, *message;
+{
+       char t[50], buf[BUFSIZ];
+       register char *cp;
+       int lmsg = 1;
+       FILE *f_tty;
+
+       (void) strcpy(t, _PATH_DEV);
+       (void) strcat(t, tty);
+
+       if ((f_tty = fopen(t, "w")) != NULL) {
+               setbuf(f_tty, buf);
+               (void) fprintf(f_tty,
+                   "\n\
+\7\7\7Message from the dump program to all operators at %d:%02d ...\r\n\n\
+DUMP: NEEDS ATTENTION: ",
+                   localclock->tm_hour, localclock->tm_min);
+               for (cp = lastmsg; ; cp++) {
+                       if (*cp == '\0') {
+                               if (lmsg) {
+                                       cp = message;
+                                       if (*cp == '\0')
+                                               break;
+                                       lmsg = 0;
+                               } else
+                                       break;
+                       }
+                       if (*cp == '\n')
+                               (void) putc('\r', f_tty);
+                       (void) putc(*cp, f_tty);
+               }
+               (void) fclose(f_tty);
+       }
+}
+
+/*
+ *     print out an estimate of the amount of time left to do the dump
+ */
+
+time_t tschedule = 0;
+
+void
+timeest()
+{
+       time_t  tnow, deltat;
+
+       time (&tnow);
+       if (tnow >= tschedule) {
+               tschedule = tnow + 300;
+               if (blockswritten < 500)
+                       return; 
+               deltat = tstart_writing - tnow +
+                       (1.0 * (tnow - tstart_writing))
+                       / blockswritten * tapesize;
+               msg("%3.2f%% done, finished in %d:%02d\n",
+                       (blockswritten * 100.0) / tapesize,
+                       deltat / 3600, (deltat % 3600) / 60);
+       }
+}
+
+/*
+ *     tapesize: total number of blocks estimated over all reels
+ *     blockswritten:  blocks actually written, over all reels
+ *     etapes: estimated number of tapes to write
+ *
+ *     tsize:  blocks can write on this reel
+ *     asize:  blocks written on this reel
+ *     tapeno: number of tapes written so far
+ */
+int
+blocksontape()
+{
+       if (tapeno == etapes)
+               return (tapesize - (etapes - 1) * tsize);
+       return (tsize);
+}
+
+#ifdef lint
+
+/* VARARGS1 */
+void msg(fmt) char *fmt; { strcpy(lastmsg, fmt); }
+
+/* VARARGS1 */
+void msgtail(fmt) char *fmt; { fmt = fmt; }
+
+void quit(fmt) char *fmt; { msg(fmt); dumpabort(); }
+
+#else /* lint */
+
+void
+msg(va_alist)
+       va_dcl
+{
+       va_list ap;
+       char *fmt;
+
+       (void) fprintf(stderr,"  DUMP: ");
+#ifdef TDEBUG
+       (void) fprintf(stderr, "pid=%d ", getpid());
+#endif
+       va_start(ap);
+       fmt = va_arg(ap, char *);
+       (void) vfprintf(stderr, fmt, ap);
+       va_end(ap);
+       (void) fflush(stdout);
+       (void) fflush(stderr);
+       va_start(ap);
+       fmt = va_arg(ap, char *);
+       (void) vsprintf(lastmsg, fmt, ap);
+       va_end(ap);
+}
+
+void
+msgtail(va_alist)
+       va_dcl
+{
+       va_list ap;
+       char *fmt;
+
+       va_start(ap);
+       fmt = va_arg(ap, char *);
+       (void) vfprintf(stderr, fmt, ap);
+       va_end(ap);
+}
+
+void
+quit(va_alist)
+       va_dcl
+{
+       va_list ap;
+       char *fmt;
+
+       (void) fprintf(stderr,"  DUMP: ");
+#ifdef TDEBUG
+       (void) fprintf(stderr, "pid=%d ", getpid());
+#endif
+       va_start(ap);
+       fmt = va_arg(ap, char *);
+       vfprintf(stderr, fmt, ap);
+       va_end(ap);
+       (void) fflush(stdout);
+       (void) fflush(stderr);
+       dumpabort();
+}
+
+#endif /* lint */
+
+/*
+ *     Tell the operator what has to be done;
+ *     we don't actually do it
+ */
+
+struct fstab *
+allocfsent(fs)
+       register struct fstab *fs;
+{
+       register struct fstab *new;
+
+       new = (struct fstab *)malloc(sizeof (*fs));
+       if (new == NULL ||
+           (new->fs_file = strdup(fs->fs_file)) == NULL ||
+           (new->fs_type = strdup(fs->fs_type)) == NULL ||
+           (new->fs_spec = strdup(fs->fs_spec)) == NULL)
+               quit("%s\n", strerror(errno));
+       new->fs_passno = fs->fs_passno;
+       new->fs_freq = fs->fs_freq;
+       return (new);
+}
+
+struct pfstab {
+       struct  pfstab *pf_next;
+       struct  fstab *pf_fstab;
+};
+
+static struct pfstab *table;
+
+void
+getfstab()
+{
+       register struct fstab *fs;
+       register struct pfstab *pf;
+
+       if (setfsent() == 0) {
+               msg("Can't open %s for dump table information: %s\n",
+                   _PATH_FSTAB, strerror(errno));
+               return;
+       }
+       while (fs = getfsent()) {
+               if (strcmp(fs->fs_type, FSTAB_RW) &&
+                   strcmp(fs->fs_type, FSTAB_RO) &&
+                   strcmp(fs->fs_type, FSTAB_RQ))
+                       continue;
+               fs = allocfsent(fs);
+               if ((pf = (struct pfstab *)malloc(sizeof (*pf))) == NULL)
+                       quit("%s\n", strerror(errno));
+               pf->pf_fstab = fs;
+               pf->pf_next = table;
+               table = pf;
+       }
+       endfsent();
+}
+
+/*
+ * Search in the fstab for a file name.
+ * This file name can be either the special or the path file name.
+ *
+ * The entries in the fstab are the BLOCK special names, not the
+ * character special names.
+ * The caller of fstabsearch assures that the character device
+ * is dumped (that is much faster)
+ *
+ * The file name can omit the leading '/'.
+ */
+struct fstab *
+fstabsearch(key)
+       char *key;
+{
+       register struct pfstab *pf;
+       register struct fstab *fs;
+       char *rawname();
+
+       for (pf = table; pf != NULL; pf = pf->pf_next) {
+               fs = pf->pf_fstab;
+               if (strcmp(fs->fs_file, key) == 0 ||
+                   strcmp(fs->fs_spec, key) == 0 ||
+                   strcmp(rawname(fs->fs_spec), key) == 0)
+                       return (fs);
+               if (key[0] != '/') {
+                       if (*fs->fs_spec == '/' &&
+                           strcmp(fs->fs_spec + 1, key) == 0)
+                               return (fs);
+                       if (*fs->fs_file == '/' &&
+                           strcmp(fs->fs_file + 1, key) == 0)
+                               return (fs);
+               }
+       }
+       return (NULL);
+}
+
+/*
+ *     Tell the operator what to do
+ */
+void
+lastdump(arg)
+       char    arg;    /* w ==> just what to do; W ==> most recent dumps */
+{
+       register int i;
+       register struct fstab *dt;
+       register struct dumpdates *dtwalk;
+       char *lastname, *date;
+       int dumpme, datesort();
+       time_t tnow;
+
+       time(&tnow);
+       getfstab();             /* /etc/fstab input */
+       initdumptimes();        /* /etc/dumpdates input */
+       qsort(ddatev, nddates, sizeof(struct dumpdates *), datesort);
+
+       if (arg == 'w')
+               (void) printf("Dump these file systems:\n");
+       else
+               (void) printf("Last dump(s) done (Dump '>' file systems):\n");
+       lastname = "??";
+       ITITERATE(i, dtwalk) {
+               if (strncmp(lastname, dtwalk->dd_name,
+                   sizeof(dtwalk->dd_name)) == 0)
+                       continue;
+               date = (char *)ctime(&dtwalk->dd_ddate);
+               date[16] = '\0';        /* blast away seconds and year */
+               lastname = dtwalk->dd_name;
+               dt = fstabsearch(dtwalk->dd_name);
+               dumpme = (dt != NULL &&
+                   dt->fs_freq != 0 &&
+                   dtwalk->dd_ddate < tnow - (dt->fs_freq * SECSPERDAY));
+               if (arg != 'w' || dumpme)
+                       (void) printf(
+                           "%c %8s\t(%6s) Last dump: Level %c, Date %s\n",
+                           dumpme && (arg != 'w') ? '>' : ' ',
+                           dtwalk->dd_name,
+                           dt ? dt->fs_file : "",
+                           dtwalk->dd_level,
+                           date);
+       }
+}
+
+int
+datesort(a1, a2)
+       void *a1, *a2;
+{
+       struct dumpdates *d1 = *(struct dumpdates **)a1;
+       struct dumpdates *d2 = *(struct dumpdates **)a2;
+       int diff;
+
+       diff = strncmp(d1->dd_name, d2->dd_name, sizeof(d1->dd_name));
+       if (diff == 0)
+               return (d2->dd_ddate - d1->dd_ddate);
+       return (diff);
+}
+
+int max(a, b)
+       int a, b;
+{
+       return (a > b ? a : b);
+}
+int min(a, b)
+       int a, b;
+{
+       return (a < b ? a : b);
+}
diff --git a/usr/src/sbin/dump/dumprmt.c b/usr/src/sbin/dump/dumprmt.c
new file mode 100644 (file)
index 0000000..316813f
--- /dev/null
@@ -0,0 +1,304 @@
+/*-
+ * Copyright (c) 1980 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)dumprmt.c  5.11 (Berkeley) 3/7/91";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mtio.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <ufs/dinode.h>
+#include <signal.h>
+
+#include <netinet/in.h>
+
+#include <netdb.h>
+#include <protocols/dumprestore.h>
+#include <pwd.h>
+#include <stdio.h>
+#ifdef __STDC__
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#endif
+#include "pathnames.h"
+
+#define        TS_CLOSED       0
+#define        TS_OPEN         1
+
+static int rmtstate = TS_CLOSED;
+int    rmtape;
+void   rmtgetconn();
+void   rmtconnaborted();
+int    rmtreply();
+int    rmtgetb();
+void   rmtgets();
+int    rmtcall();
+char   *rmtpeer;
+
+extern int ntrec;              /* blocking factor on tape */
+extern void msg();
+
+int
+rmthost(host)
+       char *host;
+{
+
+       rmtpeer = host;
+       signal(SIGPIPE, rmtconnaborted);
+       rmtgetconn();
+       if (rmtape < 0)
+               return (0);
+       return (1);
+}
+
+void
+rmtconnaborted()
+{
+
+       fprintf(stderr, "rdump: Lost connection to remote host.\n");
+       exit(1);
+}
+
+void
+rmtgetconn()
+{
+       static struct servent *sp = 0;
+       struct passwd *pw;
+       char *name = "root";
+       int size;
+
+       if (sp == 0) {
+               sp = getservbyname("shell", "tcp");
+               if (sp == 0) {
+                       fprintf(stderr, "rdump: shell/tcp: unknown service\n");
+                       exit(1);
+               }
+       }
+       pw = getpwuid(getuid());
+       if (pw && pw->pw_name)
+               name = pw->pw_name;
+       rmtape = rcmd(&rmtpeer, sp->s_port, name, name, _PATH_RMT, 0);
+       size = ntrec * TP_BSIZE;
+       while (size > TP_BSIZE &&
+           setsockopt(rmtape, SOL_SOCKET, SO_SNDBUF, &size, sizeof (size)) < 0)
+               size -= TP_BSIZE;
+}
+
+int
+rmtopen(tape, mode)
+       char *tape;
+       int mode;
+{
+       char buf[256];
+
+       (void)sprintf(buf, "O%s\n%d\n", tape, mode);
+       rmtstate = TS_OPEN;
+       return (rmtcall(tape, buf));
+}
+
+void
+rmtclose()
+{
+
+       if (rmtstate != TS_OPEN)
+               return;
+       rmtcall("close", "C\n");
+       rmtstate = TS_CLOSED;
+}
+
+int
+rmtread(buf, count)
+       char *buf;
+       int count;
+{
+       char line[30];
+       int n, i, cc;
+       extern errno;
+
+       (void)sprintf(line, "R%d\n", count);
+       n = rmtcall("read", line);
+       if (n < 0) {
+               errno = n;
+               return (-1);
+       }
+       for (i = 0; i < n; i += cc) {
+               cc = read(rmtape, buf+i, n - i);
+               if (cc <= 0) {
+                       rmtconnaborted();
+               }
+       }
+       return (n);
+}
+
+int
+rmtwrite(buf, count)
+       char *buf;
+       int count;
+{
+       char line[30];
+
+       (void)sprintf(line, "W%d\n", count);
+       write(rmtape, line, strlen(line));
+       write(rmtape, buf, count);
+       return (rmtreply("write"));
+}
+
+void
+rmtwrite0(count)
+       int count;
+{
+       char line[30];
+
+       (void)sprintf(line, "W%d\n", count);
+       write(rmtape, line, strlen(line));
+}
+
+void
+rmtwrite1(buf, count)
+       char *buf;
+       int count;
+{
+
+       write(rmtape, buf, count);
+}
+
+int
+rmtwrite2()
+{
+
+       return (rmtreply("write"));
+}
+
+int
+rmtseek(offset, pos)
+       int offset, pos;
+{
+       char line[80];
+
+       (void)sprintf(line, "L%d\n%d\n", offset, pos);
+       return (rmtcall("seek", line));
+}
+
+struct mtget mts;
+
+struct mtget *
+rmtstatus()
+{
+       register int i;
+       register char *cp;
+
+       if (rmtstate != TS_OPEN)
+               return (0);
+       rmtcall("status", "S\n");
+       for (i = 0, cp = (char *)&mts; i < sizeof(mts); i++)
+               *cp++ = rmtgetb();
+       return (&mts);
+}
+
+int
+rmtioctl(cmd, count)
+       int cmd, count;
+{
+       char buf[256];
+
+       if (count < 0)
+               return (-1);
+       (void)sprintf(buf, "I%d\n%d\n", cmd, count);
+       return (rmtcall("ioctl", buf));
+}
+
+int
+rmtcall(cmd, buf)
+       char *cmd, *buf;
+{
+
+       if (write(rmtape, buf, strlen(buf)) != strlen(buf))
+               rmtconnaborted();
+       return (rmtreply(cmd));
+}
+
+int
+rmtreply(cmd)
+       char *cmd;
+{
+       char code[30], emsg[BUFSIZ];
+
+       rmtgets(code, sizeof (code));
+       if (*code == 'E' || *code == 'F') {
+               rmtgets(emsg, sizeof (emsg));
+               msg("%s: %s\n", cmd, emsg, code + 1);
+               if (*code == 'F') {
+                       rmtstate = TS_CLOSED;
+                       return (-1);
+               }
+               return (-1);
+       }
+       if (*code != 'A') {
+               msg("Protocol to remote tape server botched (code %s?).\n",
+                   code);
+               rmtconnaborted();
+       }
+       return (atoi(code + 1));
+}
+
+int
+rmtgetb()
+{
+       char c;
+
+       if (read(rmtape, &c, 1) != 1)
+               rmtconnaborted();
+       return (c);
+}
+
+void
+rmtgets(cp, len)
+       char *cp;
+       int len;
+{
+
+       while (len > 1) {
+               *cp = rmtgetb();
+               if (*cp == '\n') {
+                       cp[1] = 0;
+                       return;
+               }
+               cp++;
+               len--;
+       }
+       msg("Protocol to remote tape server botched (in rmtgets).\n");
+       rmtconnaborted();
+}
diff --git a/usr/src/sbin/dump/dumptape.c b/usr/src/sbin/dump/dumptape.c
new file mode 100644 (file)
index 0000000..6a8144a
--- /dev/null
@@ -0,0 +1,575 @@
+/*-
+ * Copyright (c) 1980, 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)dumptape.c 5.18 (Berkeley) 4/24/91";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <ufs/dir.h>
+#include <ufs/dinode.h>
+#include <ufs/fs.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <protocols/dumprestore.h>
+#include <errno.h>
+#ifdef __STDC__
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#endif
+#include "dump.h"
+#include "pathnames.h"
+
+char   (*tblock)[TP_BSIZE];    /* pointer to malloc()ed buffer for tape */
+int    writesize;              /* size of malloc()ed buffer for tape */
+long   lastspclrec = -1;       /* tape block number of last written header */
+int    trecno = 0;             /* next record to write in current block */
+extern long blocksperfile;     /* number of blocks per output file */
+long   blocksthisvol;          /* number of blocks on current output file */
+extern int ntrec;              /* blocking factor on tape */
+extern int cartridge;
+char   *nexttape;
+#ifdef RDUMP
+extern char *host;
+int    rmtopen(), rmtwrite();
+void   rmtclose();
+#endif RDUMP
+
+int    atomic();
+void   doslave(), enslave(), flushtape(), killall();
+
+/*
+ * Concurrent dump mods (Caltech) - disk block reading and tape writing
+ * are exported to several slave processes.  While one slave writes the
+ * tape, the others read disk blocks; they pass control of the tape in
+ * a ring via flock(). The parent process traverses the filesystem and
+ * sends writeheader()'s and lists of daddr's to the slaves via pipes.
+ */
+struct req {                   /* instruction packets sent to slaves */
+       daddr_t dblk;
+       int count;
+} *req;
+int reqsiz;
+
+#define SLAVES 3               /* 1 slave writing, 1 reading, 1 for slack */
+int slavefd[SLAVES];           /* pipes from master to each slave */
+int slavepid[SLAVES];          /* used by killall() */
+int rotor;                     /* next slave to be instructed */
+int master;                    /* pid of master, for sending error signals */
+int tenths;                    /* length of tape used per block written */
+
+int
+alloctape()
+{
+       int pgoff = getpagesize() - 1;
+
+       writesize = ntrec * TP_BSIZE;
+       reqsiz = ntrec * sizeof(struct req);
+       /*
+        * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode
+        * (see DEC TU80 User's Guide).  The shorter gaps of 6250-bpi require
+        * repositioning after stopping, i.e, streaming mode, where the gap is
+        * variable, 0.30" to 0.45".  The gap is maximal when the tape stops.
+        */
+       if (blocksperfile == 0)
+               tenths = writesize / density +
+                   (cartridge ? 16 : density == 625 ? 5 : 8);
+       /*
+        * Allocate tape buffer contiguous with the array of instruction
+        * packets, so flushtape() can write them together with one write().
+        * Align tape buffer on page boundary to speed up tape write().
+        */
+       req = (struct req *)malloc(reqsiz + writesize + pgoff);
+       if (req == NULL)
+               return(0);
+       tblock = (char (*)[TP_BSIZE]) (((long)&req[ntrec] + pgoff) &~ pgoff);
+       req = (struct req *)tblock - ntrec;
+       return(1);
+}
+
+
+void
+writerec(dp)
+       char *dp;
+{
+       req[trecno].dblk = (daddr_t)0;
+       req[trecno].count = 1;
+       *(union u_spcl *)(*tblock++) = *(union u_spcl *)dp;     /* movc3 */
+       lastspclrec = spcl.c_tapea;
+       trecno++;
+       spcl.c_tapea++;
+       if (trecno >= ntrec)
+               flushtape();
+}
+
+void
+dumpblock(blkno, size)
+       daddr_t blkno;
+       int size;
+{
+       int avail, tpblks, dblkno;
+
+       dblkno = fsbtodb(sblock, blkno);
+       tpblks = size >> tp_bshift;
+       while ((avail = MIN(tpblks, ntrec - trecno)) > 0) {
+               req[trecno].dblk = dblkno;
+               req[trecno].count = avail;
+               trecno += avail;
+               spcl.c_tapea += avail;
+               if (trecno >= ntrec)
+                       flushtape();
+               dblkno += avail << (tp_bshift - dev_bshift);
+               tpblks -= avail;
+       }
+}
+
+int    nogripe = 0;
+
+void
+tperror()
+{
+       if (pipeout) {
+               msg("write error on %s\n", tape);
+               quit("Cannot recover\n");
+               /* NOTREACHED */
+       }
+       msg("write error %d blocks into volume %d\n", blocksthisvol, tapeno);
+       broadcast("DUMP WRITE ERROR!\n");
+       if (!query("Do you want to restart?"))
+               dumpabort();
+       msg("Closing this volume.  Prepare to restart with new media;\n");
+       msg("this dump volume will be rewritten.\n");
+       killall();
+       nogripe = 1;
+       close_rewind();
+       Exit(X_REWRITE);
+}
+
+void
+sigpipe()
+{
+
+       quit("Broken pipe\n");
+}
+
+void
+flushtape()
+{
+#ifndef __STDC__
+       int write();
+#endif
+
+       int siz = (char *)tblock - (char *)req;
+
+       if (atomic(write, slavefd[rotor], req, siz) != siz)
+               quit("error writing command pipe: %s\n", strerror(errno));
+       if (++rotor >= SLAVES)
+               rotor = 0;
+       tblock = (char (*)[TP_BSIZE]) &req[ntrec];
+       trecno = 0;
+       asize += tenths;
+       blockswritten += ntrec;
+       blocksthisvol += ntrec;
+       if (!pipeout && (blocksperfile ?
+           (blocksthisvol >= blocksperfile) : (asize > tsize))) {
+               close_rewind();
+               startnewtape();
+       }
+       timeest();
+}
+
+void
+trewind()
+{
+       int f;
+
+       if (pipeout)
+               return;
+       for (f = 0; f < SLAVES; f++)
+               close(slavefd[f]);
+       while (wait((int *)NULL) >= 0)  /* wait for any signals from slaves */
+               /* void */;
+       msg("Closing %s\n", tape);
+#ifdef RDUMP
+       if (host) {
+               rmtclose();
+               while (rmtopen(tape, 0) < 0)
+                       sleep(10);
+               rmtclose();
+               return;
+       }
+#endif RDUMP
+       close(tapefd);
+       while ((f = open(tape, 0)) < 0)
+               sleep (10);
+       close(f);
+}
+
+void
+close_rewind()
+{
+       trewind();
+       if (nexttape)
+               return;
+       if (!nogripe) {
+               msg("Change Volumes: Mount volume #%d\n", tapeno+1);
+               broadcast("CHANGE DUMP VOLUMES!\7\7\n");
+       }
+       while (!query("Is the new volume mounted and ready to go?"))
+               if (query("Do you want to abort?")) {
+                       dumpabort();
+                       /*NOTREACHED*/
+               }
+}
+
+/*
+ *     We implement taking and restoring checkpoints on the tape level.
+ *     When each tape is opened, a new process is created by forking; this
+ *     saves all of the necessary context in the parent.  The child
+ *     continues the dump; the parent waits around, saving the context.
+ *     If the child returns X_REWRITE, then it had problems writing that tape;
+ *     this causes the parent to fork again, duplicating the context, and
+ *     everything continues as if nothing had happened.
+ */
+
+void
+startnewtape()
+{
+       int     parentpid;
+       int     childpid;
+       int     status;
+       int     waitpid;
+       sig_t   interrupt;
+       int     blks, i;
+       char    *p;
+
+       interrupt = signal(SIGINT, SIG_IGN);
+       parentpid = getpid();
+
+    restore_check_point:
+       (void)signal(SIGINT, interrupt);
+       /*
+        *      All signals are inherited...
+        */
+       childpid = fork();
+       if (childpid < 0) {
+               msg("Context save fork fails in parent %d\n", parentpid);
+               Exit(X_ABORT);
+       }
+       if (childpid != 0) {
+               /*
+                *      PARENT:
+                *      save the context by waiting
+                *      until the child doing all of the work returns.
+                *      don't catch the interrupt
+                */
+               signal(SIGINT, SIG_IGN);
+#ifdef TDEBUG
+               msg("Tape: %d; parent process: %d child process %d\n",
+                       tapeno+1, parentpid, childpid);
+#endif TDEBUG
+               while ((waitpid = wait(&status)) != childpid)
+                       msg("Parent %d waiting for child %d has another child %d return\n",
+                               parentpid, childpid, waitpid);
+               if (status & 0xFF) {
+                       msg("Child %d returns LOB status %o\n",
+                               childpid, status&0xFF);
+               }
+               status = (status >> 8) & 0xFF;
+#ifdef TDEBUG
+               switch(status) {
+                       case X_FINOK:
+                               msg("Child %d finishes X_FINOK\n", childpid);
+                               break;
+                       case X_ABORT:
+                               msg("Child %d finishes X_ABORT\n", childpid);
+                               break;
+                       case X_REWRITE:
+                               msg("Child %d finishes X_REWRITE\n", childpid);
+                               break;
+                       default:
+                               msg("Child %d finishes unknown %d\n",
+                                       childpid, status);
+                               break;
+               }
+#endif TDEBUG
+               switch(status) {
+                       case X_FINOK:
+                               Exit(X_FINOK);
+                       case X_ABORT:
+                               Exit(X_ABORT);
+                       case X_REWRITE:
+                               goto restore_check_point;
+                       default:
+                               msg("Bad return code from dump: %d\n", status);
+                               Exit(X_ABORT);
+               }
+               /*NOTREACHED*/
+       } else {        /* we are the child; just continue */
+#ifdef TDEBUG
+               sleep(4);       /* allow time for parent's message to get out */
+               msg("Child on Tape %d has parent %d, my pid = %d\n",
+                       tapeno+1, parentpid, getpid());
+#endif TDEBUG
+               /*
+                * If we have a name like "/dev/rmt0,/dev/rmt1",
+                * use the name before the comma first, and save
+                * the remaining names for subsequent volumes.
+                */
+               tapeno++;               /* current tape sequence */
+               if (nexttape || index(tape, ',')) {
+                       if (nexttape && *nexttape)
+                               tape = nexttape;
+                       if (p = index(tape, ',')) {
+                               *p = '\0';
+                               nexttape = p + 1;
+                       } else
+                               nexttape = NULL;
+                       msg("Dumping volume %d on %s\n", tapeno, tape);
+               }
+#ifdef RDUMP
+               while ((tapefd = (host ? rmtopen(tape, 2) :
+                       pipeout ? 1 : open(tape, O_WRONLY|O_CREAT, 0666))) < 0)
+#else RDUMP
+               while ((tapefd =
+                       pipeout ? 1 : open(tape, O_WRONLY|O_CREAT, 0666)) < 0)
+#endif RDUMP
+                   {
+                       msg("Cannot open output \"%s\".\n", tape);
+                       if (!query("Do you want to retry the open?"))
+                               dumpabort();
+               }
+
+               enslave();  /* Share open tape file descriptor with slaves */
+
+               asize = 0;
+               blocksthisvol = 0;
+               newtape++;              /* new tape signal */
+               blks = 0;
+               if (spcl.c_type != TS_END)
+                       for (i = 0; i < spcl.c_count; i++)
+                               if (spcl.c_addr[i] != 0)
+                                       blks++;
+               spcl.c_count = blks + 1 - spcl.c_tapea + lastspclrec;
+               spcl.c_volume++;
+               spcl.c_type = TS_TAPE;
+               spcl.c_flags |= DR_NEWHEADER;
+               writeheader(curino);
+               spcl.c_flags &=~ DR_NEWHEADER;
+               if (tapeno > 1)
+                       msg("Volume %d begins with blocks from inode %d\n",
+                               tapeno, curino);
+       }
+}
+
+void
+dumpabort()
+{
+       if (master != 0 && master != getpid())
+               kill(master, SIGTERM);  /* Signals master to call dumpabort */
+       else {
+               killall();
+               msg("The ENTIRE dump is aborted.\n");
+       }
+       Exit(X_ABORT);
+}
+
+void
+Exit(status)
+       int status;
+{
+#ifdef TDEBUG
+       msg("pid = %d exits with status %d\n", getpid(), status);
+#endif TDEBUG
+       exit(status);
+}
+
+/*
+ * could use pipe() for this if flock() worked on pipes
+ */
+void
+lockfile(fd)
+       int fd[2];
+{
+       char tmpname[20];
+
+       strcpy(tmpname, _PATH_LOCK);
+       mktemp(tmpname);
+       if ((fd[1] = creat(tmpname, 0400)) < 0)
+               quit("cannot create lockfile %s: %s\n",
+                   tmpname, strerror(errno));
+       if ((fd[0] = open(tmpname, 0)) < 0)
+               quit("cannot reopen lockfile %s: %s\n",
+                   tmpname, strerror(errno));
+       (void) unlink(tmpname);
+}
+
+void
+enslave()
+{
+       int first[2], prev[2], next[2], cmd[2];     /* file descriptors */
+       register int i, j;
+
+       master = getpid();
+       signal(SIGTERM, dumpabort); /* Slave sends SIGTERM on dumpabort() */
+       signal(SIGPIPE, sigpipe);
+       signal(SIGUSR1, tperror);    /* Slave sends SIGUSR1 on tape errors */
+       lockfile(first);
+       for (i = 0; i < SLAVES; i++) {
+               if (i == 0) {
+                       prev[0] = first[1];
+                       prev[1] = first[0];
+               } else {
+                       prev[0] = next[0];
+                       prev[1] = next[1];
+                       flock(prev[1], LOCK_EX);
+               }
+               if (i < SLAVES - 1) {
+                       lockfile(next);
+               } else {
+                       next[0] = first[0];
+                       next[1] = first[1];         /* Last slave loops back */
+               }
+               if (pipe(cmd) < 0 || (slavepid[i] = fork()) < 0)
+                       quit("too many slaves, %d (recompile smaller): %s\n",
+                           i, strerror(errno));
+               slavefd[i] = cmd[1];
+               if (slavepid[i] == 0) {             /* Slave starts up here */
+                       for (j = 0; j <= i; j++)
+                               close(slavefd[j]);
+                       signal(SIGINT, SIG_IGN);    /* Master handles this */
+                       doslave(cmd[0], prev, next);
+                       Exit(X_FINOK);
+               }
+               close(cmd[0]);
+               if (i > 0) {
+                       close(prev[0]);
+                       close(prev[1]);
+               }
+       }
+       close(first[0]);
+       close(first[1]);
+       master = 0; rotor = 0;
+}
+
+void
+killall()
+{
+       register int i;
+
+       for (i = 0; i < SLAVES; i++)
+               if (slavepid[i] > 0)
+                       kill(slavepid[i], SIGKILL);
+}
+
+/*
+ * Synchronization - each process has a lockfile, and shares file
+ * descriptors to the following process's lockfile.  When our write
+ * completes, we release our lock on the following process's lock-
+ * file, allowing the following process to lock it and proceed. We
+ * get the lock back for the next cycle by swapping descriptors.
+ */
+void
+doslave(cmd, prev, next)
+       register int cmd, prev[2], next[2];
+{
+       register int nread, toggle = 0;
+       int nwrite;
+#ifndef __STDC__
+       int read();
+#endif
+
+       /*
+        * Need our own seek pointer.
+        */
+       close(diskfd);
+       if ((diskfd = open(disk, O_RDONLY)) < 0)
+               quit("slave couldn't reopen disk: %s\n", strerror(errno));
+       /*
+        * Get list of blocks to dump, read the blocks into tape buffer
+        */
+       while ((nread = atomic(read, cmd, req, reqsiz)) == reqsiz) {
+               register struct req *p = req;
+               for (trecno = 0; trecno < ntrec; trecno += p->count, p += p->count) {
+                       if (p->dblk) {
+                               bread(p->dblk, tblock[trecno],
+                                       p->count * TP_BSIZE);
+                       } else {
+                               if (p->count != 1 || atomic(read, cmd,
+                                   tblock[trecno], TP_BSIZE) != TP_BSIZE)
+                                       quit("master/slave protocol botched.\n");
+                       }
+               }
+               flock(prev[toggle], LOCK_EX);   /* Wait our turn */
+
+#ifdef RDUMP
+               if ((nwrite = (host ? rmtwrite(tblock[0], writesize)
+                       : write(tapefd, tblock[0], writesize))) != writesize) {
+#else RDUMP
+               if ((nwrite = write(tapefd, tblock[0], writesize))
+                   != writesize) {
+#endif RDUMP
+                       if (nwrite == -1) 
+                               perror("write");
+                       else
+                               msg("short write: got %d instead of %d\n",
+                                   nwrite, writesize);
+                       kill(master, SIGUSR1);
+                       for (;;)
+                               sigpause(0);
+               }
+               toggle ^= 1;
+               flock(next[toggle], LOCK_UN);   /* Next slave's turn */
+       }                                       /* Also jolts him awake */
+       if (nread != 0)
+               quit("error reading command pipe: %s\n", strerror(errno));
+}
+
+/*
+ * Since a read from a pipe may not return all we asked for,
+ * or a write may not write all we ask if we get a signal,
+ * loop until the count is satisfied (or error).
+ */
+int
+atomic(func, fd, buf, count)
+       int (*func)(), fd, count;
+       char *buf;
+{
+       int got, need = count;
+
+       while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0)
+               buf += got;
+       return (got < 0 ? got : count - need);
+}
diff --git a/usr/src/sbin/dump/dumptraverse.c b/usr/src/sbin/dump/dumptraverse.c
new file mode 100644 (file)
index 0000000..fa1c4f0
--- /dev/null
@@ -0,0 +1,517 @@
+/*-
+ * Copyright (c) 1980, 1988, 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)dumptraverse.c     5.11 (Berkeley) 3/7/91";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <ufs/dir.h>
+#include <ufs/dinode.h>
+#include <ufs/fs.h>
+#include <protocols/dumprestore.h>
+#ifdef __STDC__
+#include <unistd.h>
+#include <string.h>
+#endif
+#include "dump.h"
+
+void   dmpindir();
+#define        HASDUMPEDFILE   0x1
+#define        HASSUBDIRS      0x2
+
+/*
+ * This is an estimation of the number of TP_BSIZE blocks in the file.
+ * It estimates the number of blocks in files with holes by assuming
+ * that all of the blocks accounted for by di_blocks are data blocks
+ * (when some of the blocks are usually used for indirect pointers);
+ * hence the estimate may be high.
+ */
+long
+blockest(dp)
+       register struct dinode *dp;
+{
+       long blkest, sizeest;
+
+       /*
+        * dp->di_size is the size of the file in bytes.
+        * dp->di_blocks stores the number of sectors actually in the file.
+        * If there are more sectors than the size would indicate, this just
+        *      means that there are indirect blocks in the file or unused
+        *      sectors in the last file block; we can safely ignore these
+        *      (blkest = sizeest below).
+        * If the file is bigger than the number of sectors would indicate,
+        *      then the file has holes in it.  In this case we must use the
+        *      block count to estimate the number of data blocks used, but
+        *      we use the actual size for estimating the number of indirect
+        *      dump blocks (sizeest vs. blkest in the indirect block
+        *      calculation).
+        */
+       blkest = howmany(dbtob(dp->di_blocks), TP_BSIZE);
+       sizeest = howmany(dp->di_size, TP_BSIZE);
+       if (blkest > sizeest)
+               blkest = sizeest;
+       if (dp->di_size > sblock->fs_bsize * NDADDR) {
+               /* calculate the number of indirect blocks on the dump tape */
+               blkest +=
+                       howmany(sizeest - NDADDR * sblock->fs_bsize / TP_BSIZE,
+                       TP_NINDIR);
+       }
+       return (blkest + 1);
+}
+
+/*
+ * Dump pass 1.
+ *
+ * Walk the inode list for a filesystem to find all allocated inodes
+ * that have been modified since the previous dump time. Also, find all
+ * the directories in the filesystem.
+ */
+mapfiles(maxino, tapesize)
+       ino_t maxino;
+       long *tapesize;
+{
+       register int mode;
+       register ino_t ino;
+       register struct dinode *dp;
+       int anydirskipped = 0;
+
+       for (ino = 0; ino < maxino; ino++) {
+               dp = getino(ino);
+               if ((mode = (dp->di_mode & IFMT)) == 0)
+                       continue;
+               SETINO(ino, usedinomap);
+               if (mode == IFDIR)
+                       SETINO(ino, dumpdirmap);
+               if (dp->di_mtime >= spcl.c_ddate ||
+                   dp->di_ctime >= spcl.c_ddate) {
+                       SETINO(ino, dumpinomap);
+                       if (mode != IFREG && mode != IFDIR && mode != IFLNK) {
+                               *tapesize += 1;
+                               continue;
+                       }
+                       *tapesize += blockest(dp);
+                       continue;
+               }
+               if (mode == IFDIR)
+                       anydirskipped = 1;
+       }
+       /*
+        * Restore gets very upset if the root is not dumped,
+        * so ensure that it always is dumped.
+        */
+       SETINO(ROOTINO, usedinomap);
+       return (anydirskipped);
+}
+
+/*
+ * Dump pass 2.
+ *
+ * Scan each directory on the filesystem to see if it has any modified
+ * files in it. If it does, and has not already been added to the dump
+ * list (because it was itself modified), then add it. If a directory
+ * has not been modified itself, contains no modified files and has no
+ * subdirectories, then it can be deleted from the dump list and from
+ * the list of directories. By deleting it from the list of directories,
+ * its parent may now qualify for the same treatment on this or a later
+ * pass using this algorithm.
+ */
+mapdirs(maxino, tapesize)
+       ino_t maxino;
+       long *tapesize;
+{
+       register struct dinode *dp;
+       register int i, dirty;
+       register char *map;
+       register ino_t ino;
+       long filesize, blkcnt = 0;
+       int ret, change = 0;
+
+       for (map = dumpdirmap, ino = 0; ino < maxino; ) {
+               if ((ino % NBBY) == 0)
+                       dirty = *map++;
+               else
+                       dirty >>= 1;
+               ino++;
+               if ((dirty & 1) == 0 || TSTINO(ino, dumpinomap))
+                       continue;
+               dp = getino(ino);
+               filesize = dp->di_size;
+               for (ret = 0, i = 0; filesize > 0 && i < NDADDR; i++) {
+                       if (dp->di_db[i] != 0)
+                               ret |= searchdir(ino, dp->di_db[i],
+                                       dblksize(sblock, dp, i), filesize);
+                       if (ret & HASDUMPEDFILE)
+                               filesize = 0;
+                       else
+                               filesize -= sblock->fs_bsize;
+               }
+               for (i = 0; filesize > 0 && i < NIADDR; i++) {
+                       if (dp->di_ib[i] == 0)
+                               continue;
+                       ret |= dirindir(ino, dp->di_ib[i], i, &filesize);
+               }
+               if (ret & HASDUMPEDFILE) {
+                       if (!TSTINO(ino, dumpinomap)) {
+                               SETINO(ino, dumpinomap);
+                               *tapesize += blockest(dp);
+                       }
+                       change = 1;
+                       continue;
+               }
+               if ((ret & HASSUBDIRS) == 0) {
+                       if (!TSTINO(ino, dumpinomap)) {
+                               CLRINO(ino, dumpdirmap);
+                               change = 1;
+                       }
+               }
+       }
+       return (change);
+}
+
+/*
+ * Read indirect blocks, and pass the data blocks to be searched
+ * as directories. Quit as soon as any entry is found that will
+ * require the directory to be dumped.
+ */
+dirindir(ino, blkno, level, filesize)
+       ino_t ino;
+       daddr_t blkno;
+       int level, *filesize;
+{
+       int ret = 0;
+       register int i;
+       daddr_t idblk[MAXNINDIR];
+
+       bread(fsbtodb(sblock, blkno), (char *)idblk, sblock->fs_bsize);
+       if (level <= 0) {
+               for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) {
+                       blkno = idblk[i];
+                       if (blkno != 0)
+                               ret |= searchdir(ino, blkno, sblock->fs_bsize,
+                                       filesize);
+                       if (ret & HASDUMPEDFILE)
+                               *filesize = 0;
+                       else
+                               *filesize -= sblock->fs_bsize;
+               }
+               return (ret);
+       }
+       level--;
+       for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) {
+               blkno = idblk[i];
+               if (blkno != 0)
+                       ret |= dirindir(ino, blkno, level, filesize);
+       }
+       return (ret);
+}
+
+/*
+ * Scan a disk block containing directory information looking to see if
+ * any of the entries are on the dump list and to see if the directory
+ * contains any subdirectories.
+ */
+searchdir(ino, blkno, size, filesize)
+       ino_t ino;
+       daddr_t blkno;
+       register int size;
+       int filesize;
+{
+       register struct direct *dp;
+       register long loc;
+       char dblk[MAXBSIZE];
+
+       bread(fsbtodb(sblock, blkno), dblk, size);
+       if (filesize < size)
+               size = filesize;
+       for (loc = 0; loc < size; ) {
+               dp = (struct direct *)(dblk + loc);
+               if (dp->d_reclen == 0) {
+                       msg("corrupted directory, inumber %d\n", ino);
+                       break;
+               }
+               loc += dp->d_reclen;
+               if (dp->d_ino == 0)
+                       continue;
+               if (dp->d_name[0] == '.') {
+                       if (dp->d_name[1] == '\0')
+                               continue;
+                       if (dp->d_name[1] == '.' && dp->d_name[2] == '\0')
+                               continue;
+               }
+               if (TSTINO(dp->d_ino, dumpinomap))
+                       return (HASDUMPEDFILE);
+               if (TSTINO(dp->d_ino, dumpdirmap))
+                       return (HASSUBDIRS);
+       }
+       return (0);
+}
+
+/*
+ * Dump passes 3 and 4.
+ *
+ * Dump the contents of an inode to tape.
+ */
+void
+dumpino(dp, ino)
+       register struct dinode *dp;
+       ino_t ino;
+{
+       int mode, level, cnt;
+       long size;
+
+       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;
+       /*
+        * Check for freed inode.
+        */
+       if ((mode = (dp->di_mode & IFMT)) == 0)
+               return;
+       if ((mode != IFDIR && mode != IFREG && mode != IFLNK) ||
+           dp->di_size == 0) {
+               writeheader(ino);
+               return;
+       }
+       if (dp->di_size > NDADDR * 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 (level = 0; level < NIADDR; level++) {
+               dmpindir(ino, dp->di_ib[level], level, &size);
+               if (size <= 0)
+                       return;
+       }
+}
+
+/*
+ * Read indirect blocks, and pass the data blocks to be dumped.
+ */
+void
+dmpindir(ino, blk, level, size)
+       ino_t ino;
+       daddr_t blk;
+       int level;
+       long *size;
+{
+       int i, cnt;
+       daddr_t idblk[MAXNINDIR];
+
+       if (blk != 0)
+               bread(fsbtodb(sblock, blk), (char *)idblk, sblock->fs_bsize);
+       else
+               bzero((char *)idblk, sblock->fs_bsize);
+       if (level <= 0) {
+               if (*size < NINDIR(sblock) * sblock->fs_bsize)
+                       cnt = howmany(*size, sblock->fs_fsize);
+               else
+                       cnt = NINDIR(sblock) * sblock->fs_frag;
+               *size -= NINDIR(sblock) * sblock->fs_bsize;
+               blksout(&idblk[0], cnt, ino);
+               return;
+       }
+       level--;
+       for (i = 0; i < NINDIR(sblock); i++) {
+               dmpindir(ino, idblk[i], level, size);
+               if (*size <= 0)
+                       return;
+       }
+}
+
+/*
+ * Collect up the data into tape record sized buffers and output them.
+ */
+void
+blksout(blkp, frags, ino)
+       daddr_t *blkp;
+       int frags;
+       ino_t ino;
+{
+       register daddr_t *bp;
+       int i, j, count, blks, tbperdb;
+
+       blks = howmany(frags * sblock->fs_fsize, TP_BSIZE);
+       tbperdb = sblock->fs_bsize >> tp_bshift;
+       for (i = 0; i < blks; i += TP_NINDIR) {
+               if (i + TP_NINDIR > blks)
+                       count = blks;
+               else
+                       count = i + TP_NINDIR;
+               for (j = i; j < count; j++)
+                       if (blkp[j / tbperdb] != 0)
+                               spcl.c_addr[j - i] = 1;
+                       else
+                               spcl.c_addr[j - i] = 0;
+               spcl.c_count = count - i;
+               writeheader(ino);
+               bp = &blkp[i / tbperdb];
+               for (j = i; j < count; j += tbperdb, bp++)
+                       if (*bp != 0)
+                               if (j + tbperdb <= count)
+                                       dumpblock(*bp, sblock->fs_bsize);
+                               else
+                                       dumpblock(*bp, (count - j) * TP_BSIZE);
+               spcl.c_type = TS_ADDR;
+       }
+}
+
+/*
+ * Dump a map to the tape.
+ */
+void
+dumpmap(map, type, ino)
+       char *map;
+       int type;
+       ino_t ino;
+{
+       register int i;
+       char *cp;
+
+       spcl.c_type = type;
+       spcl.c_count = howmany(mapsize * sizeof(char), TP_BSIZE);
+       writeheader(ino);
+       for (i = 0, cp = map; i < spcl.c_count; i++, cp += TP_BSIZE)
+               writerec(cp);
+}
+
+/*
+ * Write a header record to the dump tape.
+ */
+void
+writeheader(ino)
+       ino_t ino;
+{
+       register long sum, cnt, *lp;
+
+       spcl.c_inumber = ino;
+       spcl.c_magic = NFS_MAGIC;
+       spcl.c_checksum = 0;
+       lp = (long *)&spcl;
+       sum = 0;
+       cnt = sizeof(union u_spcl) / (4 * sizeof(long));
+       while (--cnt >= 0) {
+               sum += *lp++;
+               sum += *lp++;
+               sum += *lp++;
+               sum += *lp++;
+       }
+       spcl.c_checksum = CHECKSUM - sum;
+       writerec((char *)&spcl);
+}
+
+struct dinode *
+getino(inum)
+       ino_t inum;
+{
+       static daddr_t minino, maxino;
+       static struct dinode inoblock[MAXINOPB];
+
+       curino = inum;
+       if (inum >= minino && inum < maxino)
+               return (&inoblock[inum - minino]);
+       bread(fsbtodb(sblock, itod(sblock, inum)), inoblock, sblock->fs_bsize);
+       minino = inum - (inum % INOPB(sblock));
+       maxino = minino + INOPB(sblock);
+       return (&inoblock[inum - minino]);
+}
+
+/*
+ * Read a chunk of data from the disk.
+ * Try to recover from hard errors by reading in sector sized pieces.
+ * Error recovery is attempted at most BREADEMAX times before seeking
+ * consent from the operator to continue.
+ */
+int    breaderrors = 0;                
+#define        BREADEMAX 32
+
+void
+bread(blkno, buf, size)
+       daddr_t blkno;
+       char *buf;
+       int size;       
+{
+       int cnt, i;
+       extern int errno;
+
+loop:
+       if (lseek(diskfd, (long)(blkno << dev_bshift), 0) < 0)
+               msg("bread: lseek fails\n");
+       if ((cnt = read(diskfd, buf, size)) == size)
+               return;
+       if (blkno + (size / dev_bsize) > fsbtodb(sblock, sblock->fs_size)) {
+               /*
+                * Trying to read the final fragment.
+                *
+                * NB - dump only works in TP_BSIZE blocks, hence
+                * rounds `dev_bsize' fragments up to TP_BSIZE pieces.
+                * It should be smarter about not actually trying to
+                * read more than it can get, but for the time being
+                * we punt and scale back the read only when it gets
+                * us into trouble. (mkm 9/25/83)
+                */
+               size -= dev_bsize;
+               goto loop;
+       }
+       msg("read error from %s [block %d]: count=%d, got=%d, errno=%d (%s)\n",
+               disk, blkno, size, cnt, errno, strerror(errno));
+       if (++breaderrors > BREADEMAX) {
+               msg("More than %d block read errors from %d\n",
+                       BREADEMAX, disk);
+               broadcast("DUMP IS AILING!\n");
+               msg("This is an unrecoverable error.\n");
+               if (!query("Do you want to attempt to continue?")){
+                       dumpabort();
+                       /*NOTREACHED*/
+               } else
+                       breaderrors = 0;
+       }
+       /*
+        * Zero buffer, then try to read each sector of buffer separately.
+        */
+       bzero(buf, size);
+       for (i = 0; i < size; i += dev_bsize, buf += dev_bsize, blkno++) {
+               if (lseek(diskfd, (long)(blkno << dev_bshift), 0) < 0)
+                       msg("bread: lseek2 fails!\n");
+               if ((cnt = read(diskfd, buf, dev_bsize)) != dev_bsize)
+                       msg("    read error from %s [sector %d, errno %d]\n",
+                           disk, blkno, errno);
+       }
+}
diff --git a/usr/src/sbin/dump/pathnames.h b/usr/src/sbin/dump/pathnames.h
new file mode 100644 (file)
index 0000000..5ebfd89
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)pathnames.h 5.6 (Berkeley) 2/28/91
+ */
+
+#include <paths.h>
+
+#define        _PATH_DEFTAPE   "/dev/rmt8"
+#define        _PATH_DTMP      "/etc/dtmp"
+#define        _PATH_DUMPDATES "/etc/dumpdates"
+#define        _PATH_LOCK      "/tmp/dumplockXXXXXX"
+#define        _PATH_RMT       "rmt"
diff --git a/usr/src/sbin/dump/rdump.8 b/usr/src/sbin/dump/rdump.8
new file mode 100644 (file)
index 0000000..df3471b
--- /dev/null
@@ -0,0 +1,77 @@
+.\" Copyright (c) 1983, 1991 Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"     @(#)rdump.8    6.3 (Berkeley) 3/16/91
+.\"
+.Dd March 16, 1991
+.Dt RDUMP 8
+.Os BSD 4.2
+.Sh NAME
+.Nm rdump
+.Nd file system dump across the network
+.Sh SYNOPSIS
+.Nm rdump
+.Oo
+.Ar flags
+.Op Ar argument ...
+.Ar filesystem
+.Oc
+.Sh DESCRIPTION
+.Nm Rdump
+remotely backs up files across a network in the same manner as
+.Xr dump 8
+locally backs up files.
+The command is identical in operation to
+.Xr dump 8
+except the 
+.Cm f
+key must be specified and the file
+.Ar argument
+supplied should be of the form
+.Pa machine:device .
+.Pp
+.Nm Rdump
+creates a remote server,
+.Xr rmt ,
+on the client machine to access the tape
+device.
+.Sh SEE ALSO
+.Xr dump 8 ,
+.Xr rmt 8
+.Sh DIAGNOSTICS
+Same as 
+.Xr dump 8
+with a few extra related to the network.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
diff --git a/usr/src/sbin/dump/unctime.c b/usr/src/sbin/dump/unctime.c
new file mode 100644 (file)
index 0000000..2983661
--- /dev/null
@@ -0,0 +1,154 @@
+/*-
+ * Copyright (c) 1980 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)unctime.c  5.4 (Berkeley) 3/7/91";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <time.h>
+#include <stdio.h>
+#ifdef __STDC__
+#include <stdlib.h>
+#include <string.h>
+#endif
+
+/*
+ * Convert a ctime(3) format string into a system format date.
+ * Return the date thus calculated.
+ *
+ * Return -1 if the string is not in ctime format.
+ */
+
+/*
+ * Offsets into the ctime string to various parts.
+ */
+
+#define        E_MONTH         4
+#define        E_DAY           8
+#define        E_HOUR          11
+#define        E_MINUTE        14
+#define        E_SECOND        17
+#define        E_YEAR          20
+
+static int lookup();
+
+time_t
+unctime(str)
+       char *str;
+{
+       struct tm then;
+       char dbuf[30];
+       time_t emitl();
+
+       if (strlen(str) != 25)
+               str[25] = 0;
+       strcpy(dbuf, str);
+       dbuf[E_MONTH+3] = 0;
+       if ((then.tm_mon = lookup(&dbuf[E_MONTH])) < 0)
+               return (-1);
+       then.tm_mday = atoi(&dbuf[E_DAY]);
+       then.tm_hour = atoi(&dbuf[E_HOUR]);
+       then.tm_min = atoi(&dbuf[E_MINUTE]);
+       then.tm_sec = atoi(&dbuf[E_SECOND]);
+       then.tm_year = atoi(&dbuf[E_YEAR]) - 1900;
+       return(emitl(&then));
+}
+
+static char months[] =
+       "JanFebMarAprMayJunJulAugSepOctNovDec";
+
+static int
+lookup(str)
+       char *str;
+{
+       register char *cp, *cp2;
+
+       for (cp = months, cp2 = str; *cp != 0; cp += 3)
+               if (strncmp(cp, cp2, 3) == 0)
+                       return((cp-months) / 3);
+       return(-1);
+}
+/*
+ * Routine to convert a localtime(3) format date back into
+ * a system format date.
+ *
+ *     Use a binary search.
+ */
+
+struct tm *localtime();
+static int dcmp();
+
+time_t
+emitl(dp)
+       struct tm *dp;
+{
+       time_t conv;
+       register int i, bit;
+       struct tm dcopy;
+
+       dcopy = *dp;
+       dp = &dcopy;
+       conv = 0;
+       for (i = 30; i >= 0; i--) {
+               bit = 1 << i;
+               conv |= bit;
+               if (dcmp(localtime(&conv), dp) > 0)
+                       conv &= ~bit;
+       }
+       return(conv);
+}
+
+/*
+ * Compare two localtime dates, return result.
+ */
+
+#define DECIDE(a) \
+       if (dp->a > dp2->a) \
+               return(1); \
+       if (dp->a < dp2->a) \
+               return(-1)
+
+static int
+dcmp(dp, dp2)
+       register struct tm *dp, *dp2;
+{
+
+       DECIDE(tm_year);
+       DECIDE(tm_mon);
+       DECIDE(tm_mday);
+       DECIDE(tm_hour);
+       DECIDE(tm_min);
+       DECIDE(tm_sec);
+       return(0);
+}