From: William F. Jolitz Date: Tue, 7 May 1991 18:54:16 +0000 (-0800) Subject: 386BSD 0.1 development X-Git-Tag: 386BSD-0.1~1473 X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/commitdiff_plain/c7007d7d21e4d433b3c3edd28e77a0e58815acb6 386BSD 0.1 development Work on file usr/src/sbin/restore/dirs.c Co-Authored-By: Lynne Greer Jolitz Synthesized-from: 386BSD-0.1 --- diff --git a/usr/src/sbin/restore/dirs.c b/usr/src/sbin/restore/dirs.c new file mode 100644 index 0000000000..b2c00f9337 --- /dev/null +++ b/usr/src/sbin/restore/dirs.c @@ -0,0 +1,691 @@ +/* + * Copyright (c) 1983 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)dirs.c 5.15 (Berkeley) 2/26/91"; +#endif /* not lint */ + +#include "restore.h" +#include +#include +#include +#include "pathnames.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; + daddr_t t_seekpt; + long t_size; +}; +static struct inotab *inotab[HASHSIZE]; +extern struct inotab *inotablookup(); +extern struct inotab *allocinotab(); + +/* + * Information retained about directories. + */ +struct modeinfo { + ino_t ino; + struct timeval timep[2]; + short mode; + short uid; + short gid; +}; + +/* + * Definitions for library routines operating on directories. + */ +#undef DIRBLKSIZ +#define DIRBLKSIZ 1024 +struct dirdesc { + int dd_fd; + long dd_loc; + long dd_size; + char dd_buf[DIRBLKSIZ]; +}; +extern DIR *opendirfile(); +extern off_t rst_telldir(); +extern void rst_seekdir(); + +/* + * Global variables for this file. + */ +static daddr_t seekpt; +static FILE *df, *mf; +static DIR *dirp; +static char dirfile[32] = "#"; /* No file */ +static char modefile[32] = "#"; /* No file */ +static char dot[2] = "."; /* So it can be modified */ +extern ino_t search(); +struct direct *rst_readdir(); +extern void rst_seekdir(); + +/* + * Format of old style directories. + */ +#define ODIRSIZ 14 +struct odirect { + u_short d_ino; + char d_name[ODIRSIZ]; +}; + +/* + * 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. + */ +extractdirs(genmode) + int genmode; +{ + register int i; + register struct dinode *ip; + struct inotab *itp; + struct direct nulldir; + int putdir(), null(); + + vprintf(stdout, "Extract directories from tape\n"); + (void) sprintf(dirfile, "%s/rstdir%d", _PATH_TMP, dumpdate); + df = fopen(dirfile, "w"); + if (df == 0) { + fprintf(stderr, + "restore: %s - cannot create directory temporary\n", + dirfile); + perror("fopen"); + done(1); + } + if (genmode != 0) { + (void) sprintf(modefile, "%s/rstmode%d", _PATH_TMP, dumpdate); + mf = fopen(modefile, "w"); + if (mf == 0) { + fprintf(stderr, + "restore: %s - cannot create modefile \n", + modefile); + perror("fopen"); + done(1); + } + } + nulldir.d_ino = 0; + nulldir.d_namlen = 1; + (void) strcpy(nulldir.d_name, "/"); + nulldir.d_reclen = DIRSIZ(&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) + perror("opendirfile"); + 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, null); + putent(&nulldir); + flushent(); + itp->t_size = seekpt - itp->t_seekpt; + } +} + +/* + * skip over all the directories on the tape + */ +skipdirs() +{ + + while ((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. + */ +treescan(pname, ino, todo) + char *pname; + ino_t ino; + long (*todo)(); +{ + register struct inotab *itp; + register struct direct *dp; + register struct entry *np; + int namelen; + daddr_t 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, MAXPATHLEN); + (void) strncat(locname, "/", MAXPATHLEN); + 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 && dp->d_ino != 0) { + 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); + treescan(locname, dp->d_ino, todo); + rst_seekdir(dirp, bpt, itp->t_seekpt); + } + dp = rst_readdir(dirp); + bpt = rst_telldir(dirp); + } + if (dp == NULL) + fprintf(stderr, "corrupted directory: %s.\n", locname); +} + +/* + * Search the directory tree rooted at inode ROOTINO + * for the path pointed at by n + */ +ino_t +psearch(n) + char *n; +{ + register char *cp, *cp1; + ino_t ino; + char c; + + ino = ROOTINO; + if (*(cp = n) == '/') + cp++; +next: + cp1 = cp + 1; + while (*cp1 != '/' && *cp1) + cp1++; + c = *cp1; + *cp1 = 0; + ino = search(ino, cp); + if (ino == 0) { + *cp1 = c; + return(0); + } + *cp1 = c; + if (c == '/') { + cp = cp1+1; + goto next; + } + return(ino); +} + +/* + * search the directory inode ino + * looking for entry cp + */ +ino_t +search(inum, cp) + ino_t inum; + char *cp; +{ + register struct direct *dp; + register struct inotab *itp; + int len; + + itp = inotablookup(inum); + if (itp == NULL) + return(0); + rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); + len = strlen(cp); + do { + dp = rst_readdir(dirp); + if (dp == NULL || dp->d_ino == 0) + return (0); + } while (dp->d_namlen != len || strncmp(dp->d_name, cp, len) != 0); + return(dp->d_ino); +} + +/* + * Put the directory entries in the directory file + */ +putdir(buf, size) + char *buf; + int size; +{ + struct direct cvtbuf; + register struct odirect *odp; + struct odirect *eodp; + register struct direct *dp; + long loc, i; + extern int Bcvt; + + 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("l2s", (char *) dp); + } + i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1)); + if ((dp->d_reclen & 0x3) != 0 || + dp->d_reclen > i || + dp->d_reclen < DIRSIZ(dp) || + dp->d_namlen > MAXNAMLEN) { + vprintf(stdout, "Mangled directory\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. + */ +putent(dp) + struct direct *dp; +{ + dp->d_reclen = DIRSIZ(dp); + if (dirloc + dp->d_reclen > DIRBLKSIZ) { + ((struct direct *)(dirbuf + prev))->d_reclen = + DIRBLKSIZ - prev; + (void) fwrite(dirbuf, 1, DIRBLKSIZ, df); + dirloc = 0; + } + bcopy((char *)dp, dirbuf + dirloc, (long)dp->d_reclen); + prev = dirloc; + dirloc += dp->d_reclen; +} + +/* + * flush out a directory that is finished. + */ +flushent() +{ + + ((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev; + (void) fwrite(dirbuf, (int)dirloc, 1, df); + seekpt = ftell(df); + dirloc = 0; +} + +dcvt(odp, ndp) + register struct odirect *odp; + register struct direct *ndp; +{ + + bzero((char *)ndp, (long)(sizeof *ndp)); + ndp->d_ino = odp->d_ino; + (void) strncpy(ndp->d_name, odp->d_name, ODIRSIZ); + ndp->d_namlen = strlen(ndp->d_name); + ndp->d_reclen = DIRSIZ(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. + */ +void +rst_seekdir(dirp, loc, base) + register DIR *dirp; + daddr_t loc, base; +{ + + if (loc == rst_telldir(dirp)) + return; + loc -= base; + if (loc < 0) + fprintf(stderr, "bad seek pointer to rst_seekdir %d\n", loc); + (void) lseek(dirp->dd_fd, base + (loc & ~(DIRBLKSIZ - 1)), 0); + 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 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) + continue; + 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 + */ +DIR * +rst_opendir(name) + char *name; +{ + struct inotab *itp; + ino_t ino; + + if ((ino = dirlookup(name)) > 0 && + (itp = inotablookup(ino)) != NULL) { + rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); + return (dirp); + } + return (0); +} + +/* + * Simulate finding the current offset in the directory. + */ +off_t +rst_telldir(dirp) + DIR *dirp; +{ + off_t lseek(); + + return (lseek(dirp->dd_fd, 0L, 1) - dirp->dd_size + dirp->dd_loc); +} + +/* + * Open a directory file. + */ +DIR * +opendirfile(name) + char *name; +{ + register DIR *dirp; + register int fd; + + if ((fd = open(name, 0)) == -1) + return NULL; + if ((dirp = (DIR *)malloc(sizeof(DIR))) == NULL) { + 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 + */ +setdirmodes() +{ + FILE *mf; + struct modeinfo node; + struct entry *ep; + char *cp; + + vprintf(stdout, "Set directory mode, owner, and times.\n"); + (void) sprintf(modefile, "%s/rstmode%d", _PATH_TMP, dumpdate); + mf = fopen(modefile, "r"); + if (mf == NULL) { + perror("fopen"); + 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 == NIL) + continue; + if (ep->e_flags & EXISTED) { + ep->e_flags &= ~NEW; + continue; + } + if (node.ino == ROOTINO && + reply("set owner/mode for '.'") == FAIL) + continue; + } + if (ep == NIL) { + 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); + 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. + */ +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 = creat(name, 0666)) < 0) { + fprintf(stderr, "%s: ", name); + (void) fflush(stderr); + perror("cannot create file"); + 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); + perror("read"); + done(1); + } + if (!Nflag && write(ofile, buf, (int) size) == -1) { + fprintf(stderr, + "write error extracting inode %d, name %s\n", + curfile.ino, curfile.name); + perror("write"); + done(1); + } + } + (void) close(dp); + (void) close(ofile); + return (GOOD); +} + +/* + * Determine the type of an inode + */ +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. + */ +struct inotab * +allocinotab(ino, dip, seekpt) + ino_t ino; + struct dinode *dip; + daddr_t seekpt; +{ + register struct inotab *itp; + struct modeinfo node; + + itp = (struct inotab *)calloc(1, sizeof(struct inotab)); + if (itp == 0) + 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 = 0; + node.timep[1].tv_sec = dip->di_mtime; + node.timep[1].tv_usec = 0; + node.mode = dip->di_mode; + 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 + */ +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 ((struct inotab *)0); +} + +/* + * Clean up and exit + */ +done(exitcode) + int exitcode; +{ + + closemt(); + if (modefile[0] != '#') + (void) unlink(modefile); + if (dirfile[0] != '#') + (void) unlink(dirfile); + exit(exitcode); +}