Index: head/sbin/restore/dirs.c =================================================================== --- head/sbin/restore/dirs.c (revision 37905) +++ head/sbin/restore/dirs.c (revision 37906) @@ -1,770 +1,768 @@ /* * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * 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. - * - * $Id: dirs.c,v 1.10 1997/09/18 14:04:49 phk Exp $ */ #ifndef lint +#if 0 static char sccsid[] = "@(#)dirs.c 8.7 (Berkeley) 5/1/95"; +#endif +static const char rcsid[] = + "$Id$"; #endif /* not lint */ #include #include #include -#include #include #include -#include #include +#include #include #include #include #include #include -#include - -#include #include "pathnames.h" #include "restore.h" #include "extern.h" /* * Symbol table of directories read from tape. */ #define HASHSIZE 1000 #define INOHASH(val) (val % HASHSIZE) struct inotab { struct inotab *t_next; ino_t t_ino; long t_seekpt; long t_size; }; static struct inotab *inotab[HASHSIZE]; /* * Information retained about directories. */ struct modeinfo { ino_t ino; struct timeval timep[2]; mode_t mode; uid_t uid; gid_t gid; int flags; }; /* * Definitions for library routines operating on directories. */ #undef DIRBLKSIZ #define DIRBLKSIZ 1024 struct rstdirdesc { int dd_fd; long dd_loc; long dd_size; char dd_buf[DIRBLKSIZ]; }; /* * Global variables for this file. */ static long seekpt; static FILE *df, *mf; static RST_DIR *dirp; static char dirfile[MAXPATHLEN] = "#"; /* No file */ static char modefile[MAXPATHLEN] = "#"; /* No file */ static char dot[2] = "."; /* So it can be modified */ /* * Format of old style directories. */ #define ODIRSIZ 14 struct odirect { u_short d_ino; char d_name[ODIRSIZ]; }; static struct inotab *allocinotab __P((ino_t, struct dinode *, long)); static void dcvt __P((struct odirect *, struct direct *)); static void flushent __P((void)); static struct inotab *inotablookup __P((ino_t)); static RST_DIR *opendirfile __P((const char *)); static void putdir __P((char *, long)); static void putent __P((struct direct *)); static void rst_seekdir __P((RST_DIR *, long, long)); static long rst_telldir __P((RST_DIR *)); static struct direct *searchdir __P((ino_t, char *)); /* * Extract directory contents, building up a directory structure * on disk for extraction by name. * If genmode is requested, save mode, owner, and times for all * directories on the tape. */ void extractdirs(genmode) int genmode; { register int i; register struct dinode *ip; struct inotab *itp; struct direct nulldir; int fd; vprintf(stdout, "Extract directories from tape\n"); (void) sprintf(dirfile, "%srstdir%d", _PATH_TMP, dumpdate); if (command != 'r' && command != 'R') { (void *) strcat(dirfile, "-XXXXXX"); fd = mkstemp(dirfile); } else fd = open(dirfile, O_RDWR|O_CREAT|O_EXCL, 0666); if (fd == -1 || (df = fdopen(fd, "w")) == NULL) { if (fd != -1) close(fd); warn("%s - cannot create directory temporary\nfopen", dirfile); done(1); } if (genmode != 0) { (void) sprintf(modefile, "%srstmode%d", _PATH_TMP, dumpdate); if (command != 'r' && command != 'R') { (void *) strcat(modefile, "-XXXXXX"); fd = mkstemp(modefile); } else fd = open(modefile, O_RDWR|O_CREAT|O_EXCL, 0666); if (fd == -1 || (mf = fdopen(fd, "w")) == NULL) { if (fd != -1) close(fd); warn("%s - cannot create modefile\nfopen", modefile); done(1); } } nulldir.d_ino = 0; nulldir.d_type = DT_DIR; nulldir.d_namlen = 1; (void) strcpy(nulldir.d_name, "/"); nulldir.d_reclen = DIRSIZ(0, &nulldir); for (;;) { curfile.name = ""; curfile.action = USING; ip = curfile.dip; if (ip == NULL || (ip->di_mode & IFMT) != IFDIR) { (void) fclose(df); dirp = opendirfile(dirfile); if (dirp == NULL) fprintf(stderr, "opendirfile: %s\n", strerror(errno)); if (mf != NULL) (void) fclose(mf); i = dirlookup(dot); if (i == 0) panic("Root directory is not on tape\n"); return; } itp = allocinotab(curfile.ino, ip, seekpt); getfile(putdir, xtrnull); putent(&nulldir); flushent(); itp->t_size = seekpt - itp->t_seekpt; } } /* * skip over all the directories on the tape */ void skipdirs() { while (curfile.dip && (curfile.dip->di_mode & IFMT) == IFDIR) { skipfile(); } } /* * Recursively find names and inumbers of all files in subtree * pname and pass them off to be processed. */ void treescan(pname, ino, todo) char *pname; ino_t ino; long (*todo) __P((char *, ino_t, int)); { register struct inotab *itp; register struct direct *dp; int namelen; long bpt; char locname[MAXPATHLEN + 1]; itp = inotablookup(ino); if (itp == NULL) { /* * Pname is name of a simple file or an unchanged directory. */ (void) (*todo)(pname, ino, LEAF); return; } /* * Pname is a dumped directory name. */ if ((*todo)(pname, ino, NODE) == FAIL) return; /* * begin search through the directory * skipping over "." and ".." */ (void) strncpy(locname, pname, sizeof(locname) - 1); locname[sizeof(locname) - 1] = '\0'; (void) strncat(locname, "/", sizeof(locname) - strlen(locname)); namelen = strlen(locname); rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); dp = rst_readdir(dirp); /* "." */ if (dp != NULL && strcmp(dp->d_name, ".") == 0) dp = rst_readdir(dirp); /* ".." */ else fprintf(stderr, "Warning: `.' missing from directory %s\n", pname); if (dp != NULL && strcmp(dp->d_name, "..") == 0) dp = rst_readdir(dirp); /* first real entry */ else fprintf(stderr, "Warning: `..' missing from directory %s\n", pname); bpt = rst_telldir(dirp); /* * a zero inode signals end of directory */ while (dp != NULL) { locname[namelen] = '\0'; if (namelen + dp->d_namlen >= sizeof(locname)) { fprintf(stderr, "%s%s: name exceeds %d char\n", locname, dp->d_name, sizeof(locname) - 1); } else { (void) strncat(locname, dp->d_name, (int)dp->d_namlen); treescan(locname, dp->d_ino, todo); rst_seekdir(dirp, bpt, itp->t_seekpt); } dp = rst_readdir(dirp); bpt = rst_telldir(dirp); } } /* * Lookup a pathname which is always assumed to start from the ROOTINO. */ struct direct * pathsearch(pathname) const char *pathname; { ino_t ino; struct direct *dp; char *path, *name, buffer[MAXPATHLEN]; strcpy(buffer, pathname); path = buffer; ino = ROOTINO; while (*path == '/') path++; dp = NULL; while ((name = strsep(&path, "/")) != NULL && *name != '\0') { if ((dp = searchdir(ino, name)) == NULL) return (NULL); ino = dp->d_ino; } return (dp); } /* * Lookup the requested name in directory inum. * Return its inode number if found, zero if it does not exist. */ static struct direct * searchdir(inum, name) ino_t inum; char *name; { register struct direct *dp; register struct inotab *itp; int len; itp = inotablookup(inum); if (itp == NULL) return (NULL); rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); len = strlen(name); do { dp = rst_readdir(dirp); if (dp == NULL) return (NULL); } while (dp->d_namlen != len || strncmp(dp->d_name, name, len) != 0); return (dp); } /* * Put the directory entries in the directory file */ static void putdir(buf, size) char *buf; long size; { struct direct cvtbuf; register struct odirect *odp; struct odirect *eodp; register struct direct *dp; long loc, i; if (cvtflag) { eodp = (struct odirect *)&buf[size]; for (odp = (struct odirect *)buf; odp < eodp; odp++) if (odp->d_ino != 0) { dcvt(odp, &cvtbuf); putent(&cvtbuf); } } else { for (loc = 0; loc < size; ) { dp = (struct direct *)(buf + loc); if (Bcvt) swabst((u_char *)"ls", (u_char *) dp); if (oldinofmt && dp->d_ino != 0) { # if BYTE_ORDER == BIG_ENDIAN if (Bcvt) dp->d_namlen = dp->d_type; # else if (!Bcvt) dp->d_namlen = dp->d_type; # endif dp->d_type = DT_UNKNOWN; } i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1)); if ((dp->d_reclen & 0x3) != 0 || dp->d_reclen > i || dp->d_reclen < DIRSIZ(0, dp) || dp->d_namlen > NAME_MAX) { vprintf(stdout, "Mangled directory: "); if ((dp->d_reclen & 0x3) != 0) vprintf(stdout, "reclen not multiple of 4 "); if (dp->d_reclen < DIRSIZ(0, dp)) vprintf(stdout, "reclen less than DIRSIZ (%d < %d) ", dp->d_reclen, DIRSIZ(0, dp)); if (dp->d_namlen > NAME_MAX) vprintf(stdout, "reclen name too big (%d > %d) ", dp->d_namlen, NAME_MAX); vprintf(stdout, "\n"); loc += i; continue; } loc += dp->d_reclen; if (dp->d_ino != 0) { putent(dp); } } } } /* * These variables are "local" to the following two functions. */ char dirbuf[DIRBLKSIZ]; long dirloc = 0; long prev = 0; /* * add a new directory entry to a file. */ static void putent(dp) struct direct *dp; { dp->d_reclen = DIRSIZ(0, dp); if (dirloc + dp->d_reclen > DIRBLKSIZ) { ((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev; (void) fwrite(dirbuf, 1, DIRBLKSIZ, df); dirloc = 0; } memmove(dirbuf + dirloc, dp, (long)dp->d_reclen); prev = dirloc; dirloc += dp->d_reclen; } /* * flush out a directory that is finished. */ static void flushent() { ((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev; (void) fwrite(dirbuf, (int)dirloc, 1, df); seekpt = ftell(df); dirloc = 0; } static void dcvt(odp, ndp) register struct odirect *odp; register struct direct *ndp; { memset(ndp, 0, (long)(sizeof *ndp)); ndp->d_ino = odp->d_ino; ndp->d_type = DT_UNKNOWN; (void) strncpy(ndp->d_name, odp->d_name, ODIRSIZ); ndp->d_namlen = strlen(ndp->d_name); ndp->d_reclen = DIRSIZ(0, ndp); } /* * Seek to an entry in a directory. * Only values returned by rst_telldir should be passed to rst_seekdir. * This routine handles many directories in a single file. * It takes the base of the directory in the file, plus * the desired seek offset into it. */ static void rst_seekdir(dirp, loc, base) register RST_DIR *dirp; long loc, base; { if (loc == rst_telldir(dirp)) return; loc -= base; if (loc < 0) fprintf(stderr, "bad seek pointer to rst_seekdir %ld\n", loc); (void) lseek(dirp->dd_fd, base + (loc & ~(DIRBLKSIZ - 1)), SEEK_SET); dirp->dd_loc = loc & (DIRBLKSIZ - 1); if (dirp->dd_loc != 0) dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ); } /* * get next entry in a directory. */ struct direct * rst_readdir(dirp) register RST_DIR *dirp; { register struct direct *dp; for (;;) { if (dirp->dd_loc == 0) { dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ); if (dirp->dd_size <= 0) { dprintf(stderr, "error reading directory\n"); return (NULL); } } if (dirp->dd_loc >= dirp->dd_size) { dirp->dd_loc = 0; continue; } dp = (struct direct *)(dirp->dd_buf + dirp->dd_loc); if (dp->d_reclen == 0 || dp->d_reclen > DIRBLKSIZ + 1 - dirp->dd_loc) { dprintf(stderr, "corrupted directory: bad reclen %d\n", dp->d_reclen); return (NULL); } dirp->dd_loc += dp->d_reclen; if (dp->d_ino == 0 && strcmp(dp->d_name, "/") == 0) return (NULL); if (dp->d_ino >= maxino) { dprintf(stderr, "corrupted directory: bad inum %d\n", dp->d_ino); continue; } return (dp); } } /* * Simulate the opening of a directory */ RST_DIR * rst_opendir(name) const char *name; { struct inotab *itp; RST_DIR *dirp; ino_t ino; if ((ino = dirlookup(name)) > 0 && (itp = inotablookup(ino)) != NULL) { dirp = opendirfile(dirfile); rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); return (dirp); } return (NULL); } /* * In our case, there is nothing to do when closing a directory. */ void rst_closedir(dirp) RST_DIR *dirp; { (void)close(dirp->dd_fd); free(dirp); return; } /* * Simulate finding the current offset in the directory. */ static long rst_telldir(dirp) RST_DIR *dirp; { return ((long)lseek(dirp->dd_fd, (off_t)0, SEEK_CUR) - dirp->dd_size + dirp->dd_loc); } /* * Open a directory file. */ static RST_DIR * opendirfile(name) const char *name; { register RST_DIR *dirp; register int fd; if ((fd = open(name, O_RDONLY)) == -1) return (NULL); if ((dirp = malloc(sizeof(RST_DIR))) == NULL) { (void)close(fd); return (NULL); } dirp->dd_fd = fd; dirp->dd_loc = 0; return (dirp); } /* * Set the mode, owner, and times for all new or changed directories */ void setdirmodes(flags) int flags; { FILE *mf; struct modeinfo node; struct entry *ep; char *cp; vprintf(stdout, "Set directory mode, owner, and times.\n"); if (command == 'r' || command == 'R') (void) sprintf(modefile, "%srstmode%d", _PATH_TMP, dumpdate); if (modefile[0] == '#') { panic("modefile not defined\n"); fprintf(stderr, "directory mode, owner, and times not set\n"); return; } mf = fopen(modefile, "r"); if (mf == NULL) { fprintf(stderr, "fopen: %s\n", strerror(errno)); fprintf(stderr, "cannot open mode file %s\n", modefile); fprintf(stderr, "directory mode, owner, and times not set\n"); return; } clearerr(mf); for (;;) { (void) fread((char *)&node, 1, sizeof(struct modeinfo), mf); if (feof(mf)) break; ep = lookupino(node.ino); if (command == 'i' || command == 'x') { if (ep == NULL) continue; if ((flags & FORCE) == 0 && ep->e_flags & EXISTED) { ep->e_flags &= ~NEW; continue; } if (node.ino == ROOTINO && reply("set owner/mode for '.'") == FAIL) continue; } if (ep == NULL) { panic("cannot find directory inode %d\n", node.ino); } else { cp = myname(ep); (void) chown(cp, node.uid, node.gid); (void) chmod(cp, node.mode); (void) chflags(cp, node.flags); utimes(cp, node.timep); ep->e_flags &= ~NEW; } } if (ferror(mf)) panic("error setting directory modes\n"); (void) fclose(mf); } /* * Generate a literal copy of a directory. */ int genliteraldir(name, ino) char *name; ino_t ino; { register struct inotab *itp; int ofile, dp, i, size; char buf[BUFSIZ]; itp = inotablookup(ino); if (itp == NULL) panic("Cannot find directory inode %d named %s\n", ino, name); if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) { fprintf(stderr, "%s: ", name); (void) fflush(stderr); fprintf(stderr, "cannot create file: %s\n", strerror(errno)); return (FAIL); } rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); dp = dup(dirp->dd_fd); for (i = itp->t_size; i > 0; i -= BUFSIZ) { size = i < BUFSIZ ? i : BUFSIZ; if (read(dp, buf, (int) size) == -1) { fprintf(stderr, "write error extracting inode %d, name %s\n", curfile.ino, curfile.name); fprintf(stderr, "read: %s\n", strerror(errno)); done(1); } if (!Nflag && write(ofile, buf, (int) size) == -1) { fprintf(stderr, "write error extracting inode %d, name %s\n", curfile.ino, curfile.name); fprintf(stderr, "write: %s\n", strerror(errno)); done(1); } } (void) close(dp); (void) close(ofile); return (GOOD); } /* * Determine the type of an inode */ int inodetype(ino) ino_t ino; { struct inotab *itp; itp = inotablookup(ino); if (itp == NULL) return (LEAF); return (NODE); } /* * Allocate and initialize a directory inode entry. * If requested, save its pertinent mode, owner, and time info. */ static struct inotab * allocinotab(ino, dip, seekpt) ino_t ino; struct dinode *dip; long seekpt; { register struct inotab *itp; struct modeinfo node; itp = calloc(1, sizeof(struct inotab)); if (itp == NULL) panic("no memory directory table\n"); itp->t_next = inotab[INOHASH(ino)]; inotab[INOHASH(ino)] = itp; itp->t_ino = ino; itp->t_seekpt = seekpt; if (mf == NULL) return (itp); node.ino = ino; node.timep[0].tv_sec = dip->di_atime; node.timep[0].tv_usec = dip->di_atimensec / 1000; node.timep[1].tv_sec = dip->di_mtime; node.timep[1].tv_usec = dip->di_mtimensec / 1000; node.mode = dip->di_mode; node.flags = dip->di_flags; node.uid = dip->di_uid; node.gid = dip->di_gid; (void) fwrite((char *)&node, 1, sizeof(struct modeinfo), mf); return (itp); } /* * Look up an inode in the table of directories */ static struct inotab * inotablookup(ino) ino_t ino; { register struct inotab *itp; for (itp = inotab[INOHASH(ino)]; itp != NULL; itp = itp->t_next) if (itp->t_ino == ino) return (itp); return (NULL); } /* * Clean up and exit */ void done(exitcode) int exitcode; { closemt(); if (modefile[0] != '#') (void) unlink(modefile); if (dirfile[0] != '#') (void) unlink(dirfile); exit(exitcode); } Index: head/sbin/restore/extern.h =================================================================== --- head/sbin/restore/extern.h (revision 37905) +++ head/sbin/restore/extern.h (revision 37906) @@ -1,108 +1,111 @@ /*- * Copyright (c) 1992, 1993 * 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. * * @(#)extern.h 8.2 (Berkeley) 1/7/94 + * $Id$ */ struct entry *addentry __P((char *, ino_t, int)); long addfile __P((char *, ino_t, int)); +int addwhiteout __P((char *)); void badentry __P((struct entry *, char *)); void canon __P((char *, char *, int)); void checkrestore __P((void)); void closemt __P((void)); void createfiles __P((void)); void createleaves __P((char *)); void createlinks __P((void)); long deletefile __P((char *, ino_t, int)); void deleteino __P((ino_t)); +void delwhiteout __P((struct entry *)); ino_t dirlookup __P((const char *)); void done __P((int)) __dead2; void dumpsymtable __P((char *, long)); void extractdirs __P((int)); int extractfile __P((char *)); void findunreflinks __P((void)); char *flagvalues __P((struct entry *)); void freeentry __P((struct entry *)); void freename __P((char *)); int genliteraldir __P((char *, ino_t)); char *gentempname __P((struct entry *)); void getfile __P((void (*)(char *, long), void (*)(char *, long))); void getvol __P((long)); void initsymtable __P((char *)); int inodetype __P((ino_t)); int linkit __P((char *, char *, int)); struct entry *lookupino __P((ino_t)); struct entry *lookupname __P((char *)); long listfile __P((char *, ino_t, int)); ino_t lowerbnd __P((ino_t)); void mktempname __P((struct entry *)); void moveentry __P((struct entry *, char *)); void msg __P((const char *, ...)); char *myname __P((struct entry *)); void newnode __P((struct entry *)); void newtapebuf __P((long)); long nodeupdates __P((char *, ino_t, int)); void onintr __P((int)); void panic __P((const char *, ...)); void pathcheck __P((char *)); struct direct *pathsearch __P((const char *)); void printdumpinfo __P((void)); void removeleaf __P((struct entry *)); void removenode __P((struct entry *)); void removeoldleaves __P((void)); void removeoldnodes __P((void)); void renameit __P((char *, char *)); int reply __P((char *)); RST_DIR *rst_opendir __P((const char *)); struct direct *rst_readdir __P((RST_DIR *)); void rst_closedir __P((RST_DIR *dirp)); void runcmdshell __P((void)); char *savename __P((char *)); void setdirmodes __P((int)); void setinput __P((char *)); void setup __P((void)); void skipdirs __P((void)); void skipfile __P((void)); void skipmaps __P((void)); void swabst __P((u_char *, u_char *)); void treescan __P((char *, ino_t, long (*)(char *, ino_t, int))); ino_t upperbnd __P((ino_t)); long verifyfile __P((char *, ino_t, int)); void xtrnull __P((char *, long)); /* From ../dump/dumprmt.c */ void rmtclose __P((void)); int rmthost __P((char *)); int rmtioctl __P((int, int)); int rmtopen __P((char *, int)); int rmtread __P((char *, int)); int rmtseek __P((int, int)); Index: head/sbin/restore/interactive.c =================================================================== --- head/sbin/restore/interactive.c (revision 37905) +++ head/sbin/restore/interactive.c (revision 37906) @@ -1,781 +1,783 @@ /* * Copyright (c) 1985, 1993 * 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 +#if 0 static char sccsid[] = "@(#)interactive.c 8.5 (Berkeley) 5/1/95"; +#endif +static const char rcsid[] = + "$Id$"; #endif /* not lint */ #include -#include #include #include #include -#include #include #include #include #include #include #include #include "restore.h" #include "extern.h" #define round(a, b) (((a) + (b) - 1) / (b) * (b)) /* * Things to handle interruptions. */ static int runshell; 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 len; /* name length */ char prefix; /* prefix character */ char postfix; /* postfix character */ }; struct arglist { int freeglob; /* glob structure needs to be freed */ int argcnt; /* next globbed argument to return */ glob_t glob; /* globbing information */ char *cmd; /* the current command */ }; static char *copynext __P((char *, char *)); static int fcmp __P((const void *, const void *)); static void formatf __P((struct afile *, int)); static void getcmd __P((char *, char *, char *, int, struct arglist *)); struct dirent *glob_readdir __P((RST_DIR *dirp)); static int glob_stat __P((const char *, struct stat *)); static void mkentry __P((char *, struct direct *, struct afile *)); static void printlist __P((char *, char *)); /* * Read and execute commands from the terminal. */ void runcmdshell() { register struct entry *np; ino_t ino; struct arglist arglist; char curdir[MAXPATHLEN]; char name[MAXPATHLEN]; char cmd[BUFSIZ]; arglist.freeglob = 0; arglist.argcnt = 0; arglist.glob.gl_flags = GLOB_ALTDIRFUNC; arglist.glob.gl_opendir = (void *)rst_opendir; arglist.glob.gl_readdir = (void *)glob_readdir; arglist.glob.gl_closedir = (void *)rst_closedir; arglist.glob.gl_lstat = glob_stat; arglist.glob.gl_stat = glob_stat; canon("/", curdir, sizeof(curdir)); loop: if (setjmp(reset) != 0) { if (arglist.freeglob != 0) { arglist.freeglob = 0; arglist.argcnt = 0; globfree(&arglist.glob); } nextarg = NULL; volno = 0; } runshell = 1; getcmd(curdir, cmd, name, sizeof(name), &arglist); 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 == NULL || (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(0); 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; printlist(name, 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(FORCE); 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. */ static void getcmd(curdir, cmd, name, size, ap) char *curdir, *cmd, *name; struct arglist *ap; int size; { 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->argcnt > 0) goto retnext; 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 is an absolute pathname, canonicalize it and return it. */ if (rawname[0] == '/') { canon(rawname, name, size); } 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, size); } if (glob(name, GLOB_ALTDIRFUNC, NULL, &ap->glob) < 0) fprintf(stderr, "%s: out of memory\n", ap->cmd); if (ap->glob.gl_pathc == 0) return; ap->freeglob = 1; ap->argcnt = ap->glob.gl_pathc; retnext: strcpy(name, ap->glob.gl_pathv[ap->glob.gl_pathc - ap->argcnt]); if (--ap->argcnt == 0) { ap->freeglob = 0; globfree(&ap->glob); } # undef rawname } /* * Strip off the next token of the input. */ static 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. + * remove any embedded "." and ".." components. */ void canon(rawname, canonname, len) char *rawname, *canonname; int len; { register char *cp, *np; if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0) (void) strcpy(canonname, ""); else if (rawname[0] == '/') (void) strcpy(canonname, "."); else (void) strcpy(canonname, "./"); if (strlen(canonname) + strlen(rawname) >= len) { - fprintf(stderr, "canonname: not enough bufferspace\n"); + fprintf(stderr, "canonname: not enough buffer space\n"); done(1); } (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; } } } /* * Do an "ls" style listing of a directory */ static void printlist(name, basename) char *name; char *basename; { register struct afile *fp, *list, *listp; register struct direct *dp; struct afile single; RST_DIR *dirp; int entries, len, namelen; char locname[MAXPATHLEN + 1]; dp = pathsearch(name); if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) || (!vflag && dp->d_ino == WINO)) return; if ((dirp = rst_opendir(name)) == NULL) { entries = 1; list = &single; mkentry(name, dp, list); len = strlen(basename) + 1; if (strlen(name) - len > single.len) { freename(single.fname); single.fname = savename(&name[len]); single.len = strlen(single.fname); } } else { entries = 0; - while (dp = rst_readdir(dirp)) + while ((dp = rst_readdir(dirp))) entries++; rst_closedir(dirp); list = (struct afile *)malloc(entries * sizeof(struct afile)); if (list == NULL) { fprintf(stderr, "ls: out of memory\n"); return; } if ((dirp = rst_opendir(name)) == NULL) panic("directory reopen failed\n"); fprintf(stderr, "%s:\n", name); entries = 0; listp = list; (void) strncpy(locname, name, MAXPATHLEN); (void) strncat(locname, "/", MAXPATHLEN); namelen = strlen(locname); - while (dp = rst_readdir(dirp)) { + while ((dp = rst_readdir(dirp))) { if (dp == NULL) break; if (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) continue; if (!vflag && (dp->d_ino == WINO || strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)) continue; locname[namelen] = '\0'; if (namelen + dp->d_namlen >= MAXPATHLEN) { fprintf(stderr, "%s%s: name exceeds %d char\n", locname, dp->d_name, MAXPATHLEN); } else { (void) strncat(locname, dp->d_name, (int)dp->d_namlen); mkentry(locname, dp, listp++); entries++; } } rst_closedir(dirp); if (entries == 0) { fprintf(stderr, "\n"); free(list); return; } qsort((char *)list, entries, sizeof(struct afile), fcmp); } formatf(list, entries); if (dirp != NULL) { for (fp = listp - 1; fp >= list; fp--) freename(fp->fname); fprintf(stderr, "\n"); free(list); } } /* * Read the contents of a directory. */ static void mkentry(name, dp, fp) char *name; struct direct *dp; register struct afile *fp; { char *cp; struct entry *np; fp->fnum = dp->d_ino; fp->fname = savename(dp->d_name); for (cp = fp->fname; *cp; cp++) if (!vflag && (*cp < ' ' || *cp >= 0177)) *cp = '?'; fp->len = cp - fp->fname; if (dflag && TSTINO(fp->fnum, dumpmap) == 0) fp->prefix = '^'; else if ((np = lookupname(name)) != NULL && (np->e_flags & NEW)) fp->prefix = '*'; else fp->prefix = ' '; switch(dp->d_type) { default: fprintf(stderr, "Warning: undefined file type %d\n", dp->d_type); /* fall through */ case DT_REG: fp->postfix = ' '; break; case DT_LNK: fp->postfix = '@'; break; case DT_FIFO: case DT_SOCK: fp->postfix = '='; break; case DT_CHR: case DT_BLK: fp->postfix = '#'; break; case DT_WHT: fp->postfix = '%'; break; case DT_UNKNOWN: case DT_DIR: if (inodetype(dp->d_ino) == NODE) fp->postfix = '/'; else fp->postfix = ' '; break; } return; } /* * Print out a pretty listing of a directory */ static void formatf(list, nentry) register struct afile *list; int nentry; { register struct afile *fp, *endlist; int width, bigino, haveprefix, havepostfix; int i, j, w, precision, columns, lines; width = 0; haveprefix = 0; havepostfix = 0; bigino = ROOTINO; endlist = &list[nentry]; for (fp = &list[0]; fp < endlist; fp++) { if (bigino < fp->fnum) bigino = fp->fnum; if (width < fp->len) width = fp->len; if (fp->prefix != ' ') haveprefix = 1; if (fp->postfix != ' ') havepostfix = 1; } if (haveprefix) width++; if (havepostfix) width++; if (vflag) { for (precision = 0, i = bigino; i > 0; i /= 10) precision++; width += precision + 1; } width++; columns = 81 / width; if (columns == 0) columns = 1; lines = (nentry + columns - 1) / columns; for (i = 0; i < lines; i++) { for (j = 0; j < columns; j++) { fp = &list[j * lines + i]; if (vflag) { fprintf(stderr, "%*d ", precision, fp->fnum); fp->len += precision + 1; } if (haveprefix) { putc(fp->prefix, stderr); fp->len++; } fprintf(stderr, "%s", fp->fname); if (havepostfix) { putc(fp->postfix, stderr); fp->len++; } if (fp + lines >= endlist) { fprintf(stderr, "\n"); break; } for (w = fp->len; w < width; w++) putc(' ', stderr); } } } /* * Skip over directory entries that are not on the tape * * First have to get definition of a dirent. */ #undef DIRBLKSIZ #include #undef d_ino struct dirent * glob_readdir(dirp) RST_DIR *dirp; { struct direct *dp; static struct dirent adirent; while ((dp = rst_readdir(dirp)) != NULL) { if (!vflag && dp->d_ino == WINO) continue; if (dflag || TSTINO(dp->d_ino, dumpmap)) break; } if (dp == NULL) return (NULL); adirent.d_fileno = dp->d_ino; adirent.d_namlen = dp->d_namlen; memmove(adirent.d_name, dp->d_name, dp->d_namlen + 1); return (&adirent); } /* * Return st_mode information in response to stat or lstat calls */ static int glob_stat(name, stp) const char *name; struct stat *stp; { register struct direct *dp; dp = pathsearch(name); if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) || (!vflag && dp->d_ino == WINO)) return (-1); if (inodetype(dp->d_ino) == NODE) stp->st_mode = IFDIR; else stp->st_mode = IFREG; return (0); } /* * Comparison routine for qsort. */ static int fcmp(f1, f2) register const void *f1, *f2; { return (strcmp(((struct afile *)f1)->fname, ((struct afile *)f2)->fname)); } /* * respond to interrupts */ void onintr(signo) int signo; { if (command == 'i' && runshell) longjmp(reset, 1); if (reply("restore interrupted, continue") == FAIL) done(1); } Index: head/sbin/restore/main.c =================================================================== --- head/sbin/restore/main.c (revision 37905) +++ head/sbin/restore/main.c (revision 37906) @@ -1,372 +1,372 @@ /* * Copyright (c) 1983, 1993 * 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 copyright[] = +static const char copyright[] = "@(#) Copyright (c) 1983, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint +#if 0 static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 5/4/95"; +#endif +static const char rcsid[] = + "$Id$"; #endif /* not lint */ #include -#include +#include #include -#include #include #include -#include -#include #include #include -#include #include #include "pathnames.h" #include "restore.h" #include "extern.h" int bflag = 0, cvtflag = 0, dflag = 0, vflag = 0, yflag = 0; int hflag = 1, mflag = 1, Nflag = 0; int uflag = 0; int dokerberos = 0; char command = '\0'; long dumpnum = 1; long volno = 0; long ntrec; char *dumpmap; char *usedinomap; ino_t maxino; time_t dumptime; time_t dumpdate; FILE *terminal; static void obsolete __P((int *, char **[])); static void usage __P((void)); int main(argc, argv) int argc; char *argv[]; { int ch; ino_t ino; char *inputdev; char *symtbl = "./restoresymtable"; char *p, name[MAXPATHLEN]; /* Temp files should *not* be readable. We set permissions later. */ (void) umask(077); if (argc < 2) usage(); if ((inputdev = getenv("TAPE")) == NULL) inputdev = _PATH_DEFTAPE; obsolete(&argc, &argv); #ifdef KERBEROS #define optlist "b:cdf:hikmNRrs:tuvxy" #else #define optlist "b:cdf:himNRrs:tuvxy" #endif while ((ch = getopt(argc, argv, optlist)) != -1) switch(ch) { case 'b': /* Change default tape blocksize. */ bflag = 1; ntrec = strtol(optarg, &p, 10); if (*p) errx(1, "illegal blocksize -- %s", optarg); if (ntrec <= 0) errx(1, "block size must be greater than 0"); break; case 'c': cvtflag = 1; break; case 'd': dflag = 1; break; case 'f': inputdev = optarg; break; case 'h': hflag = 0; break; #ifdef KERBEROS case 'k': dokerberos = 1; break; #endif case 'i': case 'R': case 'r': case 't': case 'x': if (command != '\0') errx(1, "%c and %c options are mutually exclusive", ch, command); command = ch; break; case 'm': mflag = 0; break; case 'N': Nflag = 1; break; case 's': /* Dumpnum (skip to) for multifile dump tapes. */ dumpnum = strtol(optarg, &p, 10); if (*p) errx(1, "illegal dump number -- %s", optarg); if (dumpnum <= 0) errx(1, "dump number must be greater than 0"); break; case 'u': uflag = 1; break; case 'v': vflag = 1; break; case 'y': yflag = 1; break; default: usage(); } argc -= optind; argv += optind; if (command == '\0') errx(1, "none of i, R, r, t or x options specified"); if (signal(SIGINT, onintr) == SIG_IGN) (void) signal(SIGINT, SIG_IGN); if (signal(SIGTERM, onintr) == SIG_IGN) (void) signal(SIGTERM, SIG_IGN); setlinebuf(stderr); setinput(inputdev); if (argc == 0) { argc = 1; *--argv = "."; } switch (command) { /* * Interactive mode. */ case 'i': setup(); extractdirs(1); initsymtable(NULL); runcmdshell(); break; /* * 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(FORCE); checkrestore(); if (dflag) { vprintf(stdout, "Verify the directory structure\n"); treescan(".", ROOTINO, verifyfile); } dumpsymtable(symtbl, (long)1); break; /* * Resume an incremental file system restoration. */ case 'R': initsymtable(symtbl); skipmaps(); skipdirs(); createleaves(symtbl); createlinks(); setdirmodes(FORCE); checkrestore(); dumpsymtable(symtbl, (long)1); break; /* * List contents of tape. */ case 't': setup(); extractdirs(0); initsymtable((char *)0); while (argc--) { canon(*argv++, name, sizeof(name)); ino = dirlookup(name); if (ino == 0) continue; treescan(name, ino, listfile); } break; /* * Batch extraction of tape contents. */ case 'x': setup(); extractdirs(1); initsymtable((char *)0); while (argc--) { canon(*argv++, name, sizeof(name)); ino = dirlookup(name); if (ino == 0) continue; if (mflag) pathcheck(name); treescan(name, ino, addfile); } createfiles(); createlinks(); setdirmodes(0); if (dflag) checkrestore(); break; } done(0); /* NOTREACHED */ } static void usage() { (void)fprintf(stderr, "usage:\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n", "restore -i [-chkmuvy] [-b blocksize] [-f file] [-s fileno]", "restore -r [-ckuvy] [-b blocksize] [-f file] [-s fileno]", "restore -R [-ckuvy] [-b blocksize] [-f file] [-s fileno]", "restore -x [-chkmuvy] [-b blocksize] [-f file] [-s fileno] [file ...]", "restore -t [-chkuvy] [-b blocksize] [-f file] [-s fileno] [file ...]"); done(1); } /* * obsolete -- * Change set of key letters and ordered arguments into something * getopt(3) will like. */ static void obsolete(argcp, argvp) int *argcp; char **argvp[]; { int argc, flags; char *ap, **argv, *flagsp, **nargv, *p; /* Setup. */ argv = *argvp; argc = *argcp; /* Return if no arguments or first argument has leading dash. */ ap = argv[1]; if (argc == 1 || *ap == '-') return; /* Allocate space for new arguments. */ if ((*argvp = nargv = malloc((argc + 1) * sizeof(char *))) == NULL || (p = flagsp = malloc(strlen(ap) + 2)) == NULL) err(1, NULL); *nargv++ = *argv; argv += 2, argc -= 2; for (flags = 0; *ap; ++ap) { switch (*ap) { case 'b': case 'f': case 's': if (*argv == NULL) { warnx("option requires an argument -- %c", *ap); usage(); } if ((nargv[0] = malloc(strlen(*argv) + 2 + 1)) == NULL) err(1, NULL); nargv[0][0] = '-'; nargv[0][1] = *ap; (void)strcpy(&nargv[0][2], *argv); ++argv; ++nargv; break; default: if (!flags) { *p++ = '-'; flags = 1; } *p++ = *ap; break; } } /* Terminate flags. */ if (flags) { *p = '\0'; *nargv++ = flagsp; } /* Copy remaining arguments. */ - while (*nargv++ = *argv++); + while ((*nargv++ = *argv++)); /* Update argument count. */ *argcp = nargv - *argvp - 1; } Index: head/sbin/restore/restore.8 =================================================================== --- head/sbin/restore/restore.8 (revision 37905) +++ head/sbin/restore/restore.8 (revision 37906) @@ -1,445 +1,445 @@ .\" Copyright (c) 1985, 1991, 1993 .\" 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.8 8.4 (Berkeley) 5/1/95 -.\" $Id: restore.8,v 1.11 1997/04/29 17:48:58 wollman Exp $ +.\" $Id: restore.8,v 1.12 1998/05/09 05:22:56 jkh Exp $ .\" .Dd May 1, 1995 .Dt RESTORE 8 .Os BSD 4 .Sh NAME .Nm restore .Nd "restore files or file systems from backups made with dump" .Sh SYNOPSIS .Nm restore .Fl i .Op Fl chkmuvy .Op Fl b Ar blocksize .Op Fl f Ar file .Op Fl s Ar fileno .Nm restore .Fl R .Op Fl ckuvy .Op Fl b Ar blocksize .Op Fl f Ar file .Op Fl s Ar fileno .Nm restore .Fl r .Op Fl ckuvy .Op Fl b Ar blocksize .Op Fl f Ar file .Op Fl s Ar fileno .Nm restore .Fl t .Op Fl chkuvy .Op Fl b Ar blocksize .Op Fl f Ar file .Op Fl s Ar fileno .Op file ... .Nm restore .Fl x .Op Fl chkmuvy .Op Fl b Ar blocksize .Op Fl f Ar file .Op Fl s Ar fileno .Op file ... .Pp .in -\\n(iSu (The .Bx 4.3 option syntax is implemented for backward compatibility, but is not documented here.) .Sh DESCRIPTION The .Nm restore command performs the inverse function of .Xr dump 8 . A full backup of a file system may be restored and subsequent incremental backups layered on top of it. Single files and directory subtrees may be restored from full or partial backups. .Nm Restore works across a network; to do this see the .Fl f flag described below. Other arguments to the command are file or directory names specifying the files that are to be restored. Unless the .Fl h flag is specified (see below), the appearance of a directory name refers to the files and (recursively) subdirectories of that directory. .Pp Exactly one of the following flags is required: .Bl -tag -width Ds .It Fl i This mode allows interactive restoration of files from a dump. After reading in the directory information from the dump, .Nm restore provides a shell like interface that allows the user to move around the directory tree selecting files to be extracted. The available commands are given below; for those commands that require an argument, the default is the current directory. .Bl -tag -width Fl .It Ic add Op Ar arg The current directory or specified argument is added to the list of files to be extracted. If a directory is specified, then it and all its descendents are added to the extraction list (unless the .Fl h flag is specified on the command line). Files that are on the extraction list are prepended with a ``*'' when they are listed by .Ic ls . .It Ic \&cd Ar arg Change the current working directory to the specified argument. .It Ic delete Op Ar arg The current directory or specified argument is deleted from the list of files to be extracted. If a directory is specified, then it and all its descendents are deleted from the extraction list (unless the .Fl h flag is specified on the command line). The most expedient way to extract most of the files from a directory is to add the directory to the extraction list and then delete those files that are not needed. .It Ic extract All the files that are on the extraction list are extracted from the dump. .Nm Restore will ask which volume the user wishes to mount. The fastest way to extract a few files is to start with the last volume, and work towards the first volume. .It Ic help List a summary of the available commands. .It Ic \&ls Op Ar arg List the current or specified directory. Entries that are directories are appended with a ``/''. Entries that have been marked for extraction are prepended with a ``*''. If the verbose flag is set the inode number of each entry is also listed. .It Ic pwd Print the full pathname of the current working directory. .It Ic quit Restore immediately exits, even if the extraction list is not empty. .It Ic setmodes All the directories that have been added to the extraction list have their owner, modes, and times set; nothing is extracted from the dump. This is useful for cleaning up after a restore has been prematurely aborted. .It Ic verbose The sense of the .Fl v flag is toggled. When set, the verbose flag causes the .Ic ls command to list the inode numbers of all entries. It also causes .Nm restore to print out information about each file as it is extracted. .El .It Fl R .Nm Restore requests a particular tape of a multi volume set on which to restart a full restore (see the .Fl r flag below). This is useful if the restore has been interrupted. .It Fl r Restore (rebuild a file system). The target file system should be made pristine with .Xr newfs 8 , mounted and the user .Xr cd Ns 'd into the pristine file system before starting the restoration of the initial level 0 backup. If the level 0 restores successfully, the .Fl r flag may be used to restore any necessary incremental backups on top of the level 0. The .Fl r flag precludes an interactive file extraction and can be detrimental to one's health if not used carefully (not to mention the disk). An example: .Bd -literal -offset indent newfs /dev/rrp0g eagle mount /dev/rp0g /mnt cd /mnt restore rf /dev/rst8 .Ed .Pp Note that .Nm restore leaves a file .Pa restoresymtable in the root directory to pass information between incremental restore passes. This file should be removed when the last incremental has been restored. .Pp .Nm Restore , in conjunction with .Xr newfs 8 and .Xr dump 8 , may be used to modify file system parameters such as size or block size. .It Fl t The names of the specified files are listed if they occur on the backup. If no file argument is given, then the root directory is listed, which results in the entire content of the backup being listed, unless the .Fl h flag has been specified. Note that the .Fl t flag replaces the function of the old .Xr dumpdir 8 program. .ne 1i .It Fl x The named files are read from the given media. If a named file matches a directory whose contents are on the backup and the .Fl h flag is not specified, the directory is recursively extracted. The owner, modification time, and mode are restored (if possible). If no file argument is given, then the root directory is extracted, which results in the entire content of the backup being extracted, unless the .Fl h flag has been specified. .El .Pp The following additional options may be specified: .Bl -tag -width Ds .It Fl b Ar blocksize The number of kilobytes per dump record. If the .Fl b option is not specified, .Nm restore tries to determine the media block size dynamically. .It Fl c Normally, .Nm restore will try to determine dynamically whether the dump was made from an -old (pre-4.4) or new format file sytem. The +old (pre-4.4) or new format file system. The .Fl c flag disables this check, and only allows reading a dump in the old format. .It Fl f Ar file Read the backup from .Ar file ; .Ar file may be a special device file like .Pa /dev/rmt12 (a tape drive), .Pa /dev/rsd1c (a disk drive), an ordinary file, or .Ql Fl (the standard input). If the name of the file is of the form .Dq host:file , or .Dq user@host:file , .Nm restore reads from the named file on the remote host using .Xr rmt 8 . .Pp .It Fl k Use Kerberos authentication when contacting the remote tape server. (Only available if this options was enabled when .Nm restore was compiled.) .Pp .It Fl h Extract the actual directory, rather than the files that it references. This prevents hierarchical restoration of complete subtrees from the dump. .It Fl m Extract by inode numbers rather than by file name. This is useful if only a few files are being extracted, and one wants to avoid regenerating the complete pathname to the file. .It Fl s Ar fileno Read from the specified .Ar fileno on a multi-file tape. File numbering starts at 1. .It Fl u When creating certain types of files, restore may generate a warning diagnostic if they already exist in the target directory. To prevent this, the .Fl u (unlink) flag causes restore to remove old entries before attempting to create new ones. .It Fl v Normally .Nm restore does its work silently. The .Fl v (verbose) flag causes it to type the name of each file it treats preceded by its file type. .It Fl y Do not ask the user whether to abort the restore in the event of an error. Always try to skip over the bad block(s) and continue. .El .Sh DIAGNOSTICS Complaints if it gets a read error. If .Fl y has been specified, or the user responds .Ql y , .Nm restore will attempt to continue the restore. .Pp If a backup was made using more than one tape volume, .Nm restore will notify the user when it is time to mount the next volume. If the .Fl x or .Fl i flag has been specified, .Nm restore will also ask which volume the user wishes to mount. The fastest way to extract a few files is to start with the last volume, and work towards the first volume. .Pp There are numerous consistency checks that can be listed by .Nm restore . Most checks are self-explanatory or can ``never happen''. Common errors are given below. .Pp .Bl -tag -width Ds -compact .It Converting to new file system format. A dump tape created from the old file system has been loaded. It is automatically converted to the new file system format. .Pp .It : not found on tape The specified file name was listed in the tape directory, but was not found on the tape. This is caused by tape read errors while looking for the file, and from using a dump tape created on an active file system. .Pp .It expected next file , got A file that was not listed in the directory showed up. This can occur when using a dump created on an active file system. .Pp .It Incremental dump too low When doing incremental restore, a dump that was written before the previous incremental dump, or that has too low an incremental level has been loaded. .Pp .It Incremental dump too high When doing incremental restore, a dump that does not begin its coverage where the previous incremental dump left off, or that has too high an incremental level has been loaded. .Pp .It Tape read error while restoring .It Tape read error while skipping over inode .It Tape read error while trying to resynchronize A tape (or other media) read error has occurred. If a file name is specified, then its contents are probably partially wrong. If an inode is being skipped or the tape is trying to resynchronize, then no extracted files have been corrupted, though files may not be found on the tape. .Pp .It resync restore, skipped blocks After a dump read error, .Nm restore may have to resynchronize itself. This message lists the number of blocks that were skipped over. .El .Sh FILES .Bl -tag -width "./restoresymtable" -compact .It Pa /dev/rst0 the default tape drive .It Pa /tmp/rstdir* file containing directories on the tape. .It Pa /tmp/rstmode* owner, mode, and time stamps for directories. .It Pa \&./restoresymtable information passed between incremental restores. .El .Sh SEE ALSO .Xr dump 8 , .Xr ft 8 , .Xr mount 8 , .Xr newfs 8 , .Xr rmt 8 .Sh BUGS .Nm Restore can get confused when doing incremental restores from dumps that were made on active file systems. .Pp A level zero dump must be done after a full restore. Because restore runs in user code, it has no control over inode allocation; thus a full dump must be done to get a new set of directories reflecting the new inode numbering, even though the contents of the files is unchanged. .Pp To do a network restore, you have to run restore as root. This is due to the previous security history of dump and restore. (restore is written to be setuid root, but we are not certain all bugs are gone from the restore code - run setuid at your own risk.) .Sh HISTORY The .Nm restore command appeared in .Bx 4.2 . Index: head/sbin/restore/restore.c =================================================================== --- head/sbin/restore/restore.c (revision 37905) +++ head/sbin/restore/restore.c (revision 37906) @@ -1,855 +1,858 @@ /* * Copyright (c) 1983, 1993 * 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 +#if 0 static char sccsid[] = "@(#)restore.c 8.3 (Berkeley) 9/13/94"; +#endif +static const char rcsid[] = + "$Id$"; #endif /* not lint */ #include -#include #include #include #include #include "restore.h" #include "extern.h" static char *keyval __P((int)); /* * 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 (TSTINO(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 (TSTINO(ino, dumpmap) == 0) { dprintf(stdout, "%s: not on the tape\n", name); return (descend); } if (ino == WINO && command == 'i' && !vflag) 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 != NULL) { 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 (TSTINO(ino, dumpmap) == 0) return (descend); ep = lookupname(name); if (ep != NULL) { ep->e_flags &= ~NEW; ep->e_flags |= REMOVED; if (ep->e_type != NODE) freeentry(ep); } 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 invalid whiteouts from the old tree. * Remove unneeded leaves from the old tree. * Remove directories from the lookup chains. */ void removeoldleaves() { register struct entry *ep, *nextep; register ino_t i, mydirino; vprintf(stdout, "Mark entries to be removed.\n"); - if (ep = lookupino(WINO)) { + if ((ep = lookupino(WINO))) { vprintf(stdout, "Delete whiteouts\n"); for ( ; ep != NULL; ep = nextep) { nextep = ep->e_links; mydirino = ep->e_parent->e_ino; /* * We remove all whiteouts that are in directories * that have been removed or that have been dumped. */ if (TSTINO(mydirino, usedinomap) && !TSTINO(mydirino, dumpmap)) continue; delwhiteout(ep); freeentry(ep); } } for (i = ROOTINO + 1; i < maxino; i++) { ep = lookupino(i); if (ep == NULL) continue; if (TSTINO(i, usedinomap)) continue; for ( ; ep != NULL; 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 */ /* * 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 (TSTINO(ino, dumpmap)) key |= ONTAPE; /* * Check to see if the name exists, and if the name is a link. */ np = lookupname(name); if (np != NULL) { 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 != NULL) { key |= INOFND; for (ep = ip->e_links; ep != NULL; 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 = NULL; 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. If it is a link, * then all names referring to the previous file must be removed * so that the subset of them that remain can be recreated. */ case ONTAPE|INOFND|NAMEFND: if (lookuptype == LINK) { removeleaf(np); freeentry(np); ep = addentry(name, ino, type|LINK); if (type == NODE) newnode(ep); ep->e_flags |= NEW|KEEP; dprintf(stdout, "[%s] %s: %s|LINK\n", keyval(key), name, flagvalues(ep)); break; } 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 */ for (ip = lookupino(ino); ip != NULL; ip = ip->e_links) { if (ip->e_type != LEAF) badentry(ip, "NODE and LEAF links to same inode"); 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. + * A hard link to a directory 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 NULL: 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. */ static 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. */ void 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 == NULL || ep->e_type == LEAF || TSTINO(i, dumpmap) == 0) continue; for (np = ep->e_entries; np != NULL; 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 != NULL; ep = ep->e_next) { for (np = ep->e_entries; np != NULL; 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). */ void removeoldnodes() { register struct entry *ep, **prev; long change; vprintf(stdout, "Remove old nodes (directories).\n"); do { change = 0; prev = &removelist; for (ep = removelist; ep != NULL; ep = *prev) { if (ep->e_entries != NULL) { prev = &ep->e_next; continue; } *prev = ep->e_next; removenode(ep); freeentry(ep); change++; } } while (change); for (ep = removelist; ep != NULL; 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. */ void 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 == NULL) 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 == NULL) 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"). + * to another (e.g. "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 + * as to simplify the amount of work required 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. */ void 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 == NULL) 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 == NULL) panic("corrupted symbol table\n"); (void) extractfile(myname(ep)); ep->e_flags &= ~NEW; if (volno != curvol) skipmaps(); } } } /* * Add links. */ void createlinks() { register struct entry *np, *ep; register ino_t i; char name[BUFSIZ]; - if (ep = lookupino(WINO)) { + if ((ep = lookupino(WINO))) { vprintf(stdout, "Add whiteouts\n"); for ( ; ep != NULL; ep = ep->e_links) { if ((ep->e_flags & NEW) == 0) continue; (void) addwhiteout(myname(ep)); ep->e_flags &= ~NEW; } } vprintf(stdout, "Add links\n"); for (i = ROOTINO; i < maxino; i++) { ep = lookupino(i); if (ep == NULL) continue; for (np = ep->e_links; np != NULL; 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. */ void checkrestore() { register struct entry *ep; register ino_t i; vprintf(stdout, "Check the symbol table.\n"); for (i = WINO; i < maxino; i++) { for (ep = lookupino(i); ep != NULL; ep = ep->e_links) { ep->e_flags &= ~KEEP; if (ep->e_type == NODE) ep->e_flags &= ~(NEW|EXISTED); if (ep->e_flags != 0) 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 == NULL) { fprintf(stderr, "Warning: missing name %s\n", name); return (FAIL); } np = lookupino(ino); if (np != ep) descend = FAIL; for ( ; np != NULL; np = np->e_links) if (np == ep) break; if (np == NULL) panic("missing inumber %d\n", ino); if (ep->e_type == LEAF && type != LEAF) badentry(ep, "type should be LEAF"); return (descend); } Index: head/sbin/restore/symtab.c =================================================================== --- head/sbin/restore/symtab.c (revision 37905) +++ head/sbin/restore/symtab.c (revision 37906) @@ -1,631 +1,635 @@ /* * Copyright (c) 1983, 1993 * 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 +#if 0 static char sccsid[] = "@(#)symtab.c 8.3 (Berkeley) 4/28/95"; +#endif +static const char rcsid[] = + "$Id$"; #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 #include #include #include #include #include #include #include #include #include "restore.h" #include "extern.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; static void addino __P((ino_t, struct entry *)); static struct entry *lookupparent __P((char *)); static void removeentry __P((struct entry *)); /* * Look up an entry by inode number */ struct entry * lookupino(inum) ino_t inum; { register struct entry *ep; if (inum < WINO || inum >= maxino) return (NULL); for (ep = entry[inum % entrytblsize]; ep != NULL; ep = ep->e_next) if (ep->e_ino == inum) return (ep); return (NULL); } /* * Add an entry into the entry table */ static void addino(inum, np) ino_t inum; struct entry *np; { struct entry **epp; if (inum < WINO || 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 != NULL; np = np->e_next) if (np->e_ino == inum) badentry(np, "duplicate inum"); } /* * Delete an entry from the entry table */ void deleteino(inum) ino_t inum; { register struct entry *next; struct entry **prev; if (inum < WINO || inum >= maxino) panic("deleteino: out of range %d\n", inum); prev = &entry[inum % entrytblsize]; for (next = *prev; next != NULL; 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 != NULL; ep = ep->e_entries) { for (np = buf; *cp != '/' && *cp != '\0' && np < &buf[sizeof(buf)]; ) *np++ = *cp++; if (np == &buf[sizeof(buf)]) break; *np = '\0'; for ( ; ep != NULL; ep = ep->e_sibling) if (strcmp(ep->e_name, buf) == 0) break; if (ep == NULL) break; if (*cp++ == '\0') return (ep); } return (NULL); } /* * Look up the parent of a pathname */ static struct entry * lookupparent(name) char *name; { struct entry *ep; char *tailindex; tailindex = strrchr(name, '/'); if (tailindex == NULL) return (NULL); *tailindex = '\0'; ep = lookupname(name); *tailindex = '/'; if (ep == NULL) return (NULL); 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; memmove(cp, ep->e_name, (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 + * Unused symbol table entries are linked together on a free list * headed by the following pointer. */ static struct entry *freelist = NULL; /* * 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 != NULL) { np = freelist; freelist = np->e_next; memset(np, 0, (long)sizeof(struct entry)); } else { np = (struct entry *)calloc(1, sizeof(struct entry)); if (np == NULL) panic("no memory to extend symbol table\n"); } np->e_type = type & ~LINK; ep = lookupparent(name); if (ep == NULL) { if (inum != ROOTINO || lookupino(ROOTINO) != NULL) 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(strrchr(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 == NULL) - panic("link to non-existant name\n"); + panic("link to non-existent name\n"); np->e_ino = inum; np->e_links = ep->e_links; ep->e_links = np; } else if (inum != 0) { if (lookupino(inum) != NULL) panic("duplicate entry\n"); addino(inum, np); } return (np); } /* * delete an entry from the symbol table */ void 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 != NULL) badentry(ep, "freeing referenced directory"); if (ep->e_entries != NULL) badentry(ep, "freeing non-empty directory"); } if (ep->e_ino != 0) { np = lookupino(ep->e_ino); if (np == NULL) badentry(ep, "lookupino failed"); if (np == ep) { inum = ep->e_ino; deleteino(inum); if (ep->e_links != NULL) addino(inum, ep->e_links); } else { for (; np != NULL; np = np->e_links) { if (np->e_links == ep) { np->e_links = ep->e_links; break; } } if (np == NULL) badentry(ep, "link not found"); } } removeentry(ep); freename(ep->e_name); ep->e_next = freelist; freelist = ep; } /* * Relocate an entry in the tree structure */ void moveentry(ep, newname) register struct entry *ep; char *newname; { struct entry *np; char *cp; np = lookupparent(newname); if (np == NULL) 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 = strrchr(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 */ static void 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 != NULL; np = np->e_sibling) { if (np->e_sibling == ep) { np->e_sibling = ep->e_sibling; break; } } if (np == NULL) 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. + * appropriate 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(NAME_MAX) / 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. */ void 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 */ void 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) { fprintf(stderr, "fopen: %s\n", strerror(errno)); panic("cannot create save file %s for symbol table\n", filename); } clearerr(fd); /* - * Assign indicies to each entry + * Assign indices to each entry * Write out the string entries */ for (i = WINO; i <= maxino; i++) { for (ep = lookupino(i); ep != NULL; 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 = WINO; i <= maxino; i++) { for (ep = lookupino(i); ep != NULL; ep = ep->e_links) { memmove(tep, ep, (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 != NULL) tep->e_links = (struct entry *)ep->e_links->e_index; if (ep->e_sibling != NULL) tep->e_sibling = (struct entry *)ep->e_sibling->e_index; if (ep->e_entries != NULL) tep->e_entries = (struct entry *)ep->e_entries->e_index; if (ep->e_next != NULL) 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] == NULL) tentry = NULL; 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)) { fprintf(stderr, "fwrite: %s\n", strerror(errno)); panic("output error to file %s writing symbol table\n", filename); } (void) fclose(fd); } /* * Initialize a symbol table from a file */ void 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 **)NULL) panic("no memory for entry table\n"); ep = addentry(".", ROOTINO, NODE); ep->e_flags |= NEW; return; } if ((fd = open(filename, O_RDONLY, 0)) < 0) { fprintf(stderr, "open: %s\n", strerror(errno)); panic("cannot open symbol table file %s\n", filename); } if (fstat(fd, &stbuf) < 0) { fprintf(stderr, "stat: %s\n", strerror(errno)); 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) { fprintf(stderr, "read: %s\n", strerror(errno)); 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] == NULL) 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 != NULL) ep->e_sibling = &baseep[(long)ep->e_sibling]; if (ep->e_links != NULL) ep->e_links = &baseep[(long)ep->e_links]; if (ep->e_entries != NULL) ep->e_entries = &baseep[(long)ep->e_entries]; if (ep->e_next != NULL) ep->e_next = &baseep[(long)ep->e_next]; } } Index: head/sbin/restore/tape.c =================================================================== --- head/sbin/restore/tape.c (revision 37905) +++ head/sbin/restore/tape.c (revision 37906) @@ -1,1394 +1,1397 @@ /* * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * 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 +#if 0 static char sccsid[] = "@(#)tape.c 8.9 (Berkeley) 5/1/95"; +#endif +static const char rcsid[] = + "$Id$"; #endif /* not lint */ #include #include -#include #include #include #include #include #include #include #include #include #include #include #include "restore.h" #include "extern.h" #include "pathnames.h" static long fssize = MAXBSIZE; static int mt = -1; static int pipein = 0; static char *magtape; static int blkcnt; static int numtrec; static char *tapebuf; static union u_spcl endoftapemark; static long blksread; /* blocks read since last header */ static long tpblksread = 0; /* TP_BSIZE blocks read */ static long tapesread; static jmp_buf restart; static int gettingfile = 0; /* restart has a valid frame */ static char *host = NULL; static int ofile; static char *map; static char lnkbuf[MAXPATHLEN + 1]; static int pathlen; int oldinofmt; /* old inode format conversion required */ int Bcvt; /* Swap Bytes (for CCI or sun) */ static int Qcvt; /* Swap quads (for sun) */ #define FLUSHTAPEBUF() blkcnt = ntrec + 1 static void accthdr __P((struct s_spcl *)); static int checksum __P((int *)); static void findinode __P((struct s_spcl *)); static void findtapeblksize __P((void)); static int gethead __P((struct s_spcl *)); static void readtape __P((char *)); static void setdumpnum __P((void)); static u_long swabl __P((u_long)); static u_char *swablong __P((u_char *, int)); static u_char *swabshort __P((u_char *, int)); static void terminateinput __P((void)); static void xtrfile __P((char *, long)); static void xtrlnkfile __P((char *, long)); static void xtrlnkskip __P((char *, long)); static void xtrmap __P((char *, long)); static void xtrmapskip __P((char *, long)); static void xtrskip __P((char *, long)); /* * Set up an input source */ void setinput(source) char *source; { FLUSHTAPEBUF(); if (bflag) newtapebuf(ntrec); else newtapebuf(NTREC > HIGHDENSITYTREC ? NTREC : HIGHDENSITYTREC); terminal = stdin; #ifdef RRESTORE if (strchr(source, ':')) { host = source; source = strchr(host, ':'); *source++ = '\0'; if (rmthost(host) == 0) done(1); } else #endif 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++; } setuid(getuid()); /* no longer need or want root privileges */ magtape = strdup(source); if (magtape == NULL) { fprintf(stderr, "Cannot allocate space for magtape buffer\n"); done(1); } } void newtapebuf(size) long size; { static tapebufsize = -1; ntrec = size; if (size <= tapebufsize) return; if (tapebuf != NULL) free(tapebuf); tapebuf = malloc(size * TP_BSIZE); if (tapebuf == NULL) { fprintf(stderr, "Cannot allocate space for tape buffer\n"); done(1); } tapebufsize = size; } /* * Verify that the tape drive can be accessed and * that it actually is a dump tape. */ void setup() { int i, j, *ip; struct stat stbuf; vprintf(stdout, "Verify tape and initialize maps\n"); #ifdef RRESTORE if (host) mt = rmtopen(magtape, 0); else #endif if (pipein) mt = 0; else mt = open(magtape, O_RDONLY, 0); if (mt < 0) { fprintf(stderr, "%s: %s\n", magtape, strerror(errno)); done(1); } volno = 1; setdumpnum(); FLUSHTAPEBUF(); if (!pipein && !bflag) findtapeblksize(); if (gethead(&spcl) == FAIL) { blkcnt--; /* push back this block */ blksread--; tpblksread--; 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) { fprintf(stderr, "cannot stat .: %s\n", strerror(errno)); done(1); } if (stbuf.st_blksize > 0 && stbuf.st_blksize < TP_BSIZE ) fssize = TP_BSIZE; if (stbuf.st_blksize >= TP_BSIZE && stbuf.st_blksize <= MAXBSIZE) fssize = stbuf.st_blksize; if (((fssize - 1) & fssize) != 0) { fprintf(stderr, "bad block size %ld\n", fssize); done(1); } if (spcl.c_volume != 1) { fprintf(stderr, "Tape is not volume 1 of the dump\n"); done(1); } if (gethead(&spcl) == FAIL) { dprintf(stdout, "header read failed at %ld blocks\n", blksread); panic("no header after volume mark!\n"); } findinode(&spcl); if (spcl.c_type != TS_CLRI) { 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 == NULL) panic("no memory for active inode map\n"); usedinomap = map; curfile.action = USING; getfile(xtrmap, xtrmapskip); if (spcl.c_type != TS_BITS) { 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); /* * If there may be whiteout entries on the tape, pretend that the * whiteout inode exists, so that the whiteout entries can be * extracted. */ if (oldinofmt == 0) SETINO(WINO, dumpmap); } /* * 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 + * or incremental restores, but can be overridden by * the user when only extracting a subset of the files. */ void getvol(nextvol) long nextvol; { long newvol, savecnt, wantnext, i; union u_spcl tmpspcl; # define tmpbuf tmpspcl.s_spcl char buf[TP_BSIZE]; 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; wantnext = 1; } else { newvol = 0; wantnext = 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 the first.\n"); } else { fprintf(stderr, "You have read volumes"); strcpy(buf, ": "); for (i = 1; i < 32; i++) if (tapesread & (1 << i)) { fprintf(stderr, "%s%ld", buf, i); strcpy(buf, ", "); } fprintf(stderr, "\n"); } do { fprintf(stderr, "Specify next volume #: "); (void) fflush(stderr); (void) fgets(buf, BUFSIZ, terminal); } while (!feof(terminal) && buf[0] == '\n'); if (feof(terminal)) done(1); newvol = atoi(buf); if (newvol <= 0) { fprintf(stderr, "Volume numbers are positive numerics\n"); } } if (newvol == volno) { tapesread |= 1 << volno; return; } closemt(); fprintf(stderr, "Mount tape volume %ld\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(buf, BUFSIZ, terminal); if (feof(terminal)) done(1); if (!strcmp(buf, "none\n")) { terminateinput(); return; } if (buf[0] != '\n') { (void) strcpy(magtape, buf); magtape[strlen(magtape) - 1] = '\0'; } #ifdef RRESTORE if (host) mt = rmtopen(magtape, 0); else #endif mt = open(magtape, O_RDONLY, 0); if (mt == -1) { fprintf(stderr, "Cannot open %s\n", magtape); volno = -1; goto again; } gethdr: volno = newvol; setdumpnum(); FLUSHTAPEBUF(); if (gethead(&tmpbuf) == FAIL) { dprintf(stdout, "header read failed at %ld blocks\n", blksread); fprintf(stderr, "tape is not dump tape\n"); volno = 0; goto again; } if (tmpbuf.c_volume != volno) { fprintf(stderr, "Wrong volume (%ld)\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 continuing from the previous volume, skip over any * blocks read already at the end of the previous volume. * * If coming to this volume at random, skip to the beginning * of the next record. */ dprintf(stdout, "read %ld recs, tape starts with %ld\n", tpblksread, tmpbuf.c_firstrec); if (tmpbuf.c_type == TS_TAPE && (tmpbuf.c_flags & DR_NEWHEADER)) { if (!wantnext) { tpblksread = tmpbuf.c_firstrec; for (i = tmpbuf.c_count; i > 0; i--) readtape(buf); } else if (tmpbuf.c_firstrec > 0 && tmpbuf.c_firstrec < tpblksread - 1) { /* * -1 since we've read the volume header */ i = tpblksread - tmpbuf.c_firstrec - 1; dprintf(stderr, "Skipping %ld duplicate record%s.\n", i, i > 1 ? "s" : ""); while (--i >= 0) readtape(buf); } } 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 unexpected EOF. */ static void terminateinput() { if (gettingfile && curfile.action == USING) { printf("Warning: %s %s\n", "End-of-input encountered while extracting", curfile.name); } curfile.name = ""; curfile.action = UNKNOWN; curfile.dip = NULL; curfile.ino = maxino; if (gettingfile) { gettingfile = 0; longjmp(restart, 1); } } /* * handle multiple dumps per tape by skipping forward to the * appropriate one. */ static void 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 if (host) rmtioctl(MTFSF, dumpnum - 1); else #endif if (ioctl(mt, (int)MTIOCTOP, (char *)&tcom) < 0) fprintf(stderr, "ioctl MTFSF: %s\n", strerror(errno)); } void printdumpinfo() { 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 %ld 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); } int extractfile(name) char *name; { int flags; mode_t mode; struct timeval timep[2]; struct entry *ep; curfile.name = name; curfile.action = USING; timep[0].tv_sec = curfile.dip->di_atime; timep[0].tv_usec = curfile.dip->di_atimensec / 1000; timep[1].tv_sec = curfile.dip->di_mtime; timep[1].tv_usec = curfile.dip->di_mtimensec / 1000; mode = curfile.dip->di_mode; flags = curfile.dip->di_flags; 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 == NULL || 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 IFIFO: vprintf(stdout, "extract fifo %s\n", name); if (Nflag) { skipfile(); return (GOOD); } if (uflag && !Nflag) (void)unlink(name); if (mkfifo(name, mode) < 0) { fprintf(stderr, "%s: cannot create fifo: %s\n", name, strerror(errno)); skipfile(); return (FAIL); } (void) chown(name, curfile.dip->di_uid, curfile.dip->di_gid); (void) chmod(name, mode); (void) chflags(name, flags); skipfile(); utimes(name, timep); return (GOOD); case IFCHR: case IFBLK: vprintf(stdout, "extract special file %s\n", name); if (Nflag) { skipfile(); return (GOOD); } if (uflag) (void)unlink(name); if (mknod(name, mode, (int)curfile.dip->di_rdev) < 0) { fprintf(stderr, "%s: cannot create special file: %s\n", name, strerror(errno)); skipfile(); return (FAIL); } (void) chown(name, curfile.dip->di_uid, curfile.dip->di_gid); (void) chmod(name, mode); (void) chflags(name, flags); skipfile(); utimes(name, timep); return (GOOD); case IFREG: vprintf(stdout, "extract file %s\n", name); if (Nflag) { skipfile(); return (GOOD); } if (uflag) (void)unlink(name); if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) { fprintf(stderr, "%s: cannot create file: %s\n", name, strerror(errno)); skipfile(); return (FAIL); } (void) fchown(ofile, curfile.dip->di_uid, curfile.dip->di_gid); (void) fchmod(ofile, mode); (void) fchflags(ofile, flags); getfile(xtrfile, xtrskip); (void) close(ofile); utimes(name, timep); return (GOOD); } /* NOTREACHED */ } /* * skip over bit maps on the tape */ void skipmaps() { while (spcl.c_type == TS_BITS || spcl.c_type == TS_CLRI) skipfile(); } /* * skip over a file on the tape */ void skipfile() { curfile.action = SKIP; getfile(xtrnull, xtrnull); } /* * Extract a file from the tape. * When an allocated block is found it is passed to the fill function; * when an unallocated block (hole) is found, a zeroed buffer is passed * to the skip function. */ void getfile(fill, skip) void (*fill) __P((char *, long)); void (*skip) __P((char *, long)); { register int i; int curblk = 0; quad_t size = spcl.c_dinode.di_size; static char clearedbuf[MAXBSIZE]; char buf[MAXBSIZE / TP_BSIZE][TP_BSIZE]; char junk[TP_BSIZE]; if (spcl.c_type == TS_END) panic("ran off end of tape\n"); if (spcl.c_magic != NFS_MAGIC) 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) { (*fill)((char *)buf, (long)(size > TP_BSIZE ? fssize : (curblk - 1) * TP_BSIZE + size)); curblk = 0; } } else { if (curblk > 0) { (*fill)((char *)buf, (long)(size > TP_BSIZE ? curblk * TP_BSIZE : (curblk - 1) * TP_BSIZE + size)); curblk = 0; } (*skip)(clearedbuf, (long)(size > TP_BSIZE ? TP_BSIZE : size)); } if ((size -= TP_BSIZE) <= 0) { for (i++; i < spcl.c_count; i++) if (spcl.c_addr[i]) readtape(junk); break; } } if (gethead(&spcl) == GOOD && size > 0) { if (spcl.c_type == TS_ADDR) goto loop; dprintf(stdout, "Missing address (header) block for %s at %ld blocks\n", curfile.name, blksread); } if (curblk > 0) (*fill)((char *)buf, (long)((curblk * TP_BSIZE) + size)); findinode(&spcl); gettingfile = 0; } /* * Write out the next block of a file. */ static void 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\nwrite: %s\n", curfile.ino, curfile.name, strerror(errno)); done(1); } } /* * Skip over a hole in a file. */ /* ARGSUSED */ static void xtrskip(buf, size) char *buf; long size; { if (lseek(ofile, size, SEEK_CUR) == -1) { fprintf(stderr, "seek error extracting inode %d, name %s\nlseek: %s\n", curfile.ino, curfile.name, strerror(errno)); done(1); } } /* * Collect the next block of a symbolic link. */ static void 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); } /* * Skip over a hole in a symbolic link (should never happen). */ /* ARGSUSED */ static void xtrlnkskip(buf, size) char *buf; long size; { fprintf(stderr, "unallocated block in symbolic link %s\n", curfile.name); done(1); } /* * Collect the next block of a bit map. */ static void xtrmap(buf, size) char *buf; long size; { memmove(map, buf, size); map += size; } /* * Skip over a hole in a bit map (should never happen). */ /* ARGSUSED */ static void xtrmapskip(buf, size) char *buf; long size; { panic("hole in map\n"); map += size; } /* * Noop, when an extraction function is not needed. */ /* ARGSUSED */ void xtrnull(buf, size) char *buf; long size; { return; } /* * Read TP_BSIZE blocks from the input. * Handle read errors, and end of media. */ static void readtape(buf) char *buf; { long rd, newvol, i; int cnt, seek_failed; if (blkcnt < numtrec) { memmove(buf, &tapebuf[(blkcnt++ * TP_BSIZE)], (long)TP_BSIZE); blksread++; tpblksread++; return; } for (i = 0; i < ntrec; i++) ((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0; if (numtrec == 0) numtrec = ntrec; cnt = ntrec * TP_BSIZE; rd = 0; getmore: #ifdef RRESTORE if (host) i = rmtread(&tapebuf[rd], cnt); else #endif i = read(mt, &tapebuf[rd], cnt); /* * Check for mid-tape short read error. * If found, skip rest of buffer and start with the next. */ if (!pipein && numtrec < ntrec && i > 0) { dprintf(stdout, "mid-media short read error.\n"); numtrec = ntrec; } /* * Handle partial block read. */ if (pipein && i == 0 && rd > 0) i = rd; else if (i > 0 && i != ntrec * TP_BSIZE) { if (pipein) { rd += i; cnt -= i; if (cnt > 0) goto getmore; i = rd; } else { /* * Short read. Process the blocks read. */ if (i % TP_BSIZE != 0) vprintf(stdout, "partial block read: %ld should be %ld\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; memset(tapebuf, 0, i); #ifdef RRESTORE if (host) seek_failed = (rmtseek(i, 1) < 0); else #endif seek_failed = (lseek(mt, i, SEEK_CUR) == (off_t)-1); if (seek_failed) { fprintf(stderr, "continuation failed: %s\n", strerror(errno)); done(1); } } /* * Handle end of tape. */ if (i == 0) { vprintf(stdout, "End-of-tape encountered\n"); if (!pipein) { newvol = volno + 1; volno = 0; numtrec = 0; getvol(newvol); readtape(buf); return; } if (rd % TP_BSIZE != 0) panic("partial block read: %d should be %d\n", rd, ntrec * TP_BSIZE); terminateinput(); memmove(&tapebuf[rd], &endoftapemark, (long)TP_BSIZE); } blkcnt = 0; memmove(buf, &tapebuf[(blkcnt++ * TP_BSIZE)], (long)TP_BSIZE); blksread++; tpblksread++; } static void findtapeblksize() { register long i; for (i = 0; i < ntrec; i++) ((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0; blkcnt = 0; #ifdef RRESTORE if (host) i = rmtread(tapebuf, ntrec * TP_BSIZE); else #endif i = read(mt, tapebuf, ntrec * TP_BSIZE); if (i <= 0) { fprintf(stderr, "tape read error: %s\n", strerror(errno)); done(1); } if (i % TP_BSIZE != 0) { fprintf(stderr, "Tape block size (%ld) %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 %ld\n", ntrec); } void closemt() { if (mt < 0) return; #ifdef RRESTORE if (host) rmtclose(); else #endif (void) close(mt); } /* * Read the next block from the tape. * Check to see if it is one of several vintage headers. * If it is an old style header, convert it to a new style header. * If it is not any valid header, return an error. */ static int gethead(buf) struct s_spcl *buf; { long i; union { quad_t qval; long val[2]; } qcvt; 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((u_char *)"8l4s31l", (u_char *)buf); swabst((u_char *)"l",(u_char *) &buf->c_level); swabst((u_char *)"2l",(u_char *) &buf->c_flags); } goto good; } readtape((char *)(&u_ospcl.s_ospcl)); memset(buf, 0, (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; memmove(buf->c_addr, u_ospcl.s_ospcl.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_size > 0xfffffff) && (buf->c_dinode.di_mode & IFMT) == IFDIR && Qcvt == 0) { qcvt.qval = buf->c_dinode.di_size; if (qcvt.val[0] || qcvt.val[1]) { printf("Note: Doing Quad swapping\n"); Qcvt = 1; } } if (Qcvt) { qcvt.qval = buf->c_dinode.di_size; i = qcvt.val[1]; qcvt.val[1] = qcvt.val[0]; qcvt.val[0] = i; buf->c_dinode.di_size = qcvt.qval; } 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: if ((buf->c_flags & DR_NEWINODEFMT) == 0) oldinofmt = 1; /* fall through */ 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 we are restoring a filesystem with old format inodes, * copy the uid/gid to the new location. */ if (oldinofmt) { buf->c_dinode.di_uid = buf->c_dinode.di_ouid; buf->c_dinode.di_gid = buf->c_dinode.di_ogid; } if (dflag) accthdr(buf); return(GOOD); } /* * Check that a header is where it belongs and predict the next header */ static void 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 (%s inode format) ", oldinofmt ? "old" : "new"); if (header->c_firstrec) fprintf(stderr, "begins with record %ld", header->c_firstrec); fprintf(stderr, "\n"); previno = 0x7fffffff; return; } if (previno == 0x7fffffff) goto newcalc; switch (prevtype) { case TS_BITS: fprintf(stderr, "Dumped inodes map header"); break; case TS_CLRI: fprintf(stderr, "Used inodes map 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 %ld blocks, got %ld 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. */ static void findinode(header) struct s_spcl *header; { static long skipcnt = 0; long i; char buf[TP_BSIZE]; curfile.name = ""; curfile.action = UNKNOWN; curfile.dip = NULL; curfile.ino = 0; do { if (header->c_magic != NFS_MAGIC) { skipcnt++; while (gethead(header) == FAIL || header->c_date != dumpdate) skipcnt++; } switch (header->c_type) { case TS_ADDR: /* * Skip up to the beginning of the next record */ for (i = 0; i < header->c_count; i++) if (header->c_addr[i]) readtape(buf); while (gethead(header) == FAIL || header->c_date != dumpdate) skipcnt++; break; case TS_INODE: curfile.dip = &header->c_dinode; curfile.ino = header->c_inumber; break; case TS_END: curfile.ino = maxino; break; case TS_CLRI: curfile.name = ""; break; case TS_BITS: curfile.name = ""; break; case TS_TAPE: panic("unexpected tape header\n"); /* NOTREACHED */ default: panic("unknown tape header type %d\n", spcl.c_type); /* NOTREACHED */ } } while (header->c_type == TS_ADDR); if (skipcnt > 0) fprintf(stderr, "resync restore, skipped %ld blocks\n", skipcnt); skipcnt = 0; } static int checksum(buf) register int *buf; { register int i, j; j = sizeof(union u_spcl) / sizeof(int); i = 0; if(!Bcvt) { do i += *buf++; while (--j); } else { /* What happens if we want to read restore tapes for a 16bit int machine??? */ do i += swabl(*buf++); 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 #if __STDC__ #include #else #include #endif void #if __STDC__ msg(const char *fmt, ...) #else msg(fmt, va_alist) char *fmt; va_dcl #endif { va_list ap; #if __STDC__ va_start(ap, fmt); #else va_start(ap); #endif (void)vfprintf(stderr, fmt, ap); va_end(ap); } #endif /* RRESTORE */ static 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); } static 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); } void swabst(cp, sp) register u_char *cp, *sp; { int n = 0; 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; } } static u_long swabl(x) u_long x; { swabst((u_char *)"l", (u_char *)&x); return (x); } Index: head/sbin/restore/utilities.c =================================================================== --- head/sbin/restore/utilities.c (revision 37905) +++ head/sbin/restore/utilities.c (revision 37906) @@ -1,453 +1,456 @@ /* * Copyright (c) 1983, 1993 * 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 +#if 0 static char sccsid[] = "@(#)utilities.c 8.5 (Berkeley) 4/28/95"; +#endif +static const char rcsid[] = + "$Id$"; #endif /* not lint */ #include #include #include #include #include #include -#include #include #include #include "restore.h" #include "extern.h" /* * Insure that all the components of a pathname exist. */ void pathcheck(name) char *name; { register char *cp; struct entry *ep; char *start; start = strchr(name, '/'); if (start == 0) return; for (cp = start; *cp != '\0'; cp++) { if (*cp != '/') continue; *cp = '\0'; ep = lookupname(name); if (ep == NULL) { /* Safe; we know the pathname exists in the dump. */ ep = addentry(name, pathsearch(name)->d_ino, NODE); newnode(ep); } ep->e_flags |= NEW|KEEP; *cp = '/'; } } /* * Change a name to a unique temporary name. */ void 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 != NULL && np != ep; np = np->e_links) i++; if (np == NULL) badentry(ep, "not on ino list"); (void) sprintf(name, "%s%ld%lu", TMPHDR, i, (u_long)ep->e_ino); return (name); } /* * Rename a file or directory. */ void renameit(from, to) char *from, *to; { if (!Nflag && rename(from, to) < 0) { fprintf(stderr, "warning: cannot rename %s to %s: %s\n", from, to, strerror(errno)); return; } vprintf(stdout, "rename %s to %s\n", from, to); } /* * Create a new node (directory). */ void 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 && !uflag) { np->e_flags |= EXISTED; fprintf(stderr, "warning: %s: %s\n", cp, strerror(errno)); return; } vprintf(stdout, "Make node %s\n", cp); } /* * Remove an old node (directory). */ void removenode(ep) register struct entry *ep; { char *cp; if (ep->e_type != NODE) badentry(ep, "removenode: not a node"); if (ep->e_entries != NULL) 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: %s: %s\n", cp, strerror(errno)); return; } vprintf(stdout, "Remove node %s\n", cp); } /* * Remove a leaf. */ void 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: %s: %s\n", cp, strerror(errno)); return; } vprintf(stdout, "Remove leaf %s\n", cp); } /* * Create a link. */ int linkit(existing, new, type) char *existing, *new; int type; { /* if we want to unlink first, do it now so *link() won't fail */ if (uflag && !Nflag) (void)unlink(new); if (type == SYMLINK) { if (!Nflag && symlink(existing, new) < 0) { fprintf(stderr, "warning: cannot create symbolic link %s->%s: %s\n", new, existing, strerror(errno)); return (FAIL); } } else if (type == HARDLINK) { int ret; if (!Nflag && (ret = link(existing, new)) < 0) { struct stat s; /* * Most likely, the schg flag is set. Clear the * flags and try again. */ if (stat(existing, &s) == 0 && s.st_flags != 0 && chflags(existing, 0) == 0) { ret = link(existing, new); chflags(existing, s.st_flags); } if (ret < 0) { fprintf(stderr, "warning: cannot create " "hard link %s->%s: %s\n", new, existing, strerror(errno)); 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); } /* * Create a whiteout. */ int addwhiteout(name) char *name; { if (!Nflag && mknod(name, S_IFWHT, 0) < 0) { fprintf(stderr, "warning: cannot create whiteout %s: %s\n", name, strerror(errno)); return (FAIL); } vprintf(stdout, "Create whiteout %s\n", name); return (GOOD); } /* * Delete a whiteout. */ void delwhiteout(ep) register struct entry *ep; { char *name; if (ep->e_type != LEAF) badentry(ep, "delwhiteout: not a leaf"); ep->e_flags |= REMOVED; ep->e_flags &= ~TMPNAME; name = myname(ep); if (!Nflag && undelete(name) < 0) { fprintf(stderr, "warning: cannot delete whiteout %s: %s\n", name, strerror(errno)); return; } vprintf(stdout, "Delete whiteout %s\n", name); } /* * 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 == NULL || 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 == NULL || ep->e_type == NODE) continue; if (ep->e_flags & (NEW|EXTRACT)) return (start); } return (start); } /* * report on a badly formed entry */ void 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 != NULL) fprintf(stderr, "sibling name: %s\n", myname(ep->e_sibling)); if (ep->e_entries != NULL) fprintf(stderr, "next entry name: %s\n", myname(ep->e_entries)); if (ep->e_links != NULL) fprintf(stderr, "next link name: %s\n", myname(ep->e_links)); if (ep->e_next != NULL) 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: %lu\n", (u_long)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) const char *name; { struct direct *dp; ino_t ino; ino = ((dp = pathsearch(name)) == NULL) ? 0 : dp->d_ino; if (ino == 0 || TSTINO(ino, dumpmap) == 0) fprintf(stderr, "%s is not on the tape\n", name); return (ino); } /* * Elicit a reply. */ int 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 */ #if __STDC__ #include #else #include #endif void #if __STDC__ panic(const char *fmt, ...) #else panic(fmt, va_alist) char *fmt; va_dcl #endif { va_list ap; #if __STDC__ va_start(ap, fmt); #else va_start(ap); #endif vfprintf(stderr, fmt, ap); if (yflag) return; if (reply("abort") == GOOD) { if (reply("dump core") == GOOD) abort(); done(1); } }