386BSD 0.1 development
authorWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Wed, 17 Apr 1991 00:30:40 +0000 (16:30 -0800)
committerWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Wed, 17 Apr 1991 00:30:40 +0000 (16:30 -0800)
Work on file usr/src/sbin/restore/interactive.c
Work on file usr/src/sbin/restore/main.c
Work on file usr/src/sbin/restore/pathnames.h
Work on file usr/src/sbin/restore/symtab.c
Work on file usr/src/sbin/restore/rrestore.8
Work on file usr/src/sbin/restore/restore.c
Work on file usr/src/sbin/restore/restore.h
Work on file usr/src/sbin/restore/utilities.c
Work on file usr/src/sbin/restore/tape.c

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

usr/src/sbin/restore/interactive.c [new file with mode: 0644]
usr/src/sbin/restore/main.c [new file with mode: 0644]
usr/src/sbin/restore/pathnames.h [new file with mode: 0644]
usr/src/sbin/restore/restore.c [new file with mode: 0644]
usr/src/sbin/restore/restore.h [new file with mode: 0644]
usr/src/sbin/restore/rrestore.8 [new file with mode: 0644]
usr/src/sbin/restore/symtab.c [new file with mode: 0644]
usr/src/sbin/restore/tape.c [new file with mode: 0644]
usr/src/sbin/restore/utilities.c [new file with mode: 0644]

diff --git a/usr/src/sbin/restore/interactive.c b/usr/src/sbin/restore/interactive.c
new file mode 100644 (file)
index 0000000..c3cb8b7
--- /dev/null
@@ -0,0 +1,860 @@
+/*
+ * Copyright (c) 1985 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[] = "@(#)interactive.c      5.9 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+#include "restore.h"
+#include <protocols/dumprestore.h>
+#include <setjmp.h>
+#include <ufs/dir.h>
+
+#define round(a, b) (((a) + (b) - 1) / (b) * (b))
+
+/*
+ * Things to handle interruptions.
+ */
+static jmp_buf reset;
+static char *nextarg = NULL;
+
+/*
+ * Structure and routines associated with listing directories.
+ */
+struct afile {
+       ino_t   fnum;           /* inode number of file */
+       char    *fname;         /* file name */
+       short   fflags;         /* extraction flags, if any */
+       char    ftype;          /* file type, e.g. LEAF or NODE */
+};
+struct arglist {
+       struct afile    *head;  /* start of argument list */
+       struct afile    *last;  /* end of argument list */
+       struct afile    *base;  /* current list arena */
+       int             nent;   /* maximum size of list */
+       char            *cmd;   /* the current command */
+};
+extern int fcmp();
+extern char *fmtentry();
+char *copynext();
+
+/*
+ * Read and execute commands from the terminal.
+ */
+runcmdshell()
+{
+       register struct entry *np;
+       ino_t ino;
+       static struct arglist alist = { 0, 0, 0, 0, 0 };
+       char curdir[MAXPATHLEN];
+       char name[MAXPATHLEN];
+       char cmd[BUFSIZ];
+
+       canon("/", curdir);
+loop:
+       if (setjmp(reset) != 0) {
+               for (; alist.head < alist.last; alist.head++)
+                       freename(alist.head->fname);
+               nextarg = NULL;
+               volno = 0;
+       }
+       getcmd(curdir, cmd, name, &alist);
+       switch (cmd[0]) {
+       /*
+        * Add elements to the extraction list.
+        */
+       case 'a':
+               if (strncmp(cmd, "add", strlen(cmd)) != 0)
+                       goto bad;
+               ino = dirlookup(name);
+               if (ino == 0)
+                       break;
+               if (mflag)
+                       pathcheck(name);
+               treescan(name, ino, addfile);
+               break;
+       /*
+        * Change working directory.
+        */
+       case 'c':
+               if (strncmp(cmd, "cd", strlen(cmd)) != 0)
+                       goto bad;
+               ino = dirlookup(name);
+               if (ino == 0)
+                       break;
+               if (inodetype(ino) == LEAF) {
+                       fprintf(stderr, "%s: not a directory\n", name);
+                       break;
+               }
+               (void) strcpy(curdir, name);
+               break;
+       /*
+        * Delete elements from the extraction list.
+        */
+       case 'd':
+               if (strncmp(cmd, "delete", strlen(cmd)) != 0)
+                       goto bad;
+               np = lookupname(name);
+               if (np == NIL || (np->e_flags & NEW) == 0) {
+                       fprintf(stderr, "%s: not on extraction list\n", name);
+                       break;
+               }
+               treescan(name, np->e_ino, deletefile);
+               break;
+       /*
+        * Extract the requested list.
+        */
+       case 'e':
+               if (strncmp(cmd, "extract", strlen(cmd)) != 0)
+                       goto bad;
+               createfiles();
+               createlinks();
+               setdirmodes();
+               if (dflag)
+                       checkrestore();
+               volno = 0;
+               break;
+       /*
+        * List available commands.
+        */
+       case 'h':
+               if (strncmp(cmd, "help", strlen(cmd)) != 0)
+                       goto bad;
+       case '?':
+               fprintf(stderr, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+                       "Available commands are:\n",
+                       "\tls [arg] - list directory\n",
+                       "\tcd arg - change directory\n",
+                       "\tpwd - print current directory\n",
+                       "\tadd [arg] - add `arg' to list of",
+                       " files to be extracted\n",
+                       "\tdelete [arg] - delete `arg' from",
+                       " list of files to be extracted\n",
+                       "\textract - extract requested files\n",
+                       "\tsetmodes - set modes of requested directories\n",
+                       "\tquit - immediately exit program\n",
+                       "\twhat - list dump header information\n",
+                       "\tverbose - toggle verbose flag",
+                       " (useful with ``ls'')\n",
+                       "\thelp or `?' - print this list\n",
+                       "If no `arg' is supplied, the current",
+                       " directory is used\n");
+               break;
+       /*
+        * List a directory.
+        */
+       case 'l':
+               if (strncmp(cmd, "ls", strlen(cmd)) != 0)
+                       goto bad;
+               ino = dirlookup(name);
+               if (ino == 0)
+                       break;
+               printlist(name, ino, curdir);
+               break;
+       /*
+        * Print current directory.
+        */
+       case 'p':
+               if (strncmp(cmd, "pwd", strlen(cmd)) != 0)
+                       goto bad;
+               if (curdir[1] == '\0')
+                       fprintf(stderr, "/\n");
+               else
+                       fprintf(stderr, "%s\n", &curdir[1]);
+               break;
+       /*
+        * Quit.
+        */
+       case 'q':
+               if (strncmp(cmd, "quit", strlen(cmd)) != 0)
+                       goto bad;
+               return;
+       case 'x':
+               if (strncmp(cmd, "xit", strlen(cmd)) != 0)
+                       goto bad;
+               return;
+       /*
+        * Toggle verbose mode.
+        */
+       case 'v':
+               if (strncmp(cmd, "verbose", strlen(cmd)) != 0)
+                       goto bad;
+               if (vflag) {
+                       fprintf(stderr, "verbose mode off\n");
+                       vflag = 0;
+                       break;
+               }
+               fprintf(stderr, "verbose mode on\n");
+               vflag++;
+               break;
+       /*
+        * Just restore requested directory modes.
+        */
+       case 's':
+               if (strncmp(cmd, "setmodes", strlen(cmd)) != 0)
+                       goto bad;
+               setdirmodes();
+               break;
+       /*
+        * Print out dump header information.
+        */
+       case 'w':
+               if (strncmp(cmd, "what", strlen(cmd)) != 0)
+                       goto bad;
+               printdumpinfo();
+               break;
+       /*
+        * Turn on debugging.
+        */
+       case 'D':
+               if (strncmp(cmd, "Debug", strlen(cmd)) != 0)
+                       goto bad;
+               if (dflag) {
+                       fprintf(stderr, "debugging mode off\n");
+                       dflag = 0;
+                       break;
+               }
+               fprintf(stderr, "debugging mode on\n");
+               dflag++;
+               break;
+       /*
+        * Unknown command.
+        */
+       default:
+       bad:
+               fprintf(stderr, "%s: unknown command; type ? for help\n", cmd);
+               break;
+       }
+       goto loop;
+}
+
+/*
+ * Read and parse an interactive command.
+ * The first word on the line is assigned to "cmd". If
+ * there are no arguments on the command line, then "curdir"
+ * is returned as the argument. If there are arguments
+ * on the line they are returned one at a time on each
+ * successive call to getcmd. Each argument is first assigned
+ * to "name". If it does not start with "/" the pathname in
+ * "curdir" is prepended to it. Finally "canon" is called to
+ * eliminate any embedded ".." components.
+ */
+getcmd(curdir, cmd, name, ap)
+       char *curdir, *cmd, *name;
+       struct arglist *ap;
+{
+       register char *cp;
+       static char input[BUFSIZ];
+       char output[BUFSIZ];
+#      define rawname input    /* save space by reusing input buffer */
+
+       /*
+        * Check to see if still processing arguments.
+        */
+       if (ap->head != ap->last) {
+               strcpy(name, ap->head->fname);
+               freename(ap->head->fname);
+               ap->head++;
+               return;
+       }
+       if (nextarg != NULL)
+               goto getnext;
+       /*
+        * Read a command line and trim off trailing white space.
+        */
+       do      {
+               fprintf(stderr, "restore > ");
+               (void) fflush(stderr);
+               (void) fgets(input, BUFSIZ, terminal);
+       } while (!feof(terminal) && input[0] == '\n');
+       if (feof(terminal)) {
+               (void) strcpy(cmd, "quit");
+               return;
+       }
+       for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--)
+               /* trim off trailing white space and newline */;
+       *++cp = '\0';
+       /*
+        * Copy the command into "cmd".
+        */
+       cp = copynext(input, cmd);
+       ap->cmd = cmd;
+       /*
+        * If no argument, use curdir as the default.
+        */
+       if (*cp == '\0') {
+               (void) strcpy(name, curdir);
+               return;
+       }
+       nextarg = cp;
+       /*
+        * Find the next argument.
+        */
+getnext:
+       cp = copynext(nextarg, rawname);
+       if (*cp == '\0')
+               nextarg = NULL;
+       else
+               nextarg = cp;
+       /*
+        * If it an absolute pathname, canonicalize it and return it.
+        */
+       if (rawname[0] == '/') {
+               canon(rawname, name);
+       } else {
+               /*
+                * For relative pathnames, prepend the current directory to
+                * it then canonicalize and return it.
+                */
+               (void) strcpy(output, curdir);
+               (void) strcat(output, "/");
+               (void) strcat(output, rawname);
+               canon(output, name);
+       }
+       expandarg(name, ap);
+       strcpy(name, ap->head->fname);
+       freename(ap->head->fname);
+       ap->head++;
+#      undef rawname
+}
+
+/*
+ * Strip off the next token of the input.
+ */
+char *
+copynext(input, output)
+       char *input, *output;
+{
+       register char *cp, *bp;
+       char quote;
+
+       for (cp = input; *cp == ' ' || *cp == '\t'; cp++)
+               /* skip to argument */;
+       bp = output;
+       while (*cp != ' ' && *cp != '\t' && *cp != '\0') {
+               /*
+                * Handle back slashes.
+                */
+               if (*cp == '\\') {
+                       if (*++cp == '\0') {
+                               fprintf(stderr,
+                                       "command lines cannot be continued\n");
+                               continue;
+                       }
+                       *bp++ = *cp++;
+                       continue;
+               }
+               /*
+                * The usual unquoted case.
+                */
+               if (*cp != '\'' && *cp != '"') {
+                       *bp++ = *cp++;
+                       continue;
+               }
+               /*
+                * Handle single and double quotes.
+                */
+               quote = *cp++;
+               while (*cp != quote && *cp != '\0')
+                       *bp++ = *cp++ | 0200;
+               if (*cp++ == '\0') {
+                       fprintf(stderr, "missing %c\n", quote);
+                       cp--;
+                       continue;
+               }
+       }
+       *bp = '\0';
+       return (cp);
+}
+
+/*
+ * Canonicalize file names to always start with ``./'' and
+ * remove any imbedded "." and ".." components.
+ */
+canon(rawname, canonname)
+       char *rawname, *canonname;
+{
+       register char *cp, *np;
+       int len;
+
+       if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0)
+               (void) strcpy(canonname, "");
+       else if (rawname[0] == '/')
+               (void) strcpy(canonname, ".");
+       else
+               (void) strcpy(canonname, "./");
+       (void) strcat(canonname, rawname);
+       /*
+        * Eliminate multiple and trailing '/'s
+        */
+       for (cp = np = canonname; *np != '\0'; cp++) {
+               *cp = *np++;
+               while (*cp == '/' && *np == '/')
+                       np++;
+       }
+       *cp = '\0';
+       if (*--cp == '/')
+               *cp = '\0';
+       /*
+        * Eliminate extraneous "." and ".." from pathnames.
+        */
+       for (np = canonname; *np != '\0'; ) {
+               np++;
+               cp = np;
+               while (*np != '/' && *np != '\0')
+                       np++;
+               if (np - cp == 1 && *cp == '.') {
+                       cp--;
+                       (void) strcpy(cp, np);
+                       np = cp;
+               }
+               if (np - cp == 2 && strncmp(cp, "..", 2) == 0) {
+                       cp--;
+                       while (cp > &canonname[1] && *--cp != '/')
+                               /* find beginning of name */;
+                       (void) strcpy(cp, np);
+                       np = cp;
+               }
+       }
+}
+
+/*
+ * globals (file name generation)
+ *
+ * "*" in params matches r.e ".*"
+ * "?" in params matches r.e. "."
+ * "[...]" in params matches character class
+ * "[...a-z...]" in params matches a through z.
+ */
+expandarg(arg, ap)
+       char *arg;
+       register struct arglist *ap;
+{
+       static struct afile single;
+       struct entry *ep;
+       int size;
+
+       ap->head = ap->last = (struct afile *)0;
+       size = expand(arg, 0, ap);
+       if (size == 0) {
+               ep = lookupname(arg);
+               single.fnum = ep ? ep->e_ino : 0;
+               single.fname = savename(arg);
+               ap->head = &single;
+               ap->last = ap->head + 1;
+               return;
+       }
+       qsort((char *)ap->head, ap->last - ap->head, sizeof *ap->head, fcmp);
+}
+
+/*
+ * Expand a file name
+ */
+expand(as, rflg, ap)
+       char *as;
+       int rflg;
+       register struct arglist *ap;
+{
+       int             count, size;
+       char            dir = 0;
+       char            *rescan = 0;
+       DIR             *dirp;
+       register char   *s, *cs;
+       int             sindex, rindex, lindex;
+       struct direct   *dp;
+       register char   slash; 
+       register char   *rs; 
+       register char   c;
+
+       /*
+        * check for meta chars
+        */
+       s = cs = as;
+       slash = 0;
+       while (*cs != '*' && *cs != '?' && *cs != '[') {        
+               if (*cs++ == 0) {       
+                       if (rflg && slash)
+                               break; 
+                       else
+                               return (0) ;
+               } else if (*cs == '/') {        
+                       slash++;
+               }
+       }
+       for (;;) {      
+               if (cs == s) {  
+                       s = "";
+                       break;
+               } else if (*--cs == '/') {      
+                       *cs = 0;
+                       if (s == cs)
+                               s = "/";
+                       break;
+               }
+       }
+       if ((dirp = rst_opendir(s)) != NULL)
+               dir++;
+       count = 0;
+       if (*cs == 0)
+               *cs++ = 0200;
+       if (dir) {
+               /*
+                * check for rescan
+                */
+               rs = cs;
+               do {    
+                       if (*rs == '/') { 
+                               rescan = rs; 
+                               *rs = 0; 
+                       }
+               } while (*rs++);
+               sindex = ap->last - ap->head;
+               while ((dp = rst_readdir(dirp)) != NULL && dp->d_ino != 0) {
+                       if (!dflag && BIT(dp->d_ino, dumpmap) == 0)
+                               continue;
+                       if ((*dp->d_name == '.' && *cs != '.'))
+                               continue;
+                       if (gmatch(dp->d_name, cs)) {   
+                               if (addg(dp, s, rescan, ap) < 0)
+                                       return (-1);
+                               count++;
+                       }
+               }
+               if (rescan) {   
+                       rindex = sindex; 
+                       lindex = ap->last - ap->head;
+                       if (count) {    
+                               count = 0;
+                               while (rindex < lindex) {       
+                                       size = expand(ap->head[rindex].fname,
+                                           1, ap);
+                                       if (size < 0)
+                                               return (size);
+                                       count += size;
+                                       rindex++;
+                               }
+                       }
+                       bcopy((char *)&ap->head[lindex],
+                            (char *)&ap->head[sindex],
+                            (ap->last - &ap->head[rindex]) * sizeof *ap->head);
+                       ap->last -= lindex - sindex;
+                       *rescan = '/';
+               }
+       }
+       s = as;
+       while (c = *s)
+               *s++ = (c&0177 ? c : '/');
+       return (count);
+}
+
+/*
+ * Check for a name match
+ */
+gmatch(s, p)
+       register char   *s, *p;
+{
+       register int    scc;
+       char            c;
+       char            ok; 
+       int             lc;
+
+       if (scc = *s++)
+               if ((scc &= 0177) == 0)
+                       scc = 0200;
+       switch (c = *p++) {
+
+       case '[':
+               ok = 0; 
+               lc = 077777;
+               while (c = *p++) {      
+                       if (c == ']') {
+                               return (ok ? gmatch(s, p) : 0);
+                       } else if (c == '-') {  
+                               if (lc <= scc && scc <= (*p++))
+                                       ok++ ;
+                       } else {        
+                               if (scc == (lc = (c&0177)))
+                                       ok++ ;
+                       }
+               }
+               return (0);
+
+       default:
+               if ((c&0177) != scc)
+                       return (0) ;
+               /* falls through */
+
+       case '?':
+               return (scc ? gmatch(s, p) : 0);
+
+       case '*':
+               if (*p == 0)
+                       return (1) ;
+               s--;
+               while (*s) {  
+                       if (gmatch(s++, p))
+                               return (1);
+               }
+               return (0);
+
+       case 0:
+               return (scc == 0);
+       }
+}
+
+/*
+ * Construct a matched name.
+ */
+addg(dp, as1, as3, ap)
+       struct direct   *dp;
+       char            *as1, *as3;
+       struct arglist  *ap;
+{
+       register char   *s1, *s2;
+       register int    c;
+       char            buf[BUFSIZ];
+
+       s2 = buf;
+       s1 = as1;
+       while (c = *s1++) {     
+               if ((c &= 0177) == 0) { 
+                       *s2++ = '/';
+                       break;
+               }
+               *s2++ = c;
+       }
+       s1 = dp->d_name;
+       while (*s2 = *s1++)
+               s2++;
+       if (s1 = as3) { 
+               *s2++ = '/';
+               while (*s2++ = *++s1)
+                       /* void */;
+       }
+       if (mkentry(buf, dp->d_ino, ap) == FAIL)
+               return (-1);
+}
+
+/*
+ * Do an "ls" style listing of a directory
+ */
+printlist(name, ino, basename)
+       char *name;
+       ino_t ino;
+       char *basename;
+{
+       register struct afile *fp;
+       register struct direct *dp;
+       static struct arglist alist = { 0, 0, 0, 0, "ls" };
+       struct afile single;
+       DIR *dirp;
+
+       if ((dirp = rst_opendir(name)) == NULL) {
+               single.fnum = ino;
+               single.fname = savename(name + strlen(basename) + 1);
+               alist.head = &single;
+               alist.last = alist.head + 1;
+       } else {
+               alist.head = (struct afile *)0;
+               fprintf(stderr, "%s:\n", name);
+               while (dp = rst_readdir(dirp)) {
+                       if (dp == NULL || dp->d_ino == 0)
+                               break;
+                       if (!dflag && BIT(dp->d_ino, dumpmap) == 0)
+                               continue;
+                       if (vflag == 0 &&
+                           (strcmp(dp->d_name, ".") == 0 ||
+                            strcmp(dp->d_name, "..") == 0))
+                               continue;
+                       if (!mkentry(dp->d_name, dp->d_ino, &alist))
+                               return;
+               }
+       }
+       if (alist.head != 0) {
+               qsort((char *)alist.head, alist.last - alist.head,
+                       sizeof *alist.head, fcmp);
+               formatf(&alist);
+               for (fp = alist.head; fp < alist.last; fp++)
+                       freename(fp->fname);
+       }
+       if (dirp != NULL)
+               fprintf(stderr, "\n");
+}
+
+/*
+ * Read the contents of a directory.
+ */
+mkentry(name, ino, ap)
+       char *name;
+       ino_t ino;
+       register struct arglist *ap;
+{
+       register struct afile *fp;
+
+       if (ap->base == NULL) {
+               ap->nent = 20;
+               ap->base = (struct afile *)calloc((unsigned)ap->nent,
+                       sizeof (struct afile));
+               if (ap->base == NULL) {
+                       fprintf(stderr, "%s: out of memory\n", ap->cmd);
+                       return (FAIL);
+               }
+       }
+       if (ap->head == 0)
+               ap->head = ap->last = ap->base;
+       fp = ap->last;
+       fp->fnum = ino;
+       fp->fname = savename(name);
+       fp++;
+       if (fp == ap->head + ap->nent) {
+               ap->base = (struct afile *)realloc((char *)ap->base,
+                   (unsigned)(2 * ap->nent * sizeof (struct afile)));
+               if (ap->base == 0) {
+                       fprintf(stderr, "%s: out of memory\n", ap->cmd);
+                       return (FAIL);
+               }
+               ap->head = ap->base;
+               fp = ap->head + ap->nent;
+               ap->nent *= 2;
+       }
+       ap->last = fp;
+       return (GOOD);
+}
+
+/*
+ * Print out a pretty listing of a directory
+ */
+formatf(ap)
+       register struct arglist *ap;
+{
+       register struct afile *fp;
+       struct entry *np;
+       int width = 0, w, nentry = ap->last - ap->head;
+       int i, j, len, columns, lines;
+       char *cp;
+
+       if (ap->head == ap->last)
+               return;
+       for (fp = ap->head; fp < ap->last; fp++) {
+               fp->ftype = inodetype(fp->fnum);
+               np = lookupino(fp->fnum);
+               if (np != NIL)
+                       fp->fflags = np->e_flags;
+               else
+                       fp->fflags = 0;
+               len = strlen(fmtentry(fp));
+               if (len > width)
+                       width = len;
+       }
+       width += 2;
+       columns = 80 / width;
+       if (columns == 0)
+               columns = 1;
+       lines = (nentry + columns - 1) / columns;
+       for (i = 0; i < lines; i++) {
+               for (j = 0; j < columns; j++) {
+                       fp = ap->head + j * lines + i;
+                       cp = fmtentry(fp);
+                       fprintf(stderr, "%s", cp);
+                       if (fp + lines >= ap->last) {
+                               fprintf(stderr, "\n");
+                               break;
+                       }
+                       w = strlen(cp);
+                       while (w < width) {
+                               w++;
+                               fprintf(stderr, " ");
+                       }
+               }
+       }
+}
+
+/*
+ * Comparison routine for qsort.
+ */
+fcmp(f1, f2)
+       register struct afile *f1, *f2;
+{
+
+       return (strcmp(f1->fname, f2->fname));
+}
+
+/*
+ * Format a directory entry.
+ */
+char *
+fmtentry(fp)
+       register struct afile *fp;
+{
+       static char fmtres[BUFSIZ];
+       static int precision = 0;
+       int i;
+       register char *cp, *dp;
+
+       if (!vflag) {
+               fmtres[0] = '\0';
+       } else {
+               if (precision == 0)
+                       for (i = maxino; i > 0; i /= 10)
+                               precision++;
+               (void) sprintf(fmtres, "%*d ", precision, fp->fnum);
+       }
+       dp = &fmtres[strlen(fmtres)];
+       if (dflag && BIT(fp->fnum, dumpmap) == 0)
+               *dp++ = '^';
+       else if ((fp->fflags & NEW) != 0)
+               *dp++ = '*';
+       else
+               *dp++ = ' ';
+       for (cp = fp->fname; *cp; cp++)
+               if (!vflag && (*cp < ' ' || *cp >= 0177))
+                       *dp++ = '?';
+               else
+                       *dp++ = *cp;
+       if (fp->ftype == NODE)
+               *dp++ = '/';
+       *dp++ = 0;
+       return (fmtres);
+}
+
+/*
+ * respond to interrupts
+ */
+void
+onintr()
+{
+       if (command == 'i')
+               longjmp(reset, 1);
+       if (reply("restore interrupted, continue") == FAIL)
+               done(1);
+}
diff --git a/usr/src/sbin/restore/main.c b/usr/src/sbin/restore/main.c
new file mode 100644 (file)
index 0000000..12cf204
--- /dev/null
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 1983 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) 1983 The Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c     5.8 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+/*
+ *     Modified to recursively extract all files within a subtree
+ *     (supressed by the h option) and recreate the heirarchical
+ *     structure of that subtree and move extracted files to their
+ *     proper homes (supressed by the m option).
+ *     Includes the s (skip files) option for use with multiple
+ *     dumps on a single tape.
+ *     8/29/80         by Mike Litzkow
+ *
+ *     Modified to work on the new file system and to recover from
+ *     tape read errors.
+ *     1/19/82         by Kirk McKusick
+ *
+ *     Full incremental restore running entirely in user code and
+ *     interactive tape browser.
+ *     1/19/83         by Kirk McKusick
+ */
+
+#include "restore.h"
+#include <protocols/dumprestore.h>
+#include <sys/signal.h>
+#include "pathnames.h"
+
+int    bflag = 0, cvtflag = 0, dflag = 0, vflag = 0, yflag = 0;
+int    hflag = 1, mflag = 1, Nflag = 0;
+char   command = '\0';
+long   dumpnum = 1;
+long   volno = 0;
+long   ntrec;
+char   *dumpmap;
+char   *clrimap;
+ino_t  maxino;
+time_t dumptime;
+time_t dumpdate;
+FILE   *terminal;
+
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       register char *cp;
+       ino_t ino;
+       char *inputdev = _PATH_DEFTAPE;
+       char *symtbl = "./restoresymtable";
+       char name[MAXPATHLEN];
+       void onintr();
+
+       if (signal(SIGINT, onintr) == SIG_IGN)
+               (void) signal(SIGINT, SIG_IGN);
+       if (signal(SIGTERM, onintr) == SIG_IGN)
+               (void) signal(SIGTERM, SIG_IGN);
+       setlinebuf(stderr);
+       if (argc < 2) {
+usage:
+               fprintf(stderr, "Usage:\n%s%s%s%s%s",
+                       "\trestore tfhsvy [file file ...]\n",
+                       "\trestore xfhmsvy [file file ...]\n",
+                       "\trestore ifhmsvy\n",
+                       "\trestore rfsvy\n",
+                       "\trestore Rfsvy\n");
+               done(1);
+       }
+       argv++;
+       argc -= 2;
+       command = '\0';
+       for (cp = *argv++; *cp; cp++) {
+               switch (*cp) {
+               case '-':
+                       break;
+               case 'c':
+                       cvtflag++;
+                       break;
+               case 'd':
+                       dflag++;
+                       break;
+               case 'h':
+                       hflag = 0;
+                       break;
+               case 'm':
+                       mflag = 0;
+                       break;
+               case 'N':
+                       Nflag++;
+                       break;
+               case 'v':
+                       vflag++;
+                       break;
+               case 'y':
+                       yflag++;
+                       break;
+               case 'f':
+                       if (argc < 1) {
+                               fprintf(stderr, "missing device specifier\n");
+                               done(1);
+                       }
+                       inputdev = *argv++;
+                       argc--;
+                       break;
+               case 'b':
+                       /*
+                        * change default tape blocksize
+                        */
+                       bflag++;
+                       if (argc < 1) {
+                               fprintf(stderr, "missing block size\n");
+                               done(1);
+                       }
+                       ntrec = atoi(*argv++);
+                       if (ntrec <= 0) {
+                               fprintf(stderr, "Block size must be a positive integer\n");
+                               done(1);
+                       }
+                       argc--;
+                       break;
+               case 's':
+                       /*
+                        * dumpnum (skip to) for multifile dump tapes
+                        */
+                       if (argc < 1) {
+                               fprintf(stderr, "missing dump number\n");
+                               done(1);
+                       }
+                       dumpnum = atoi(*argv++);
+                       if (dumpnum <= 0) {
+                               fprintf(stderr, "Dump number must be a positive integer\n");
+                               done(1);
+                       }
+                       argc--;
+                       break;
+               case 't':
+               case 'R':
+               case 'r':
+               case 'x':
+               case 'i':
+                       if (command != '\0') {
+                               fprintf(stderr,
+                                       "%c and %c are mutually exclusive\n",
+                                       *cp, command);
+                               goto usage;
+                       }
+                       command = *cp;
+                       break;
+               default:
+                       fprintf(stderr, "Bad key character %c\n", *cp);
+                       goto usage;
+               }
+       }
+       if (command == '\0') {
+               fprintf(stderr, "must specify i, t, r, R, or x\n");
+               goto usage;
+       }
+       setinput(inputdev);
+       if (argc == 0) {
+               argc = 1;
+               *--argv = ".";
+       }
+       switch (command) {
+       /*
+        * Interactive mode.
+        */
+       case 'i':
+               setup();
+               extractdirs(1);
+               initsymtable((char *)0);
+               runcmdshell();
+               done(0);
+       /*
+        * Incremental restoration of a file system.
+        */
+       case 'r':
+               setup();
+               if (dumptime > 0) {
+                       /*
+                        * This is an incremental dump tape.
+                        */
+                       vprintf(stdout, "Begin incremental restore\n");
+                       initsymtable(symtbl);
+                       extractdirs(1);
+                       removeoldleaves();
+                       vprintf(stdout, "Calculate node updates.\n");
+                       treescan(".", ROOTINO, nodeupdates);
+                       findunreflinks();
+                       removeoldnodes();
+               } else {
+                       /*
+                        * This is a level zero dump tape.
+                        */
+                       vprintf(stdout, "Begin level 0 restore\n");
+                       initsymtable((char *)0);
+                       extractdirs(1);
+                       vprintf(stdout, "Calculate extraction list.\n");
+                       treescan(".", ROOTINO, nodeupdates);
+               }
+               createleaves(symtbl);
+               createlinks();
+               setdirmodes();
+               checkrestore();
+               if (dflag) {
+                       vprintf(stdout, "Verify the directory structure\n");
+                       treescan(".", ROOTINO, verifyfile);
+               }
+               dumpsymtable(symtbl, (long)1);
+               done(0);
+       /*
+        * Resume an incremental file system restoration.
+        */
+       case 'R':
+               initsymtable(symtbl);
+               skipmaps();
+               skipdirs();
+               createleaves(symtbl);
+               createlinks();
+               setdirmodes();
+               checkrestore();
+               dumpsymtable(symtbl, (long)1);
+               done(0);
+       /*
+        * List contents of tape.
+        */
+       case 't':
+               setup();
+               extractdirs(0);
+               initsymtable((char *)0);
+               while (argc--) {
+                       canon(*argv++, name);
+                       ino = dirlookup(name);
+                       if (ino == 0)
+                               continue;
+                       treescan(name, ino, listfile);
+               }
+               done(0);
+       /*
+        * Batch extraction of tape contents.
+        */
+       case 'x':
+               setup();
+               extractdirs(1);
+               initsymtable((char *)0);
+               while (argc--) {
+                       canon(*argv++, name);
+                       ino = dirlookup(name);
+                       if (ino == 0)
+                               continue;
+                       if (mflag)
+                               pathcheck(name);
+                       treescan(name, ino, addfile);
+               }
+               createfiles();
+               createlinks();
+               setdirmodes();
+               if (dflag)
+                       checkrestore();
+               done(0);
+       }
+}
diff --git a/usr/src/sbin/restore/pathnames.h b/usr/src/sbin/restore/pathnames.h
new file mode 100644 (file)
index 0000000..b3cf676
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * 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.2 (Berkeley) 6/1/90
+ */
+
+#include <paths.h>
+
+#define        _PATH_DEFTAPE   "/dev/rmt8"
diff --git a/usr/src/sbin/restore/restore.c b/usr/src/sbin/restore/restore.c
new file mode 100644 (file)
index 0000000..151d026
--- /dev/null
@@ -0,0 +1,791 @@
+/*
+ * Copyright (c) 1983 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[] = "@(#)restore.c  5.7 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+#include "restore.h"
+
+/*
+ * This implements the 't' option.
+ * List entries on the tape.
+ */
+long
+listfile(name, ino, type)
+       char *name;
+       ino_t ino;
+       int type;
+{
+       long descend = hflag ? GOOD : FAIL;
+
+       if (BIT(ino, dumpmap) == 0) {
+               return (descend);
+       }
+       vprintf(stdout, "%s", type == LEAF ? "leaf" : "dir ");
+       fprintf(stdout, "%10d\t%s\n", ino, name);
+       return (descend);
+}
+
+/*
+ * This implements the 'x' option.
+ * Request that new entries be extracted.
+ */
+long
+addfile(name, ino, type)
+       char *name;
+       ino_t ino;
+       int type;
+{
+       register struct entry *ep;
+       long descend = hflag ? GOOD : FAIL;
+       char buf[100];
+
+       if (BIT(ino, dumpmap) == 0) {
+               dprintf(stdout, "%s: not on the tape\n", name);
+               return (descend);
+       }
+       if (!mflag) {
+               (void) sprintf(buf, "./%u", ino);
+               name = buf;
+               if (type == NODE) {
+                       (void) genliteraldir(name, ino);
+                       return (descend);
+               }
+       }
+       ep = lookupino(ino);
+       if (ep != NIL) {
+               if (strcmp(name, myname(ep)) == 0) {
+                       ep->e_flags |= NEW;
+                       return (descend);
+               }
+               type |= LINK;
+       }
+       ep = addentry(name, ino, type);
+       if (type == NODE)
+               newnode(ep);
+       ep->e_flags |= NEW;
+       return (descend);
+}
+
+/*
+ * This is used by the 'i' option to undo previous requests made by addfile.
+ * Delete entries from the request queue.
+ */
+/* ARGSUSED */
+long
+deletefile(name, ino, type)
+       char *name;
+       ino_t ino;
+       int type;
+{
+       long descend = hflag ? GOOD : FAIL;
+       struct entry *ep;
+
+       if (BIT(ino, dumpmap) == 0) {
+               return (descend);
+       }
+       ep = lookupino(ino);
+       if (ep != NIL)
+               ep->e_flags &= ~NEW;
+       return (descend);
+}
+
+/* 
+ * The following four routines implement the incremental
+ * restore algorithm. The first removes old entries, the second
+ * does renames and calculates the extraction list, the third
+ * cleans up link names missed by the first two, and the final
+ * one deletes old directories.
+ *
+ * Directories cannot be immediately deleted, as they may have
+ * other files in them which need to be moved out first. As
+ * directories to be deleted are found, they are put on the 
+ * following deletion list. After all deletions and renames
+ * are done, this list is actually deleted.
+ */
+static struct entry *removelist;
+
+/*
+ *     Remove unneeded leaves from the old tree.
+ *     Remove directories from the lookup chains.
+ */
+removeoldleaves()
+{
+       register struct entry *ep;
+       register ino_t i;
+
+       vprintf(stdout, "Mark entries to be removed.\n");
+       for (i = ROOTINO + 1; i < maxino; i++) {
+               ep = lookupino(i);
+               if (ep == NIL)
+                       continue;
+               if (BIT(i, clrimap))
+                       continue;
+               for ( ; ep != NIL; ep = ep->e_links) {
+                       dprintf(stdout, "%s: REMOVE\n", myname(ep));
+                       if (ep->e_type == LEAF) {
+                               removeleaf(ep);
+                               freeentry(ep);
+                       } else {
+                               mktempname(ep);
+                               deleteino(ep->e_ino);
+                               ep->e_next = removelist;
+                               removelist = ep;
+                       }
+               }
+       }
+}
+
+/*
+ *     For each directory entry on the incremental tape, determine which
+ *     category it falls into as follows:
+ *     KEEP - entries that are to be left alone.
+ *     NEW - new entries to be added.
+ *     EXTRACT - files that must be updated with new contents.
+ *     LINK - new links to be added.
+ *     Renames are done at the same time.
+ */
+long
+nodeupdates(name, ino, type)
+       char *name;
+       ino_t ino;
+       int type;
+{
+       register struct entry *ep, *np, *ip;
+       long descend = GOOD;
+       int lookuptype = 0;
+       int key = 0;
+               /* key values */
+#              define ONTAPE   0x1     /* inode is on the tape */
+#              define INOFND   0x2     /* inode already exists */
+#              define NAMEFND  0x4     /* name already exists */
+#              define MODECHG  0x8     /* mode of inode changed */
+       extern char *keyval();
+
+       /*
+        * This routine is called once for each element in the 
+        * directory hierarchy, with a full path name.
+        * The "type" value is incorrectly specified as LEAF for
+        * directories that are not on the dump tape.
+        *
+        * Check to see if the file is on the tape.
+        */
+       if (BIT(ino, dumpmap))
+               key |= ONTAPE;
+       /*
+        * Check to see if the name exists, and if the name is a link.
+        */
+       np = lookupname(name);
+       if (np != NIL) {
+               key |= NAMEFND;
+               ip = lookupino(np->e_ino);
+               if (ip == NULL)
+                       panic("corrupted symbol table\n");
+               if (ip != np)
+                       lookuptype = LINK;
+       }
+       /*
+        * Check to see if the inode exists, and if one of its links
+        * corresponds to the name (if one was found).
+        */
+       ip = lookupino(ino);
+       if (ip != NIL) {
+               key |= INOFND;
+               for (ep = ip->e_links; ep != NIL; ep = ep->e_links) {
+                       if (ep == np) {
+                               ip = ep;
+                               break;
+                       }
+               }
+       }
+       /*
+        * If both a name and an inode are found, but they do not
+        * correspond to the same file, then both the inode that has
+        * been found and the inode corresponding to the name that
+        * has been found need to be renamed. The current pathname
+        * is the new name for the inode that has been found. Since
+        * all files to be deleted have already been removed, the
+        * named file is either a now unneeded link, or it must live
+        * under a new name in this dump level. If it is a link, it
+        * can be removed. If it is not a link, it is given a
+        * temporary name in anticipation that it will be renamed
+        * when it is later found by inode number.
+        */
+       if (((key & (INOFND|NAMEFND)) == (INOFND|NAMEFND)) && ip != np) {
+               if (lookuptype == LINK) {
+                       removeleaf(np);
+                       freeentry(np);
+               } else {
+                       dprintf(stdout, "name/inode conflict, mktempname %s\n",
+                               myname(np));
+                       mktempname(np);
+               }
+               np = NIL;
+               key &= ~NAMEFND;
+       }
+       if ((key & ONTAPE) &&
+         (((key & INOFND) && ip->e_type != type) ||
+          ((key & NAMEFND) && np->e_type != type)))
+               key |= MODECHG;
+
+       /*
+        * Decide on the disposition of the file based on its flags.
+        * Note that we have already handled the case in which
+        * a name and inode are found that correspond to different files.
+        * Thus if both NAMEFND and INOFND are set then ip == np.
+        */
+       switch (key) {
+
+       /*
+        * A previously existing file has been found.
+        * Mark it as KEEP so that other links to the inode can be
+        * detected, and so that it will not be reclaimed by the search
+        * for unreferenced names.
+        */
+       case INOFND|NAMEFND:
+               ip->e_flags |= KEEP;
+               dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
+                       flagvalues(ip));
+               break;
+
+       /*
+        * A file on the tape has a name which is the same as a name
+        * corresponding to a different file in the previous dump.
+        * Since all files to be deleted have already been removed,
+        * this file is either a now unneeded link, or it must live
+        * under a new name in this dump level. If it is a link, it
+        * can simply be removed. If it is not a link, it is given a
+        * temporary name in anticipation that it will be renamed
+        * when it is later found by inode number (see INOFND case
+        * below). The entry is then treated as a new file.
+        */
+       case ONTAPE|NAMEFND:
+       case ONTAPE|NAMEFND|MODECHG:
+               if (lookuptype == LINK) {
+                       removeleaf(np);
+                       freeentry(np);
+               } else {
+                       mktempname(np);
+               }
+               /* fall through */
+
+       /*
+        * A previously non-existent file.
+        * Add it to the file system, and request its extraction.
+        * If it is a directory, create it immediately.
+        * (Since the name is unused there can be no conflict)
+        */
+       case ONTAPE:
+               ep = addentry(name, ino, type);
+               if (type == NODE)
+                       newnode(ep);
+               ep->e_flags |= NEW|KEEP;
+               dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
+                       flagvalues(ep));
+               break;
+
+       /*
+        * A file with the same inode number, but a different
+        * name has been found. If the other name has not already
+        * been found (indicated by the KEEP flag, see above) then
+        * this must be a new name for the file, and it is renamed.
+        * If the other name has been found then this must be a
+        * link to the file. Hard links to directories are not
+        * permitted, and are either deleted or converted to
+        * symbolic links. Finally, if the file is on the tape,
+        * a request is made to extract it.
+        */
+       case ONTAPE|INOFND:
+               if (type == LEAF && (ip->e_flags & KEEP) == 0)
+                       ip->e_flags |= EXTRACT;
+               /* fall through */
+       case INOFND:
+               if ((ip->e_flags & KEEP) == 0) {
+                       renameit(myname(ip), name);
+                       moveentry(ip, name);
+                       ip->e_flags |= KEEP;
+                       dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
+                               flagvalues(ip));
+                       break;
+               }
+               if (ip->e_type == NODE) {
+                       descend = FAIL;
+                       fprintf(stderr,
+                               "deleted hard link %s to directory %s\n",
+                               name, myname(ip));
+                       break;
+               }
+               ep = addentry(name, ino, type|LINK);
+               ep->e_flags |= NEW;
+               dprintf(stdout, "[%s] %s: %s|LINK\n", keyval(key), name,
+                       flagvalues(ep));
+               break;
+
+       /*
+        * A previously known file which is to be updated.
+        */
+       case ONTAPE|INOFND|NAMEFND:
+               if (type == LEAF && lookuptype != LINK)
+                       np->e_flags |= EXTRACT;
+               np->e_flags |= KEEP;
+               dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
+                       flagvalues(np));
+               break;
+
+       /*
+        * An inode is being reused in a completely different way.
+        * Normally an extract can simply do an "unlink" followed
+        * by a "creat". Here we must do effectively the same
+        * thing. The complications arise because we cannot really
+        * delete a directory since it may still contain files
+        * that we need to rename, so we delete it from the symbol
+        * table, and put it on the list to be deleted eventually.
+        * Conversely if a directory is to be created, it must be
+        * done immediately, rather than waiting until the 
+        * extraction phase.
+        */
+       case ONTAPE|INOFND|MODECHG:
+       case ONTAPE|INOFND|NAMEFND|MODECHG:
+               if (ip->e_flags & KEEP) {
+                       badentry(ip, "cannot KEEP and change modes");
+                       break;
+               }
+               if (ip->e_type == LEAF) {
+                       /* changing from leaf to node */
+                       removeleaf(ip);
+                       freeentry(ip);
+                       ip = addentry(name, ino, type);
+                       newnode(ip);
+               } else {
+                       /* changing from node to leaf */
+                       if ((ip->e_flags & TMPNAME) == 0)
+                               mktempname(ip);
+                       deleteino(ip->e_ino);
+                       ip->e_next = removelist;
+                       removelist = ip;
+                       ip = addentry(name, ino, type);
+               }
+               ip->e_flags |= NEW|KEEP;
+               dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
+                       flagvalues(ip));
+               break;
+
+       /*
+        * A hard link to a diirectory that has been removed.
+        * Ignore it.
+        */
+       case NAMEFND:
+               dprintf(stdout, "[%s] %s: Extraneous name\n", keyval(key),
+                       name);
+               descend = FAIL;
+               break;
+
+       /*
+        * If we find a directory entry for a file that is not on
+        * the tape, then we must have found a file that was created
+        * while the dump was in progress. Since we have no contents
+        * for it, we discard the name knowing that it will be on the
+        * next incremental tape.
+        */
+       case NIL:
+               fprintf(stderr, "%s: (inode %d) not found on tape\n",
+                       name, ino);
+               break;
+
+       /*
+        * If any of these arise, something is grievously wrong with
+        * the current state of the symbol table.
+        */
+       case INOFND|NAMEFND|MODECHG:
+       case NAMEFND|MODECHG:
+       case INOFND|MODECHG:
+               fprintf(stderr, "[%s] %s: inconsistent state\n", keyval(key),
+                       name);
+               break;
+
+       /*
+        * These states "cannot" arise for any state of the symbol table.
+        */
+       case ONTAPE|MODECHG:
+       case MODECHG:
+       default:
+               panic("[%s] %s: impossible state\n", keyval(key), name);
+               break;
+       }       
+       return (descend);
+}
+
+/*
+ * Calculate the active flags in a key.
+ */
+char *
+keyval(key)
+       int key;
+{
+       static char keybuf[32];
+
+       (void) strcpy(keybuf, "|NIL");
+       keybuf[0] = '\0';
+       if (key & ONTAPE)
+               (void) strcat(keybuf, "|ONTAPE");
+       if (key & INOFND)
+               (void) strcat(keybuf, "|INOFND");
+       if (key & NAMEFND)
+               (void) strcat(keybuf, "|NAMEFND");
+       if (key & MODECHG)
+               (void) strcat(keybuf, "|MODECHG");
+       return (&keybuf[1]);
+}
+
+/*
+ * Find unreferenced link names.
+ */
+findunreflinks()
+{
+       register struct entry *ep, *np;
+       register ino_t i;
+
+       vprintf(stdout, "Find unreferenced names.\n");
+       for (i = ROOTINO; i < maxino; i++) {
+               ep = lookupino(i);
+               if (ep == NIL || ep->e_type == LEAF || BIT(i, dumpmap) == 0)
+                       continue;
+               for (np = ep->e_entries; np != NIL; np = np->e_sibling) {
+                       if (np->e_flags == 0) {
+                               dprintf(stdout,
+                                   "%s: remove unreferenced name\n",
+                                   myname(np));
+                               removeleaf(np);
+                               freeentry(np);
+                       }
+               }
+       }
+       /*
+        * Any leaves remaining in removed directories is unreferenced.
+        */
+       for (ep = removelist; ep != NIL; ep = ep->e_next) {
+               for (np = ep->e_entries; np != NIL; np = np->e_sibling) {
+                       if (np->e_type == LEAF) {
+                               if (np->e_flags != 0)
+                                       badentry(np, "unreferenced with flags");
+                               dprintf(stdout,
+                                   "%s: remove unreferenced name\n",
+                                   myname(np));
+                               removeleaf(np);
+                               freeentry(np);
+                       }
+               }
+       }
+}
+
+/*
+ * Remove old nodes (directories).
+ * Note that this routine runs in O(N*D) where:
+ *     N is the number of directory entries to be removed.
+ *     D is the maximum depth of the tree.
+ * If N == D this can be quite slow. If the list were
+ * topologically sorted, the deletion could be done in
+ * time O(N).
+ */
+removeoldnodes()
+{
+       register struct entry *ep, **prev;
+       long change;
+
+       vprintf(stdout, "Remove old nodes (directories).\n");
+       do      {
+               change = 0;
+               prev = &removelist;
+               for (ep = removelist; ep != NIL; ep = *prev) {
+                       if (ep->e_entries != NIL) {
+                               prev = &ep->e_next;
+                               continue;
+                       }
+                       *prev = ep->e_next;
+                       removenode(ep);
+                       freeentry(ep);
+                       change++;
+               }
+       } while (change);
+       for (ep = removelist; ep != NIL; ep = ep->e_next)
+               badentry(ep, "cannot remove, non-empty");
+}
+
+/*
+ * This is the routine used to extract files for the 'r' command.
+ * Extract new leaves.
+ */
+createleaves(symtabfile)
+       char *symtabfile;
+{
+       register struct entry *ep;
+       ino_t first;
+       long curvol;
+
+       if (command == 'R') {
+               vprintf(stdout, "Continue extraction of new leaves\n");
+       } else {
+               vprintf(stdout, "Extract new leaves.\n");
+               dumpsymtable(symtabfile, volno);
+       }
+       first = lowerbnd(ROOTINO);
+       curvol = volno;
+       while (curfile.ino < maxino) {
+               first = lowerbnd(first);
+               /*
+                * If the next available file is not the one which we
+                * expect then we have missed one or more files. Since
+                * we do not request files that were not on the tape,
+                * the lost files must have been due to a tape read error,
+                * or a file that was removed while the dump was in progress.
+                */
+               while (first < curfile.ino) {
+                       ep = lookupino(first);
+                       if (ep == NIL)
+                               panic("%d: bad first\n", first);
+                       fprintf(stderr, "%s: not found on tape\n", myname(ep));
+                       ep->e_flags &= ~(NEW|EXTRACT);
+                       first = lowerbnd(first);
+               }
+               /*
+                * If we find files on the tape that have no corresponding
+                * directory entries, then we must have found a file that
+                * was created while the dump was in progress. Since we have 
+                * no name for it, we discard it knowing that it will be
+                * on the next incremental tape.
+                */
+               if (first != curfile.ino) {
+                       fprintf(stderr, "expected next file %d, got %d\n",
+                               first, curfile.ino);
+                       skipfile();
+                       goto next;
+               }
+               ep = lookupino(curfile.ino);
+               if (ep == NIL)
+                       panic("unknown file on tape\n");
+               if ((ep->e_flags & (NEW|EXTRACT)) == 0)
+                       badentry(ep, "unexpected file on tape");
+               /*
+                * If the file is to be extracted, then the old file must
+                * be removed since its type may change from one leaf type
+                * to another (eg "file" to "character special").
+                */
+               if ((ep->e_flags & EXTRACT) != 0) {
+                       removeleaf(ep);
+                       ep->e_flags &= ~REMOVED;
+               }
+               (void) extractfile(myname(ep));
+               ep->e_flags &= ~(NEW|EXTRACT);
+               /*
+                * We checkpoint the restore after every tape reel, so
+                * as to simplify the amount of work re quired by the
+                * 'R' command.
+                */
+       next:
+               if (curvol != volno) {
+                       dumpsymtable(symtabfile, volno);
+                       skipmaps();
+                       curvol = volno;
+               }
+       }
+}
+
+/*
+ * This is the routine used to extract files for the 'x' and 'i' commands.
+ * Efficiently extract a subset of the files on a tape.
+ */
+createfiles()
+{
+       register ino_t first, next, last;
+       register struct entry *ep;
+       long curvol;
+
+       vprintf(stdout, "Extract requested files\n");
+       curfile.action = SKIP;
+       getvol((long)1);
+       skipmaps();
+       skipdirs();
+       first = lowerbnd(ROOTINO);
+       last = upperbnd(maxino - 1);
+       for (;;) {
+               first = lowerbnd(first);
+               last = upperbnd(last);
+               /*
+                * Check to see if any files remain to be extracted
+                */
+               if (first > last)
+                       return;
+               /*
+                * Reject any volumes with inodes greater
+                * than the last one needed
+                */
+               while (curfile.ino > last) {
+                       curfile.action = SKIP;
+                       getvol((long)0);
+                       skipmaps();
+                       skipdirs();
+               }
+               /*
+                * Decide on the next inode needed.
+                * Skip across the inodes until it is found
+                * or an out of order volume change is encountered
+                */
+               next = lowerbnd(curfile.ino);
+               do      {
+                       curvol = volno;
+                       while (next > curfile.ino && volno == curvol)
+                               skipfile();
+                       skipmaps();
+                       skipdirs();
+               } while (volno == curvol + 1);
+               /*
+                * If volume change out of order occurred the
+                * current state must be recalculated
+                */
+               if (volno != curvol)
+                       continue;
+               /*
+                * If the current inode is greater than the one we were
+                * looking for then we missed the one we were looking for.
+                * Since we only attempt to extract files listed in the
+                * dump map, the lost files must have been due to a tape
+                * read error, or a file that was removed while the dump
+                * was in progress. Thus we report all requested files
+                * between the one we were looking for, and the one we
+                * found as missing, and delete their request flags.
+                */
+               while (next < curfile.ino) {
+                       ep = lookupino(next);
+                       if (ep == NIL)
+                               panic("corrupted symbol table\n");
+                       fprintf(stderr, "%s: not found on tape\n", myname(ep));
+                       ep->e_flags &= ~NEW;
+                       next = lowerbnd(next);
+               }
+               /*
+                * The current inode is the one that we are looking for,
+                * so extract it per its requested name.
+                */
+               if (next == curfile.ino && next <= last) {
+                       ep = lookupino(next);
+                       if (ep == NIL)
+                               panic("corrupted symbol table\n");
+                       (void) extractfile(myname(ep));
+                       ep->e_flags &= ~NEW;
+                       if (volno != curvol)
+                               skipmaps();
+               }
+       }
+}
+
+/*
+ * Add links.
+ */
+createlinks()
+{
+       register struct entry *np, *ep;
+       register ino_t i;
+       char name[BUFSIZ];
+
+       vprintf(stdout, "Add links\n");
+       for (i = ROOTINO; i < maxino; i++) {
+               ep = lookupino(i);
+               if (ep == NIL)
+                       continue;
+               for (np = ep->e_links; np != NIL; np = np->e_links) {
+                       if ((np->e_flags & NEW) == 0)
+                               continue;
+                       (void) strcpy(name, myname(ep));
+                       if (ep->e_type == NODE) {
+                               (void) linkit(name, myname(np), SYMLINK);
+                       } else {
+                               (void) linkit(name, myname(np), HARDLINK);
+                       }
+                       np->e_flags &= ~NEW;
+               }
+       }
+}
+
+/*
+ * Check the symbol table.
+ * We do this to insure that all the requested work was done, and
+ * that no temporary names remain.
+ */
+checkrestore()
+{
+       register struct entry *ep;
+       register ino_t i;
+
+       vprintf(stdout, "Check the symbol table.\n");
+       for (i = ROOTINO; i < maxino; i++) {
+               for (ep = lookupino(i); ep != NIL; ep = ep->e_links) {
+                       ep->e_flags &= ~KEEP;
+                       if (ep->e_type == NODE)
+                               ep->e_flags &= ~(NEW|EXISTED);
+                       if (ep->e_flags != NULL)
+                               badentry(ep, "incomplete operations");
+               }
+       }
+}
+
+/*
+ * Compare with the directory structure on the tape
+ * A paranoid check that things are as they should be.
+ */
+long
+verifyfile(name, ino, type)
+       char *name;
+       ino_t ino;
+       int type;
+{
+       struct entry *np, *ep;
+       long descend = GOOD;
+
+       ep = lookupname(name);
+       if (ep == NIL) {
+               fprintf(stderr, "Warning: missing name %s\n", name);
+               return (FAIL);
+       }
+       np = lookupino(ino);
+       if (np != ep)
+               descend = FAIL;
+       for ( ; np != NIL; np = np->e_links)
+               if (np == ep)
+                       break;
+       if (np == NIL)
+               panic("missing inumber %d\n", ino);
+       if (ep->e_type == LEAF && type != LEAF)
+               badentry(ep, "type should be LEAF");
+       return (descend);
+}
diff --git a/usr/src/sbin/restore/restore.h b/usr/src/sbin/restore/restore.h
new file mode 100644 (file)
index 0000000..fc8a9c9
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 1983 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.
+ *
+ *     @(#)restore.h   5.8 (Berkeley) 6/1/90
+ */
+
+#include <stdio.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <ufs/dinode.h>
+#include <ufs/fs.h>
+
+/*
+ * Flags
+ */
+extern int     cvtflag;        /* convert from old to new tape format */
+extern int     bflag;          /* set input block size */
+extern int     dflag;          /* print out debugging info */
+extern int     hflag;          /* restore heirarchies */
+extern int     mflag;          /* restore by name instead of inode number */
+extern int     Nflag;          /* do not write the disk */
+extern int     vflag;          /* print out actions taken */
+extern int     yflag;          /* always try to recover from tape errors */
+/*
+ * Global variables
+ */
+extern char    *dumpmap;       /* map of inodes on this dump tape */
+extern char    *clrimap;       /* map of inodes to be deleted */
+extern ino_t   maxino;         /* highest numbered inode in this file system */
+extern long    dumpnum;        /* location of the dump on this tape */
+extern long    volno;          /* current volume being read */
+extern long    ntrec;          /* number of TP_BSIZE records per tape block */
+extern time_t  dumptime;       /* time that this dump begins */
+extern time_t  dumpdate;       /* time that this dump was made */
+extern char    command;        /* opration being performed */
+extern FILE    *terminal;      /* file descriptor for the terminal input */
+
+/*
+ * Each file in the file system is described by one of these entries
+ */
+struct entry {
+       char    *e_name;                /* the current name of this entry */
+       u_char  e_namlen;               /* length of this name */
+       char    e_type;                 /* type of this entry, see below */
+       short   e_flags;                /* status flags, see below */
+       ino_t   e_ino;                  /* inode number in previous file sys */
+       long    e_index;                /* unique index (for dumpped table) */
+       struct  entry *e_parent;        /* pointer to parent directory (..) */
+       struct  entry *e_sibling;       /* next element in this directory (.) */
+       struct  entry *e_links;         /* hard links to this inode */
+       struct  entry *e_entries;       /* for directories, their entries */
+       struct  entry *e_next;          /* hash chain list */
+};
+/* types */
+#define        LEAF 1                  /* non-directory entry */
+#define NODE 2                 /* directory entry */
+#define LINK 4                 /* synthesized type, stripped by addentry */
+/* flags */
+#define EXTRACT                0x0001  /* entry is to be replaced from the tape */
+#define NEW            0x0002  /* a new entry to be extracted */
+#define KEEP           0x0004  /* entry is not to change */
+#define REMOVED                0x0010  /* entry has been removed */
+#define TMPNAME                0x0020  /* entry has been given a temporary name */
+#define EXISTED                0x0040  /* directory already existed during extract */
+/*
+ * functions defined on entry structs
+ */
+extern struct entry *lookupino();
+extern struct entry *lookupname();
+extern struct entry *lookupparent();
+extern struct entry *addentry();
+extern char *myname();
+extern char *savename();
+extern char *gentempname();
+extern char *flagvalues();
+extern ino_t lowerbnd();
+extern ino_t upperbnd();
+#define NIL ((struct entry *)(0))
+/*
+ * Constants associated with entry structs
+ */
+#define HARDLINK       1
+#define SYMLINK                2
+#define TMPHDR         "RSTTMP"
+
+/*
+ * The entry describes the next file available on the tape
+ */
+struct context {
+       char    *name;          /* name of file */
+       ino_t   ino;            /* inumber of file */
+       struct  dinode *dip;    /* pointer to inode */
+       char    action;         /* action being taken on this file */
+} curfile;
+/* actions */
+#define        USING   1       /* extracting from the tape */
+#define        SKIP    2       /* skipping */
+#define UNKNOWN 3      /* disposition or starting point is unknown */
+
+/*
+ * Definitions for library routines operating on directories.
+ */
+typedef struct dirdesc DIR;
+extern DIR *rst_opendir();
+extern struct direct *rst_readdir();
+
+/*
+ * Other exported routines
+ */
+extern ino_t psearch();
+extern ino_t dirlookup();
+extern long listfile();
+extern long deletefile();
+extern long addfile();
+extern long nodeupdates();
+extern long verifyfile();
+extern char *rindex();
+extern char *index();
+extern char *strcat();
+extern char *strncat();
+extern char *strcpy();
+extern char *strncpy();
+extern char *fgets();
+extern char *mktemp();
+extern char *malloc();
+extern char *calloc();
+extern char *realloc();
+extern long lseek();
+
+/*
+ * Useful macros
+ */
+#define        MWORD(m,i) (m[(unsigned)(i-1)/NBBY])
+#define        MBIT(i) (1<<((unsigned)(i-1)%NBBY))
+#define        BIS(i,w)        (MWORD(w,i) |=  MBIT(i))
+#define        BIC(i,w)        (MWORD(w,i) &= ~MBIT(i))
+#define        BIT(i,w)        (MWORD(w,i) & MBIT(i))
+
+#define dprintf                if (dflag) fprintf
+#define vprintf                if (vflag) fprintf
+
+#define GOOD 1
+#define FAIL 0
diff --git a/usr/src/sbin/restore/rrestore.8 b/usr/src/sbin/restore/rrestore.8
new file mode 100644 (file)
index 0000000..e597806
--- /dev/null
@@ -0,0 +1,76 @@
+.\" Copyright (c) 1983, 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.
+.\"
+.\"     @(#)rrestore.8 6.5 (Berkeley) 3/16/91
+.\"
+.Dd March 16, 1991
+.Dt RRESTORE 8
+.Os BSD 4.2
+.Sh NAME
+.Nm rrestore
+.Nd "restore a file system dump across the network"
+.Sh SYNOPSIS
+.Nm rrestore
+.Oo
+.Ar key
+.Op Ar name ...
+.Oc
+.Sh DESCRIPTION
+.Nm Rrestore
+remotely restores files across a network in the same manner as
+.Xr restore 8
+locally restores files.
+.Xr dump 8 .
+The command is identical in operation to
+.Xr restore 8
+except the 
+.Cm f
+key should be specified and the file
+supplied should be of the form
+.Ar machine:device .
+.Pp
+.Nm Rrestore
+creates a remote server,
+rmt,
+on the client machine to access the tape
+device.
+.Sh SEE ALSO
+.Xr restore 8 ,
+.Xr rmt 8
+.Sh DIAGNOSTICS
+Same as 
+.Xr restore 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/restore/symtab.c b/usr/src/sbin/restore/symtab.c
new file mode 100644 (file)
index 0000000..20eafb1
--- /dev/null
@@ -0,0 +1,606 @@
+/*
+ * Copyright (c) 1983 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[] = "@(#)symtab.c   5.5 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+/*
+ * These routines maintain the symbol table which tracks the state
+ * of the file system being restored. They provide lookup by either
+ * name or inode number. They also provide for creation, deletion,
+ * and renaming of entries. Because of the dynamic nature of pathnames,
+ * names should not be saved, but always constructed just before they
+ * are needed, by calling "myname".
+ */
+
+#include "restore.h"
+#include <sys/stat.h>
+#include <ufs/dir.h>
+
+/*
+ * The following variables define the inode symbol table.
+ * The primary hash table is dynamically allocated based on
+ * the number of inodes in the file system (maxino), scaled by
+ * HASHFACTOR. The variable "entry" points to the hash table;
+ * the variable "entrytblsize" indicates its size (in entries).
+ */
+#define HASHFACTOR 5
+static struct entry **entry;
+static long entrytblsize;
+
+/*
+ * Look up an entry by inode number
+ */
+struct entry *
+lookupino(inum)
+       ino_t inum;
+{
+       register struct entry *ep;
+
+       if (inum < ROOTINO || inum >= maxino)
+               return (NIL);
+       for (ep = entry[inum % entrytblsize]; ep != NIL; ep = ep->e_next)
+               if (ep->e_ino == inum)
+                       return (ep);
+       return (NIL);
+}
+
+/*
+ * Add an entry into the entry table
+ */
+addino(inum, np)
+       ino_t inum;
+       struct entry *np;
+{
+       struct entry **epp;
+
+       if (inum < ROOTINO || inum >= maxino)
+               panic("addino: out of range %d\n", inum);
+       epp = &entry[inum % entrytblsize];
+       np->e_ino = inum;
+       np->e_next = *epp;
+       *epp = np;
+       if (dflag)
+               for (np = np->e_next; np != NIL; np = np->e_next)
+                       if (np->e_ino == inum)
+                               badentry(np, "duplicate inum");
+}
+
+/*
+ * Delete an entry from the entry table
+ */
+deleteino(inum)
+       ino_t inum;
+{
+       register struct entry *next;
+       struct entry **prev;
+
+       if (inum < ROOTINO || inum >= maxino)
+               panic("deleteino: out of range %d\n", inum);
+       prev = &entry[inum % entrytblsize];
+       for (next = *prev; next != NIL; next = next->e_next) {
+               if (next->e_ino == inum) {
+                       next->e_ino = 0;
+                       *prev = next->e_next;
+                       return;
+               }
+               prev = &next->e_next;
+       }
+       panic("deleteino: %d not found\n", inum);
+}
+
+/*
+ * Look up an entry by name
+ */
+struct entry *
+lookupname(name)
+       char *name;
+{
+       register struct entry *ep;
+       register char *np, *cp;
+       char buf[MAXPATHLEN];
+
+       cp = name;
+       for (ep = lookupino(ROOTINO); ep != NIL; ep = ep->e_entries) {
+               for (np = buf; *cp != '/' && *cp != '\0'; )
+                       *np++ = *cp++;
+               *np = '\0';
+               for ( ; ep != NIL; ep = ep->e_sibling)
+                       if (strcmp(ep->e_name, buf) == 0)
+                               break;
+               if (ep == NIL)
+                       break;
+               if (*cp++ == '\0')
+                       return (ep);
+       }
+       return (NIL);
+}
+
+/*
+ * Look up the parent of a pathname
+ */
+struct entry *
+lookupparent(name)
+       char *name;
+{
+       struct entry *ep;
+       char *tailindex;
+
+       tailindex = rindex(name, '/');
+       if (tailindex == 0)
+               return (NIL);
+       *tailindex = '\0';
+       ep = lookupname(name);
+       *tailindex = '/';
+       if (ep == NIL)
+               return (NIL);
+       if (ep->e_type != NODE)
+               panic("%s is not a directory\n", name);
+       return (ep);
+}
+
+/*
+ * Determine the current pathname of a node or leaf
+ */
+char *
+myname(ep)
+       register struct entry *ep;
+{
+       register char *cp;
+       static char namebuf[MAXPATHLEN];
+
+       for (cp = &namebuf[MAXPATHLEN - 2]; cp > &namebuf[ep->e_namlen]; ) {
+               cp -= ep->e_namlen;
+               bcopy(ep->e_name, cp, (long)ep->e_namlen);
+               if (ep == lookupino(ROOTINO))
+                       return (cp);
+               *(--cp) = '/';
+               ep = ep->e_parent;
+       }
+       panic("%s: pathname too long\n", cp);
+       return(cp);
+}
+
+/*
+ * Unused symbol table entries are linked together on a freelist
+ * headed by the following pointer.
+ */
+static struct entry *freelist = NIL;
+
+/*
+ * add an entry to the symbol table
+ */
+struct entry *
+addentry(name, inum, type)
+       char *name;
+       ino_t inum;
+       int type;
+{
+       register struct entry *np, *ep;
+
+       if (freelist != NIL) {
+               np = freelist;
+               freelist = np->e_next;
+               bzero((char *)np, (long)sizeof(struct entry));
+       } else {
+               np = (struct entry *)calloc(1, sizeof(struct entry));
+               if (np == NIL)
+                       panic("no memory to extend symbol table\n");
+       }
+       np->e_type = type & ~LINK;
+       ep = lookupparent(name);
+       if (ep == NIL) {
+               if (inum != ROOTINO || lookupino(ROOTINO) != NIL)
+                       panic("bad name to addentry %s\n", name);
+               np->e_name = savename(name);
+               np->e_namlen = strlen(name);
+               np->e_parent = np;
+               addino(ROOTINO, np);
+               return (np);
+       }
+       np->e_name = savename(rindex(name, '/') + 1);
+       np->e_namlen = strlen(np->e_name);
+       np->e_parent = ep;
+       np->e_sibling = ep->e_entries;
+       ep->e_entries = np;
+       if (type & LINK) {
+               ep = lookupino(inum);
+               if (ep == NIL)
+                       panic("link to non-existant name\n");
+               np->e_ino = inum;
+               np->e_links = ep->e_links;
+               ep->e_links = np;
+       } else if (inum != 0) {
+               if (lookupino(inum) != NIL)
+                       panic("duplicate entry\n");
+               addino(inum, np);
+       }
+       return (np);
+}
+
+/*
+ * delete an entry from the symbol table
+ */
+freeentry(ep)
+       register struct entry *ep;
+{
+       register struct entry *np;
+       ino_t inum;
+
+       if (ep->e_flags != REMOVED)
+               badentry(ep, "not marked REMOVED");
+       if (ep->e_type == NODE) {
+               if (ep->e_links != NIL)
+                       badentry(ep, "freeing referenced directory");
+               if (ep->e_entries != NIL)
+                       badentry(ep, "freeing non-empty directory");
+       }
+       if (ep->e_ino != 0) {
+               np = lookupino(ep->e_ino);
+               if (np == NIL)
+                       badentry(ep, "lookupino failed");
+               if (np == ep) {
+                       inum = ep->e_ino;
+                       deleteino(inum);
+                       if (ep->e_links != NIL)
+                               addino(inum, ep->e_links);
+               } else {
+                       for (; np != NIL; np = np->e_links) {
+                               if (np->e_links == ep) {
+                                       np->e_links = ep->e_links;
+                                       break;
+                               }
+                       }
+                       if (np == NIL)
+                               badentry(ep, "link not found");
+               }
+       }
+       removeentry(ep);
+       freename(ep->e_name);
+       ep->e_next = freelist;
+       freelist = ep;
+}
+
+/*
+ * Relocate an entry in the tree structure
+ */
+moveentry(ep, newname)
+       register struct entry *ep;
+       char *newname;
+{
+       struct entry *np;
+       char *cp;
+
+       np = lookupparent(newname);
+       if (np == NIL)
+               badentry(ep, "cannot move ROOT");
+       if (np != ep->e_parent) {
+               removeentry(ep);
+               ep->e_parent = np;
+               ep->e_sibling = np->e_entries;
+               np->e_entries = ep;
+       }
+       cp = rindex(newname, '/') + 1;
+       freename(ep->e_name);
+       ep->e_name = savename(cp);
+       ep->e_namlen = strlen(cp);
+       if (strcmp(gentempname(ep), ep->e_name) == 0)
+               ep->e_flags |= TMPNAME;
+       else
+               ep->e_flags &= ~TMPNAME;
+}
+
+/*
+ * Remove an entry in the tree structure
+ */
+removeentry(ep)
+       register struct entry *ep;
+{
+       register struct entry *np;
+
+       np = ep->e_parent;
+       if (np->e_entries == ep) {
+               np->e_entries = ep->e_sibling;
+       } else {
+               for (np = np->e_entries; np != NIL; np = np->e_sibling) {
+                       if (np->e_sibling == ep) {
+                               np->e_sibling = ep->e_sibling;
+                               break;
+                       }
+               }
+               if (np == NIL)
+                       badentry(ep, "cannot find entry in parent list");
+       }
+}
+
+/*
+ * Table of unused string entries, sorted by length.
+ * 
+ * Entries are allocated in STRTBLINCR sized pieces so that names
+ * of similar lengths can use the same entry. The value of STRTBLINCR
+ * is chosen so that every entry has at least enough space to hold
+ * a "struct strtbl" header. Thus every entry can be linked onto an
+ * apprpriate free list.
+ *
+ * NB. The macro "allocsize" below assumes that "struct strhdr"
+ *     has a size that is a power of two.
+ */
+struct strhdr {
+       struct strhdr *next;
+};
+
+#define STRTBLINCR     (sizeof(struct strhdr))
+#define allocsize(size)        (((size) + 1 + STRTBLINCR - 1) & ~(STRTBLINCR - 1))
+
+static struct strhdr strtblhdr[allocsize(MAXNAMLEN) / STRTBLINCR];
+
+/*
+ * Allocate space for a name. It first looks to see if it already
+ * has an appropriate sized entry, and if not allocates a new one.
+ */
+char *
+savename(name)
+       char *name;
+{
+       struct strhdr *np;
+       long len;
+       char *cp;
+
+       if (name == NULL)
+               panic("bad name\n");
+       len = strlen(name);
+       np = strtblhdr[len / STRTBLINCR].next;
+       if (np != NULL) {
+               strtblhdr[len / STRTBLINCR].next = np->next;
+               cp = (char *)np;
+       } else {
+               cp = malloc((unsigned)allocsize(len));
+               if (cp == NULL)
+                       panic("no space for string table\n");
+       }
+       (void) strcpy(cp, name);
+       return (cp);
+}
+
+/*
+ * Free space for a name. The resulting entry is linked onto the
+ * appropriate free list.
+ */
+freename(name)
+       char *name;
+{
+       struct strhdr *tp, *np;
+       
+       tp = &strtblhdr[strlen(name) / STRTBLINCR];
+       np = (struct strhdr *)name;
+       np->next = tp->next;
+       tp->next = np;
+}
+
+/*
+ * Useful quantities placed at the end of a dumped symbol table.
+ */
+struct symtableheader {
+       long    volno;
+       long    stringsize;
+       long    entrytblsize;
+       time_t  dumptime;
+       time_t  dumpdate;
+       ino_t   maxino;
+       long    ntrec;
+};
+
+/*
+ * dump a snapshot of the symbol table
+ */
+dumpsymtable(filename, checkpt)
+       char *filename;
+       long checkpt;
+{
+       register struct entry *ep, *tep;
+       register ino_t i;
+       struct entry temp, *tentry;
+       long mynum = 1, stroff = 0;
+       FILE *fd;
+       struct symtableheader hdr;
+
+       vprintf(stdout, "Check pointing the restore\n");
+       if (Nflag)
+               return;
+       if ((fd = fopen(filename, "w")) == NULL) {
+               perror("fopen");
+               panic("cannot create save file %s for symbol table\n",
+                       filename);
+       }
+       clearerr(fd);
+       /*
+        * Assign indicies to each entry
+        * Write out the string entries
+        */
+       for (i = ROOTINO; i < maxino; i++) {
+               for (ep = lookupino(i); ep != NIL; ep = ep->e_links) {
+                       ep->e_index = mynum++;
+                       (void) fwrite(ep->e_name, sizeof(char),
+                              (int)allocsize(ep->e_namlen), fd);
+               }
+       }
+       /*
+        * Convert pointers to indexes, and output
+        */
+       tep = &temp;
+       stroff = 0;
+       for (i = ROOTINO; i < maxino; i++) {
+               for (ep = lookupino(i); ep != NIL; ep = ep->e_links) {
+                       bcopy((char *)ep, (char *)tep,
+                               (long)sizeof(struct entry));
+                       tep->e_name = (char *)stroff;
+                       stroff += allocsize(ep->e_namlen);
+                       tep->e_parent = (struct entry *)ep->e_parent->e_index;
+                       if (ep->e_links != NIL)
+                               tep->e_links =
+                                       (struct entry *)ep->e_links->e_index;
+                       if (ep->e_sibling != NIL)
+                               tep->e_sibling =
+                                       (struct entry *)ep->e_sibling->e_index;
+                       if (ep->e_entries != NIL)
+                               tep->e_entries =
+                                       (struct entry *)ep->e_entries->e_index;
+                       if (ep->e_next != NIL)
+                               tep->e_next =
+                                       (struct entry *)ep->e_next->e_index;
+                       (void) fwrite((char *)tep, sizeof(struct entry), 1, fd);
+               }
+       }
+       /*
+        * Convert entry pointers to indexes, and output
+        */
+       for (i = 0; i < entrytblsize; i++) {
+               if (entry[i] == NIL)
+                       tentry = NIL;
+               else
+                       tentry = (struct entry *)entry[i]->e_index;
+               (void) fwrite((char *)&tentry, sizeof(struct entry *), 1, fd);
+       }
+       hdr.volno = checkpt;
+       hdr.maxino = maxino;
+       hdr.entrytblsize = entrytblsize;
+       hdr.stringsize = stroff;
+       hdr.dumptime = dumptime;
+       hdr.dumpdate = dumpdate;
+       hdr.ntrec = ntrec;
+       (void) fwrite((char *)&hdr, sizeof(struct symtableheader), 1, fd);
+       if (ferror(fd)) {
+               perror("fwrite");
+               panic("output error to file %s writing symbol table\n",
+                       filename);
+       }
+       (void) fclose(fd);
+}
+
+/*
+ * Initialize a symbol table from a file
+ */
+initsymtable(filename)
+       char *filename;
+{
+       char *base;
+       long tblsize;
+       register struct entry *ep;
+       struct entry *baseep, *lep;
+       struct symtableheader hdr;
+       struct stat stbuf;
+       register long i;
+       int fd;
+
+       vprintf(stdout, "Initialize symbol table.\n");
+       if (filename == NULL) {
+               entrytblsize = maxino / HASHFACTOR;
+               entry = (struct entry **)
+                       calloc((unsigned)entrytblsize, sizeof(struct entry *));
+               if (entry == (struct entry **)NIL)
+                       panic("no memory for entry table\n");
+               ep = addentry(".", ROOTINO, NODE);
+               ep->e_flags |= NEW;
+               return;
+       }
+       if ((fd = open(filename, 0)) < 0) {
+               perror("open");
+               panic("cannot open symbol table file %s\n", filename);
+       }
+       if (fstat(fd, &stbuf) < 0) {
+               perror("stat");
+               panic("cannot stat symbol table file %s\n", filename);
+       }
+       tblsize = stbuf.st_size - sizeof(struct symtableheader);
+       base = calloc(sizeof(char), (unsigned)tblsize);
+       if (base == NULL)
+               panic("cannot allocate space for symbol table\n");
+       if (read(fd, base, (int)tblsize) < 0 ||
+           read(fd, (char *)&hdr, sizeof(struct symtableheader)) < 0) {
+               perror("read");
+               panic("cannot read symbol table file %s\n", filename);
+       }
+       switch (command) {
+       case 'r':
+               /*
+                * For normal continuation, insure that we are using
+                * the next incremental tape
+                */
+               if (hdr.dumpdate != dumptime) {
+                       if (hdr.dumpdate < dumptime)
+                               fprintf(stderr, "Incremental tape too low\n");
+                       else
+                               fprintf(stderr, "Incremental tape too high\n");
+                       done(1);
+               }
+               break;
+       case 'R':
+               /*
+                * For restart, insure that we are using the same tape
+                */
+               curfile.action = SKIP;
+               dumptime = hdr.dumptime;
+               dumpdate = hdr.dumpdate;
+               if (!bflag)
+                       newtapebuf(hdr.ntrec);
+               getvol(hdr.volno);
+               break;
+       default:
+               panic("initsymtable called from command %c\n", command);
+               break;
+       }
+       maxino = hdr.maxino;
+       entrytblsize = hdr.entrytblsize;
+       entry = (struct entry **)
+               (base + tblsize - (entrytblsize * sizeof(struct entry *)));
+       baseep = (struct entry *)(base + hdr.stringsize - sizeof(struct entry));
+       lep = (struct entry *)entry;
+       for (i = 0; i < entrytblsize; i++) {
+               if (entry[i] == NIL)
+                       continue;
+               entry[i] = &baseep[(long)entry[i]];
+       }
+       for (ep = &baseep[1]; ep < lep; ep++) {
+               ep->e_name = base + (long)ep->e_name;
+               ep->e_parent = &baseep[(long)ep->e_parent];
+               if (ep->e_sibling != NIL)
+                       ep->e_sibling = &baseep[(long)ep->e_sibling];
+               if (ep->e_links != NIL)
+                       ep->e_links = &baseep[(long)ep->e_links];
+               if (ep->e_entries != NIL)
+                       ep->e_entries = &baseep[(long)ep->e_entries];
+               if (ep->e_next != NIL)
+                       ep->e_next = &baseep[(long)ep->e_next];
+       }
+}
diff --git a/usr/src/sbin/restore/tape.c b/usr/src/sbin/restore/tape.c
new file mode 100644 (file)
index 0000000..f8694e5
--- /dev/null
@@ -0,0 +1,1209 @@
+/*
+ * Copyright (c) 1983 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[] = "@(#)tape.c     5.21 (Berkeley) 2/22/91";
+#endif /* not lint */
+
+#include "restore.h"
+#include <protocols/dumprestore.h>
+#include <sys/ioctl.h>
+#include <sys/mtio.h>
+#include <sys/file.h>
+#include <setjmp.h>
+#include <sys/stat.h>
+#include "pathnames.h"
+
+static long    fssize = MAXBSIZE;
+static int     mt = -1;
+static int     pipein = 0;
+static char    magtape[BUFSIZ];
+static int     bct;
+static int     numtrec;
+static char    *tbf;
+static union   u_spcl endoftapemark;
+static long    blksread;
+static long    tapesread;
+static jmp_buf restart;
+static int     gettingfile = 0;        /* restart has a valid frame */
+
+static int     ofile;
+static char    *map;
+static char    lnkbuf[MAXPATHLEN + 1];
+static int     pathlen;
+
+int            Bcvt;           /* Swap Bytes (for CCI or sun) */
+static int     Qcvt;           /* Swap quads (for sun) */
+u_long         swabl();
+/*
+ * Set up an input source
+ */
+setinput(source)
+       char *source;
+{
+       extern int errno;
+#ifdef RRESTORE
+       char *host, *tape;
+#endif RRESTORE
+       char *strerror();
+
+       flsht();
+       if (bflag)
+               newtapebuf(ntrec);
+       else
+               newtapebuf(NTREC > HIGHDENSITYTREC ? NTREC : HIGHDENSITYTREC);
+       terminal = stdin;
+#ifdef RRESTORE
+       host = source;
+       tape = index(host, ':');
+       if (tape == 0) {
+nohost:
+               msg("need keyletter ``f'' and device ``host:tape''\n");
+               done(1);
+       }
+       *tape++ = '\0';
+       (void) strcpy(magtape, tape);
+       if (rmthost(host) == 0)
+               done(1);
+       setuid(getuid());       /* no longer need or want root privileges */
+#else
+       if (strcmp(source, "-") == 0) {
+               /*
+                * Since input is coming from a pipe we must establish
+                * our own connection to the terminal.
+                */
+               terminal = fopen(_PATH_TTY, "r");
+               if (terminal == NULL) {
+                       (void)fprintf(stderr, "Cannot open %s: %s\n",
+                           _PATH_TTY, strerror(errno));
+                       terminal = fopen(_PATH_DEVNULL, "r");
+                       if (terminal == NULL) {
+                           (void)fprintf(stderr, "Cannot open %s: %s\n",
+                               _PATH_DEVNULL, strerror(errno));
+                               done(1);
+                       }
+               }
+               pipein++;
+       }
+       (void) strcpy(magtape, source);
+#endif RRESTORE
+}
+
+newtapebuf(size)
+       long size;
+{
+       static tbfsize = -1;
+
+       ntrec = size;
+       if (size <= tbfsize)
+               return;
+       if (tbf != NULL)
+               free(tbf);
+       tbf = (char *)malloc(size * TP_BSIZE);
+       if (tbf == NULL) {
+               fprintf(stderr, "Cannot allocate space for tape buffer\n");
+               done(1);
+       }
+       tbfsize = size;
+}
+
+/*
+ * Verify that the tape drive can be accessed and
+ * that it actually is a dump tape.
+ */
+setup()
+{
+       int i, j, *ip;
+       struct stat stbuf;
+       extern int xtrmap(), xtrmapskip();
+
+       vprintf(stdout, "Verify tape and initialize maps\n");
+#ifdef RRESTORE
+       if ((mt = rmtopen(magtape, 0)) < 0)
+#else
+       if (pipein)
+               mt = 0;
+       else if ((mt = open(magtape, 0)) < 0)
+#endif
+       {
+               perror(magtape);
+               done(1);
+       }
+       volno = 1;
+       setdumpnum();
+       flsht();
+       if (!pipein && !bflag)
+               findtapeblksize();
+       if (gethead(&spcl) == FAIL) {
+               bct--; /* push back this block */
+               cvtflag++;
+               if (gethead(&spcl) == FAIL) {
+                       fprintf(stderr, "Tape is not a dump tape\n");
+                       done(1);
+               }
+               fprintf(stderr, "Converting to new file system format.\n");
+       }
+       if (pipein) {
+               endoftapemark.s_spcl.c_magic = cvtflag ? OFS_MAGIC : NFS_MAGIC;
+               endoftapemark.s_spcl.c_type = TS_END;
+               ip = (int *)&endoftapemark;
+               j = sizeof(union u_spcl) / sizeof(int);
+               i = 0;
+               do
+                       i += *ip++;
+               while (--j);
+               endoftapemark.s_spcl.c_checksum = CHECKSUM - i;
+       }
+       if (vflag || command == 't')
+               printdumpinfo();
+       dumptime = spcl.c_ddate;
+       dumpdate = spcl.c_date;
+       if (stat(".", &stbuf) < 0) {
+               perror("cannot stat .");
+               done(1);
+       }
+       if (stbuf.st_blksize > 0 && stbuf.st_blksize <= MAXBSIZE)
+               fssize = stbuf.st_blksize;
+       if (((fssize - 1) & fssize) != 0) {
+               fprintf(stderr, "bad block size %d\n", fssize);
+               done(1);
+       }
+       if (checkvol(&spcl, (long)1) == FAIL) {
+               fprintf(stderr, "Tape is not volume 1 of the dump\n");
+               done(1);
+       }
+       if (readhdr(&spcl) == FAIL)
+               panic("no header after volume mark!\n");
+       findinode(&spcl);
+       if (checktype(&spcl, TS_CLRI) == FAIL) {
+               fprintf(stderr, "Cannot find file removal list\n");
+               done(1);
+       }
+       maxino = (spcl.c_count * TP_BSIZE * NBBY) + 1;
+       dprintf(stdout, "maxino = %d\n", maxino);
+       map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY));
+       if (map == (char *)NIL)
+               panic("no memory for file removal list\n");
+       clrimap = map;
+       curfile.action = USING;
+       getfile(xtrmap, xtrmapskip);
+       if (checktype(&spcl, TS_BITS) == FAIL) {
+               fprintf(stderr, "Cannot find file dump list\n");
+               done(1);
+       }
+       map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY));
+       if (map == (char *)NULL)
+               panic("no memory for file dump list\n");
+       dumpmap = map;
+       curfile.action = USING;
+       getfile(xtrmap, xtrmapskip);
+}
+
+/*
+ * Prompt user to load a new dump volume.
+ * "Nextvol" is the next suggested volume to use.
+ * This suggested volume is enforced when doing full
+ * or incremental restores, but can be overrridden by
+ * the user when only extracting a subset of the files.
+ */
+getvol(nextvol)
+       long nextvol;
+{
+       long newvol;
+       long savecnt, i;
+       union u_spcl tmpspcl;
+#      define tmpbuf tmpspcl.s_spcl
+       char buf[TP_BSIZE];
+       extern char *ctime();
+
+       if (nextvol == 1) {
+               tapesread = 0;
+               gettingfile = 0;
+       }
+       if (pipein) {
+               if (nextvol != 1)
+                       panic("Changing volumes on pipe input?\n");
+               if (volno == 1)
+                       return;
+               goto gethdr;
+       }
+       savecnt = blksread;
+again:
+       if (pipein)
+               done(1); /* pipes do not get a second chance */
+       if (command == 'R' || command == 'r' || curfile.action != SKIP)
+               newvol = nextvol;
+       else 
+               newvol = 0;
+       while (newvol <= 0) {
+               if (tapesread == 0) {
+                       fprintf(stderr, "%s%s%s%s%s",
+                           "You have not read any tapes yet.\n",
+                           "Unless you know which volume your",
+                           " file(s) are on you should start\n",
+                           "with the last volume and work",
+                           " towards towards the first.\n");
+               } else {
+                       fprintf(stderr, "You have read volumes");
+                       strcpy(tbf, ": ");
+                       for (i = 1; i < 32; i++)
+                               if (tapesread & (1 << i)) {
+                                       fprintf(stderr, "%s%d", tbf, i);
+                                       strcpy(tbf, ", ");
+                               }
+                       fprintf(stderr, "\n");
+               }
+               do      {
+                       fprintf(stderr, "Specify next volume #: ");
+                       (void) fflush(stderr);
+                       (void) fgets(tbf, BUFSIZ, terminal);
+               } while (!feof(terminal) && tbf[0] == '\n');
+               if (feof(terminal))
+                       done(1);
+               newvol = atoi(tbf);
+               if (newvol <= 0) {
+                       fprintf(stderr,
+                           "Volume numbers are positive numerics\n");
+               }
+       }
+       if (newvol == volno) {
+               tapesread |= 1 << volno;
+               return;
+       }
+       closemt();
+       fprintf(stderr, "Mount tape volume %d\n", newvol);
+       fprintf(stderr, "Enter ``none'' if there are no more tapes\n");
+       fprintf(stderr, "otherwise enter tape name (default: %s) ", magtape);
+       (void) fflush(stderr);
+       (void) fgets(tbf, BUFSIZ, terminal);
+       if (feof(terminal))
+               done(1);
+       if (!strcmp(tbf, "none\n")) {
+               curfile.name = "<name unknown>";
+               curfile.action = UNKNOWN;
+               curfile.dip = (struct dinode *)NIL;
+               curfile.ino = maxino;
+               if (gettingfile) {
+                       gettingfile = 0;
+                       longjmp(restart, 1);
+               }
+       }
+       if (tbf[0] != '\n') {
+               (void) strcpy(magtape, tbf);
+               magtape[strlen(magtape) - 1] = '\0';
+       }
+#ifdef RRESTORE
+       if ((mt = rmtopen(magtape, 0)) == -1)
+#else
+       if ((mt = open(magtape, 0)) == -1)
+#endif
+       {
+               fprintf(stderr, "Cannot open %s\n", magtape);
+               volno = -1;
+               goto again;
+       }
+gethdr:
+       volno = newvol;
+       setdumpnum();
+       flsht();
+       if (readhdr(&tmpbuf) == FAIL) {
+               fprintf(stderr, "tape is not dump tape\n");
+               volno = 0;
+               goto again;
+       }
+       if (checkvol(&tmpbuf, volno) == FAIL) {
+               fprintf(stderr, "Wrong volume (%d)\n", tmpbuf.c_volume);
+               volno = 0;
+               goto again;
+       }
+       if (tmpbuf.c_date != dumpdate || tmpbuf.c_ddate != dumptime) {
+               fprintf(stderr, "Wrong dump date\n\tgot: %s",
+                       ctime(&tmpbuf.c_date));
+               fprintf(stderr, "\twanted: %s", ctime(&dumpdate));
+               volno = 0;
+               goto again;
+       }
+       tapesread |= 1 << volno;
+       blksread = savecnt;
+       if (curfile.action == USING) {
+               if (volno == 1)
+                       panic("active file into volume 1\n");
+               return;
+       }
+       /*
+        * Skip up to the beginning of the next record
+        */
+       if (tmpbuf.c_type == TS_TAPE && (tmpbuf.c_flags & DR_NEWHEADER))
+               for (i = tmpbuf.c_count; i > 0; i--)
+                       readtape(buf);
+       (void) gethead(&spcl);
+       findinode(&spcl);
+       if (gettingfile) {
+               gettingfile = 0;
+               longjmp(restart, 1);
+       }
+}
+
+/*
+ * handle multiple dumps per tape by skipping forward to the
+ * appropriate one.
+ */
+setdumpnum()
+{
+       struct mtop tcom;
+
+       if (dumpnum == 1 || volno != 1)
+               return;
+       if (pipein) {
+               fprintf(stderr, "Cannot have multiple dumps on pipe input\n");
+               done(1);
+       }
+       tcom.mt_op = MTFSF;
+       tcom.mt_count = dumpnum - 1;
+#ifdef RRESTORE
+       rmtioctl(MTFSF, dumpnum - 1);
+#else
+       if (ioctl(mt, (int)MTIOCTOP, (char *)&tcom) < 0)
+               perror("ioctl MTFSF");
+#endif
+}
+
+printdumpinfo()
+{
+       extern char *ctime();
+
+       fprintf(stdout, "Dump   date: %s", ctime(&spcl.c_date));
+       fprintf(stdout, "Dumped from: %s",
+           (spcl.c_ddate == 0) ? "the epoch\n" : ctime(&spcl.c_ddate));
+       if (spcl.c_host[0] == '\0')
+               return;
+       fprintf(stderr, "Level %d dump of %s on %s:%s\n",
+               spcl.c_level, spcl.c_filesys, spcl.c_host, spcl.c_dev);
+       fprintf(stderr, "Label: %s\n", spcl.c_label);
+}
+
+extractfile(name)
+       char *name;
+{
+       int mode;
+       struct timeval timep[2];
+       struct entry *ep;
+       extern int xtrlnkfile(), xtrlnkskip();
+       extern int xtrfile(), xtrskip();
+
+       curfile.name = name;
+       curfile.action = USING;
+       timep[0].tv_sec = curfile.dip->di_atime;
+       timep[0].tv_usec = 0;
+       timep[1].tv_sec = curfile.dip->di_mtime;
+       timep[1].tv_usec = 0;
+       mode = curfile.dip->di_mode;
+       switch (mode & IFMT) {
+
+       default:
+               fprintf(stderr, "%s: unknown file mode 0%o\n", name, mode);
+               skipfile();
+               return (FAIL);
+
+       case IFSOCK:
+               vprintf(stdout, "skipped socket %s\n", name);
+               skipfile();
+               return (GOOD);
+
+       case IFDIR:
+               if (mflag) {
+                       ep = lookupname(name);
+                       if (ep == NIL || ep->e_flags & EXTRACT)
+                               panic("unextracted directory %s\n", name);
+                       skipfile();
+                       return (GOOD);
+               }
+               vprintf(stdout, "extract file %s\n", name);
+               return (genliteraldir(name, curfile.ino));
+
+       case IFLNK:
+               lnkbuf[0] = '\0';
+               pathlen = 0;
+               getfile(xtrlnkfile, xtrlnkskip);
+               if (pathlen == 0) {
+                       vprintf(stdout,
+                           "%s: zero length symbolic link (ignored)\n", name);
+                       return (GOOD);
+               }
+               return (linkit(lnkbuf, name, SYMLINK));
+
+       case IFCHR:
+       case IFBLK:
+               vprintf(stdout, "extract special file %s\n", name);
+               if (Nflag) {
+                       skipfile();
+                       return (GOOD);
+               }
+               if (mknod(name, mode, (int)curfile.dip->di_rdev) < 0) {
+                       fprintf(stderr, "%s: ", name);
+                       (void) fflush(stderr);
+                       perror("cannot create special file");
+                       skipfile();
+                       return (FAIL);
+               }
+               (void) chown(name, curfile.dip->di_uid, curfile.dip->di_gid);
+               (void) chmod(name, mode);
+               skipfile();
+               utimes(name, timep);
+               return (GOOD);
+
+       case IFREG:
+               vprintf(stdout, "extract file %s\n", name);
+               if (Nflag) {
+                       skipfile();
+                       return (GOOD);
+               }
+               if ((ofile = creat(name, 0666)) < 0) {
+                       fprintf(stderr, "%s: ", name);
+                       (void) fflush(stderr);
+                       perror("cannot create file");
+                       skipfile();
+                       return (FAIL);
+               }
+               (void) fchown(ofile, curfile.dip->di_uid, curfile.dip->di_gid);
+               (void) fchmod(ofile, mode);
+               getfile(xtrfile, xtrskip);
+               (void) close(ofile);
+               utimes(name, timep);
+               return (GOOD);
+       }
+       /* NOTREACHED */
+}
+
+/*
+ * skip over bit maps on the tape
+ */
+skipmaps()
+{
+
+       while (checktype(&spcl, TS_CLRI) == GOOD ||
+              checktype(&spcl, TS_BITS) == GOOD)
+               skipfile();
+}
+
+/*
+ * skip over a file on the tape
+ */
+skipfile()
+{
+       extern int null();
+
+       curfile.action = SKIP;
+       getfile(null, null);
+}
+
+/*
+ * Do the file extraction, calling the supplied functions
+ * with the blocks
+ */
+getfile(f1, f2)
+       int     (*f2)(), (*f1)();
+{
+       register int i;
+       int curblk = 0;
+       off_t size = spcl.c_dinode.di_size;
+       static char clearedbuf[MAXBSIZE];
+       char buf[MAXBSIZE / TP_BSIZE][TP_BSIZE];
+       char junk[TP_BSIZE];
+
+       if (checktype(&spcl, TS_END) == GOOD)
+               panic("ran off end of tape\n");
+       if (ishead(&spcl) == FAIL)
+               panic("not at beginning of a file\n");
+       if (!gettingfile && setjmp(restart) != 0)
+               return;
+       gettingfile++;
+loop:
+       for (i = 0; i < spcl.c_count; i++) {
+               if (spcl.c_addr[i]) {
+                       readtape(&buf[curblk++][0]);
+                       if (curblk == fssize / TP_BSIZE) {
+                               (*f1)(buf, size > TP_BSIZE ?
+                                    (long) (fssize) :
+                                    (curblk - 1) * TP_BSIZE + size);
+                               curblk = 0;
+                       }
+               } else {
+                       if (curblk > 0) {
+                               (*f1)(buf, size > TP_BSIZE ?
+                                    (long) (curblk * TP_BSIZE) :
+                                    (curblk - 1) * TP_BSIZE + size);
+                               curblk = 0;
+                       }
+                       (*f2)(clearedbuf, size > TP_BSIZE ?
+                               (long) TP_BSIZE : size);
+               }
+               if ((size -= TP_BSIZE) <= 0) {
+                       for (i++; i < spcl.c_count; i++)
+                               if (spcl.c_addr[i])
+                                       readtape(junk);
+                       break;
+               }
+       }
+       if (readhdr(&spcl) == GOOD && size > 0) {
+               if (checktype(&spcl, TS_ADDR) == GOOD)
+                       goto loop;
+               dprintf(stdout, "Missing address (header) block for %s\n",
+                       curfile.name);
+       }
+       if (curblk > 0)
+               (*f1)(buf, (curblk * TP_BSIZE) + size);
+       findinode(&spcl);
+       gettingfile = 0;
+}
+
+/*
+ * The next routines are called during file extraction to
+ * put the data into the right form and place.
+ */
+xtrfile(buf, size)
+       char    *buf;
+       long    size;
+{
+
+       if (Nflag)
+               return;
+       if (write(ofile, buf, (int) size) == -1) {
+               fprintf(stderr, "write error extracting inode %d, name %s\n",
+                       curfile.ino, curfile.name);
+               perror("write");
+               done(1);
+       }
+}
+
+xtrskip(buf, size)
+       char *buf;
+       long size;
+{
+
+#ifdef lint
+       buf = buf;
+#endif
+       if (lseek(ofile, size, 1) == (long)-1) {
+               fprintf(stderr, "seek error extracting inode %d, name %s\n",
+                       curfile.ino, curfile.name);
+               perror("lseek");
+               done(1);
+       }
+}
+
+xtrlnkfile(buf, size)
+       char    *buf;
+       long    size;
+{
+
+       pathlen += size;
+       if (pathlen > MAXPATHLEN) {
+               fprintf(stderr, "symbolic link name: %s->%s%s; too long %d\n",
+                   curfile.name, lnkbuf, buf, pathlen);
+               done(1);
+       }
+       (void) strcat(lnkbuf, buf);
+}
+
+xtrlnkskip(buf, size)
+       char *buf;
+       long size;
+{
+
+#ifdef lint
+       buf = buf, size = size;
+#endif
+       fprintf(stderr, "unallocated block in symbolic link %s\n",
+               curfile.name);
+       done(1);
+}
+
+xtrmap(buf, size)
+       char    *buf;
+       long    size;
+{
+
+       bcopy(buf, map, size);
+       map += size;
+}
+
+xtrmapskip(buf, size)
+       char *buf;
+       long size;
+{
+
+#ifdef lint
+       buf = buf;
+#endif
+       panic("hole in map\n");
+       map += size;
+}
+
+null() {;}
+
+/*
+ * Do the tape i/o, dealing with volume changes
+ * etc..
+ */
+readtape(b)
+       char *b;
+{
+       register long i;
+       long rd, newvol;
+       int cnt;
+
+top:
+       if (bct < numtrec) {
+               bcopy(&tbf[(bct++*TP_BSIZE)], b, (long)TP_BSIZE);
+               blksread++;
+               return;
+       }
+       for (i = 0; i < ntrec; i++)
+               ((struct s_spcl *)&tbf[i*TP_BSIZE])->c_magic = 0;
+       if (numtrec == 0)
+               numtrec = ntrec;
+       cnt = ntrec*TP_BSIZE;
+       rd = 0;
+getmore:
+#ifdef RRESTORE
+       i = rmtread(&tbf[rd], cnt);
+#else
+       i = read(mt, &tbf[rd], cnt);
+#endif
+       /*
+        * Check for mid-tape short read error.
+        * If found, return rest of buffer.
+        */
+       if (numtrec < ntrec && i != 0) {
+               numtrec = ntrec;
+               goto top;
+       }
+       /*
+        * Handle partial block read.
+        */
+       if (i > 0 && i != ntrec*TP_BSIZE) {
+               if (pipein) {
+                       rd += i;
+                       cnt -= i;
+                       if (cnt > 0)
+                               goto getmore;
+                       i = rd;
+               } else {
+                       if (i % TP_BSIZE != 0)
+                               panic("partial block read: %d should be %d\n",
+                                       i, ntrec * TP_BSIZE);
+                       numtrec = i / TP_BSIZE;
+               }
+       }
+       /*
+        * Handle read error.
+        */
+       if (i < 0) {
+               fprintf(stderr, "Tape read error while ");
+               switch (curfile.action) {
+               default:
+                       fprintf(stderr, "trying to set up tape\n");
+                       break;
+               case UNKNOWN:
+                       fprintf(stderr, "trying to resynchronize\n");
+                       break;
+               case USING:
+                       fprintf(stderr, "restoring %s\n", curfile.name);
+                       break;
+               case SKIP:
+                       fprintf(stderr, "skipping over inode %d\n",
+                               curfile.ino);
+                       break;
+               }
+               if (!yflag && !reply("continue"))
+                       done(1);
+               i = ntrec*TP_BSIZE;
+               bzero(tbf, i);
+#ifdef RRESTORE
+               if (rmtseek(i, 1) < 0)
+#else
+               if (lseek(mt, i, 1) == (long)-1)
+#endif
+               {
+                       perror("continuation failed");
+                       done(1);
+               }
+       }
+       /*
+        * Handle end of tape.
+        */
+       if (i == 0) {
+               if (!pipein) {
+                       newvol = volno + 1;
+                       volno = 0;
+                       numtrec = 0;
+                       getvol(newvol);
+                       readtape(b);
+                       return;
+               }
+               if (rd % TP_BSIZE != 0)
+                       panic("partial block read: %d should be %d\n",
+                               rd, ntrec * TP_BSIZE);
+               bcopy((char *)&endoftapemark, &tbf[rd], (long)TP_BSIZE);
+       }
+       bct = 0;
+       bcopy(&tbf[(bct++*TP_BSIZE)], b, (long)TP_BSIZE);
+       blksread++;
+}
+
+findtapeblksize()
+{
+       register long i;
+
+       for (i = 0; i < ntrec; i++)
+               ((struct s_spcl *)&tbf[i * TP_BSIZE])->c_magic = 0;
+       bct = 0;
+#ifdef RRESTORE
+       i = rmtread(tbf, ntrec * TP_BSIZE);
+#else
+       i = read(mt, tbf, ntrec * TP_BSIZE);
+#endif
+       if (i <= 0) {
+               perror("Tape read error");
+               done(1);
+       }
+       if (i % TP_BSIZE != 0) {
+               fprintf(stderr, "Tape block size (%d) %s (%d)\n",
+                       i, "is not a multiple of dump block size", TP_BSIZE);
+               done(1);
+       }
+       ntrec = i / TP_BSIZE;
+       numtrec = ntrec;
+       vprintf(stdout, "Tape block size is %d\n", ntrec);
+}
+
+flsht()
+{
+
+       bct = ntrec+1;
+}
+
+closemt()
+{
+       if (mt < 0)
+               return;
+#ifdef RRESTORE
+       rmtclose();
+#else
+       (void) close(mt);
+#endif
+}
+
+checkvol(b, t)
+       struct s_spcl *b;
+       long t;
+{
+
+       if (b->c_volume != t)
+               return(FAIL);
+       return(GOOD);
+}
+
+readhdr(b)
+       struct s_spcl *b;
+{
+
+       if (gethead(b) == FAIL) {
+               dprintf(stdout, "readhdr fails at %d blocks\n", blksread);
+               return(FAIL);
+       }
+       return(GOOD);
+}
+
+/*
+ * read the tape into buf, then return whether or
+ * or not it is a header block.
+ */
+gethead(buf)
+       struct s_spcl *buf;
+{
+       long i;
+       union u_ospcl {
+               char dummy[TP_BSIZE];
+               struct  s_ospcl {
+                       long    c_type;
+                       long    c_date;
+                       long    c_ddate;
+                       long    c_volume;
+                       long    c_tapea;
+                       u_short c_inumber;
+                       long    c_magic;
+                       long    c_checksum;
+                       struct odinode {
+                               unsigned short odi_mode;
+                               u_short odi_nlink;
+                               u_short odi_uid;
+                               u_short odi_gid;
+                               long    odi_size;
+                               long    odi_rdev;
+                               char    odi_addr[36];
+                               long    odi_atime;
+                               long    odi_mtime;
+                               long    odi_ctime;
+                       } c_dinode;
+                       long    c_count;
+                       char    c_addr[256];
+               } s_ospcl;
+       } u_ospcl;
+
+       if (!cvtflag) {
+               readtape((char *)buf);
+               if (buf->c_magic != NFS_MAGIC) {
+                       if (swabl(buf->c_magic) != NFS_MAGIC)
+                               return (FAIL);
+                       if (!Bcvt) {
+                               vprintf(stdout, "Note: Doing Byte swapping\n");
+                               Bcvt = 1;
+                       }
+               }
+               if (checksum((int *)buf) == FAIL)
+                       return (FAIL);
+               if (Bcvt)
+                       swabst("8l4s31l", (char *)buf);
+               goto good;
+       }
+       readtape((char *)(&u_ospcl.s_ospcl));
+       bzero((char *)buf, (long)TP_BSIZE);
+       buf->c_type = u_ospcl.s_ospcl.c_type;
+       buf->c_date = u_ospcl.s_ospcl.c_date;
+       buf->c_ddate = u_ospcl.s_ospcl.c_ddate;
+       buf->c_volume = u_ospcl.s_ospcl.c_volume;
+       buf->c_tapea = u_ospcl.s_ospcl.c_tapea;
+       buf->c_inumber = u_ospcl.s_ospcl.c_inumber;
+       buf->c_checksum = u_ospcl.s_ospcl.c_checksum;
+       buf->c_magic = u_ospcl.s_ospcl.c_magic;
+       buf->c_dinode.di_mode = u_ospcl.s_ospcl.c_dinode.odi_mode;
+       buf->c_dinode.di_nlink = u_ospcl.s_ospcl.c_dinode.odi_nlink;
+       buf->c_dinode.di_uid = u_ospcl.s_ospcl.c_dinode.odi_uid;
+       buf->c_dinode.di_gid = u_ospcl.s_ospcl.c_dinode.odi_gid;
+       buf->c_dinode.di_size = u_ospcl.s_ospcl.c_dinode.odi_size;
+       buf->c_dinode.di_rdev = u_ospcl.s_ospcl.c_dinode.odi_rdev;
+       buf->c_dinode.di_atime = u_ospcl.s_ospcl.c_dinode.odi_atime;
+       buf->c_dinode.di_mtime = u_ospcl.s_ospcl.c_dinode.odi_mtime;
+       buf->c_dinode.di_ctime = u_ospcl.s_ospcl.c_dinode.odi_ctime;
+       buf->c_count = u_ospcl.s_ospcl.c_count;
+       bcopy(u_ospcl.s_ospcl.c_addr, buf->c_addr, (long)256);
+       if (u_ospcl.s_ospcl.c_magic != OFS_MAGIC ||
+           checksum((int *)(&u_ospcl.s_ospcl)) == FAIL)
+               return(FAIL);
+       buf->c_magic = NFS_MAGIC;
+
+good:
+       if (buf->c_dinode.di_size == 0 &&
+           (buf->c_dinode.di_mode & IFMT) == IFDIR && Qcvt == 0) {
+               if (buf->c_dinode.di_qsize.val[0] ||
+                   buf->c_dinode.di_qsize.val[1]) {
+                       printf("Note: Doing Quad swapping\n");
+                       Qcvt = 1;
+               }
+       }
+       if (Qcvt) {
+               i = buf->c_dinode.di_qsize.val[1];
+               buf->c_dinode.di_qsize.val[1] = buf->c_dinode.di_qsize.val[0];
+               buf->c_dinode.di_qsize.val[0] = i;
+       }
+       switch (buf->c_type) {
+
+       case TS_CLRI:
+       case TS_BITS:
+               /*
+                * Have to patch up missing information in bit map headers
+                */
+               buf->c_inumber = 0;
+               buf->c_dinode.di_size = buf->c_count * TP_BSIZE;
+               for (i = 0; i < buf->c_count; i++)
+                       buf->c_addr[i]++;
+               break;
+
+       case TS_TAPE:
+       case TS_END:
+               buf->c_inumber = 0;
+               break;
+
+       case TS_INODE:
+       case TS_ADDR:
+               break;
+
+       default:
+               panic("gethead: unknown inode type %d\n", buf->c_type);
+               break;
+       }
+       if (dflag)
+               accthdr(buf);
+       return(GOOD);
+}
+
+/*
+ * Check that a header is where it belongs and predict the next header
+ */
+accthdr(header)
+       struct s_spcl *header;
+{
+       static ino_t previno = 0x7fffffff;
+       static int prevtype;
+       static long predict;
+       long blks, i;
+
+       if (header->c_type == TS_TAPE) {
+               fprintf(stderr, "Volume header\n");
+               previno = 0x7fffffff;
+               return;
+       }
+       if (previno == 0x7fffffff)
+               goto newcalc;
+       switch (prevtype) {
+       case TS_BITS:
+               fprintf(stderr, "Dump mask header");
+               break;
+       case TS_CLRI:
+               fprintf(stderr, "Remove mask header");
+               break;
+       case TS_INODE:
+               fprintf(stderr, "File header, ino %d", previno);
+               break;
+       case TS_ADDR:
+               fprintf(stderr, "File continuation header, ino %d", previno);
+               break;
+       case TS_END:
+               fprintf(stderr, "End of tape header");
+               break;
+       }
+       if (predict != blksread - 1)
+               fprintf(stderr, "; predicted %d blocks, got %d blocks",
+                       predict, blksread - 1);
+       fprintf(stderr, "\n");
+newcalc:
+       blks = 0;
+       if (header->c_type != TS_END)
+               for (i = 0; i < header->c_count; i++)
+                       if (header->c_addr[i] != 0)
+                               blks++;
+       predict = blks;
+       blksread = 0;
+       prevtype = header->c_type;
+       previno = header->c_inumber;
+}
+
+/*
+ * Find an inode header.
+ * Complain if had to skip, and complain is set.
+ */
+findinode(header)
+       struct s_spcl *header;
+{
+       static long skipcnt = 0;
+       long i;
+       char buf[TP_BSIZE];
+
+       curfile.name = "<name unknown>";
+       curfile.action = UNKNOWN;
+       curfile.dip = (struct dinode *)NIL;
+       curfile.ino = 0;
+       if (ishead(header) == FAIL) {
+               skipcnt++;
+               while (gethead(header) == FAIL || header->c_date != dumpdate)
+                       skipcnt++;
+       }
+       for (;;) {
+               if (checktype(header, TS_ADDR) == GOOD) {
+                       /*
+                        * Skip up to the beginning of the next record
+                        */
+                       for (i = 0; i < header->c_count; i++)
+                               if (header->c_addr[i])
+                                       readtape(buf);
+                       (void) gethead(header);
+                       continue;
+               }
+               if (checktype(header, TS_INODE) == GOOD) {
+                       curfile.dip = &header->c_dinode;
+                       curfile.ino = header->c_inumber;
+                       break;
+               }
+               if (checktype(header, TS_END) == GOOD) {
+                       curfile.ino = maxino;
+                       break;
+               }
+               if (checktype(header, TS_CLRI) == GOOD) {
+                       curfile.name = "<file removal list>";
+                       break;
+               }
+               if (checktype(header, TS_BITS) == GOOD) {
+                       curfile.name = "<file dump list>";
+                       break;
+               }
+               while (gethead(header) == FAIL)
+                       skipcnt++;
+       }
+       if (skipcnt > 0)
+               fprintf(stderr, "resync restore, skipped %d blocks\n", skipcnt);
+       skipcnt = 0;
+}
+
+/*
+ * return whether or not the buffer contains a header block
+ */
+ishead(buf)
+       struct s_spcl *buf;
+{
+
+       if (buf->c_magic != NFS_MAGIC)
+               return(FAIL);
+       return(GOOD);
+}
+
+checktype(b, t)
+       struct s_spcl *b;
+       int     t;
+{
+
+       if (b->c_type != t)
+               return(FAIL);
+       return(GOOD);
+}
+
+checksum(b)
+       register int *b;
+{
+       register int i, j;
+
+       j = sizeof(union u_spcl) / sizeof(int);
+       i = 0;
+       if(!Bcvt) {
+               do
+                       i += *b++;
+               while (--j);
+       } else {
+               /* What happens if we want to read restore tapes
+                       for a 16bit int machine??? */
+               do 
+                       i += swabl(*b++);
+               while (--j);
+       }
+                       
+       if (i != CHECKSUM) {
+               fprintf(stderr, "Checksum error %o, inode %d file %s\n", i,
+                       curfile.ino, curfile.name);
+               return(FAIL);
+       }
+       return(GOOD);
+}
+
+#ifdef RRESTORE
+/* VARARGS1 */
+msg(cp, a1, a2, a3)
+       char *cp;
+{
+
+       fprintf(stderr, cp, a1, a2, a3);
+}
+#endif RRESTORE
+
+u_char *
+swabshort(sp, n)
+       register u_char *sp;
+       register int n;
+{
+       char c;
+
+       while (--n >= 0) {
+               c = sp[0]; sp[0] = sp[1]; sp[1] = c;
+               sp += 2;
+       }
+       return (sp);
+}
+
+u_char *
+swablong(sp, n)
+       register u_char *sp;
+       register int n;
+{
+       char c;
+
+       while (--n >= 0) {
+               c = sp[0]; sp[0] = sp[3]; sp[3] = c;
+               c = sp[2]; sp[2] = sp[1]; sp[1] = c;
+               sp += 4;
+       }
+       return (sp);
+}
+
+swabst(cp, sp)
+       register u_char *cp, *sp;
+{
+       int n = 0;
+       u_char c;
+
+       while (*cp) {
+               switch (*cp) {
+               case '0': case '1': case '2': case '3': case '4':
+               case '5': case '6': case '7': case '8': case '9':
+                       n = (n * 10) + (*cp++ - '0');
+                       continue;
+               
+               case 's': case 'w': case 'h':
+                       if (n == 0)
+                               n = 1;
+                       sp = swabshort(sp, n);
+                       break;
+
+               case 'l':
+                       if (n == 0)
+                               n = 1;
+                       sp = swablong(sp, n);
+                       break;
+
+               default: /* Any other character, like 'b' counts as byte. */
+                       if (n == 0)
+                               n = 1;
+                       sp += n;
+                       break;
+               }
+               cp++;
+               n = 0;
+       }
+}
+
+u_long
+swabl(x)
+       u_long x;
+{
+       swabst("l", (char *)&x);
+       return (x);
+}
diff --git a/usr/src/sbin/restore/utilities.c b/usr/src/sbin/restore/utilities.c
new file mode 100644 (file)
index 0000000..3e344fa
--- /dev/null
@@ -0,0 +1,363 @@
+/*
+ * Copyright (c) 1983 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[] = "@(#)utilities.c        5.6 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+#include "restore.h"
+
+/*
+ * Insure that all the components of a pathname exist.
+ */
+pathcheck(name)
+       char *name;
+{
+       register char *cp;
+       struct entry *ep;
+       char *start;
+
+       start = index(name, '/');
+       if (start == 0)
+               return;
+       for (cp = start; *cp != '\0'; cp++) {
+               if (*cp != '/')
+                       continue;
+               *cp = '\0';
+               ep = lookupname(name);
+               if (ep == NIL) {
+                       ep = addentry(name, psearch(name), NODE);
+                       newnode(ep);
+               }
+               ep->e_flags |= NEW|KEEP;
+               *cp = '/';
+       }
+}
+
+/*
+ * Change a name to a unique temporary name.
+ */
+mktempname(ep)
+       register struct entry *ep;
+{
+       char oldname[MAXPATHLEN];
+
+       if (ep->e_flags & TMPNAME)
+               badentry(ep, "mktempname: called with TMPNAME");
+       ep->e_flags |= TMPNAME;
+       (void) strcpy(oldname, myname(ep));
+       freename(ep->e_name);
+       ep->e_name = savename(gentempname(ep));
+       ep->e_namlen = strlen(ep->e_name);
+       renameit(oldname, myname(ep));
+}
+
+/*
+ * Generate a temporary name for an entry.
+ */
+char *
+gentempname(ep)
+       struct entry *ep;
+{
+       static char name[MAXPATHLEN];
+       struct entry *np;
+       long i = 0;
+
+       for (np = lookupino(ep->e_ino); np != NIL && np != ep; np = np->e_links)
+               i++;
+       if (np == NIL)
+               badentry(ep, "not on ino list");
+       (void) sprintf(name, "%s%d%d", TMPHDR, i, ep->e_ino);
+       return (name);
+}
+
+/*
+ * Rename a file or directory.
+ */
+renameit(from, to)
+       char *from, *to;
+{
+       if (!Nflag && rename(from, to) < 0) {
+               fprintf(stderr, "Warning: cannot rename %s to %s", from, to);
+               (void) fflush(stderr);
+               perror("");
+               return;
+       }
+       vprintf(stdout, "rename %s to %s\n", from, to);
+}
+
+/*
+ * Create a new node (directory).
+ */
+newnode(np)
+       struct entry *np;
+{
+       char *cp;
+
+       if (np->e_type != NODE)
+               badentry(np, "newnode: not a node");
+       cp = myname(np);
+       if (!Nflag && mkdir(cp, 0777) < 0) {
+               np->e_flags |= EXISTED;
+               fprintf(stderr, "Warning: ");
+               (void) fflush(stderr);
+               perror(cp);
+               return;
+       }
+       vprintf(stdout, "Make node %s\n", cp);
+}
+
+/*
+ * Remove an old node (directory).
+ */
+removenode(ep)
+       register struct entry *ep;
+{
+       char *cp;
+
+       if (ep->e_type != NODE)
+               badentry(ep, "removenode: not a node");
+       if (ep->e_entries != NIL)
+               badentry(ep, "removenode: non-empty directory");
+       ep->e_flags |= REMOVED;
+       ep->e_flags &= ~TMPNAME;
+       cp = myname(ep);
+       if (!Nflag && rmdir(cp) < 0) {
+               fprintf(stderr, "Warning: ");
+               (void) fflush(stderr);
+               perror(cp);
+               return;
+       }
+       vprintf(stdout, "Remove node %s\n", cp);
+}
+
+/*
+ * Remove a leaf.
+ */
+removeleaf(ep)
+       register struct entry *ep;
+{
+       char *cp;
+
+       if (ep->e_type != LEAF)
+               badentry(ep, "removeleaf: not a leaf");
+       ep->e_flags |= REMOVED;
+       ep->e_flags &= ~TMPNAME;
+       cp = myname(ep);
+       if (!Nflag && unlink(cp) < 0) {
+               fprintf(stderr, "Warning: ");
+               (void) fflush(stderr);
+               perror(cp);
+               return;
+       }
+       vprintf(stdout, "Remove leaf %s\n", cp);
+}
+
+/*
+ * Create a link.
+ */
+linkit(existing, new, type)
+       char *existing, *new;
+       int type;
+{
+
+       if (type == SYMLINK) {
+               if (!Nflag && symlink(existing, new) < 0) {
+                       fprintf(stderr,
+                               "Warning: cannot create symbolic link %s->%s: ",
+                               new, existing);
+                       (void) fflush(stderr);
+                       perror("");
+                       return (FAIL);
+               }
+       } else if (type == HARDLINK) {
+               if (!Nflag && link(existing, new) < 0) {
+                       fprintf(stderr,
+                               "Warning: cannot create hard link %s->%s: ",
+                               new, existing);
+                       (void) fflush(stderr);
+                       perror("");
+                       return (FAIL);
+               }
+       } else {
+               panic("linkit: unknown type %d\n", type);
+               return (FAIL);
+       }
+       vprintf(stdout, "Create %s link %s->%s\n",
+               type == SYMLINK ? "symbolic" : "hard", new, existing);
+       return (GOOD);
+}
+
+/*
+ * find lowest number file (above "start") that needs to be extracted
+ */
+ino_t
+lowerbnd(start)
+       ino_t start;
+{
+       register struct entry *ep;
+
+       for ( ; start < maxino; start++) {
+               ep = lookupino(start);
+               if (ep == NIL || ep->e_type == NODE)
+                       continue;
+               if (ep->e_flags & (NEW|EXTRACT))
+                       return (start);
+       }
+       return (start);
+}
+
+/*
+ * find highest number file (below "start") that needs to be extracted
+ */
+ino_t
+upperbnd(start)
+       ino_t start;
+{
+       register struct entry *ep;
+
+       for ( ; start > ROOTINO; start--) {
+               ep = lookupino(start);
+               if (ep == NIL || ep->e_type == NODE)
+                       continue;
+               if (ep->e_flags & (NEW|EXTRACT))
+                       return (start);
+       }
+       return (start);
+}
+
+/*
+ * report on a badly formed entry
+ */
+badentry(ep, msg)
+       register struct entry *ep;
+       char *msg;
+{
+
+       fprintf(stderr, "bad entry: %s\n", msg);
+       fprintf(stderr, "name: %s\n", myname(ep));
+       fprintf(stderr, "parent name %s\n", myname(ep->e_parent));
+       if (ep->e_sibling != NIL)
+               fprintf(stderr, "sibling name: %s\n", myname(ep->e_sibling));
+       if (ep->e_entries != NIL)
+               fprintf(stderr, "next entry name: %s\n", myname(ep->e_entries));
+       if (ep->e_links != NIL)
+               fprintf(stderr, "next link name: %s\n", myname(ep->e_links));
+       if (ep->e_next != NIL)
+               fprintf(stderr, "next hashchain name: %s\n", myname(ep->e_next));
+       fprintf(stderr, "entry type: %s\n",
+               ep->e_type == NODE ? "NODE" : "LEAF");
+       fprintf(stderr, "inode number: %ld\n", ep->e_ino);
+       panic("flags: %s\n", flagvalues(ep));
+}
+
+/*
+ * Construct a string indicating the active flag bits of an entry.
+ */
+char *
+flagvalues(ep)
+       register struct entry *ep;
+{
+       static char flagbuf[BUFSIZ];
+
+       (void) strcpy(flagbuf, "|NIL");
+       flagbuf[0] = '\0';
+       if (ep->e_flags & REMOVED)
+               (void) strcat(flagbuf, "|REMOVED");
+       if (ep->e_flags & TMPNAME)
+               (void) strcat(flagbuf, "|TMPNAME");
+       if (ep->e_flags & EXTRACT)
+               (void) strcat(flagbuf, "|EXTRACT");
+       if (ep->e_flags & NEW)
+               (void) strcat(flagbuf, "|NEW");
+       if (ep->e_flags & KEEP)
+               (void) strcat(flagbuf, "|KEEP");
+       if (ep->e_flags & EXISTED)
+               (void) strcat(flagbuf, "|EXISTED");
+       return (&flagbuf[1]);
+}
+
+/*
+ * Check to see if a name is on a dump tape.
+ */
+ino_t
+dirlookup(name)
+       char *name;
+{
+       ino_t ino;
+
+       ino = psearch(name);
+       if (ino == 0 || BIT(ino, dumpmap) == 0)
+               fprintf(stderr, "%s is not on tape\n", name);
+       return (ino);
+}
+
+/*
+ * Elicit a reply.
+ */
+reply(question)
+       char *question;
+{
+       char c;
+
+       do      {
+               fprintf(stderr, "%s? [yn] ", question);
+               (void) fflush(stderr);
+               c = getc(terminal);
+               while (c != '\n' && getc(terminal) != '\n')
+                       if (feof(terminal))
+                               return (FAIL);
+       } while (c != 'y' && c != 'n');
+       if (c == 'y')
+               return (GOOD);
+       return (FAIL);
+}
+
+/*
+ * handle unexpected inconsistencies
+ */
+/* VARARGS1 */
+panic(msg, d1, d2)
+       char *msg;
+       long d1, d2;
+{
+
+       fprintf(stderr, msg, d1, d2);
+       if (yflag)
+               return;
+       if (reply("abort") == GOOD) {
+               if (reply("dump core") == GOOD)
+                       abort();
+               done(1);
+       }
+}