Home | History | Annotate | Line # | Download | only in restore
dirs.c revision 1.21
      1 /*	$NetBSD: dirs.c,v 1.21 1996/11/30 18:04:45 cgd Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1983, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  * (c) UNIX System Laboratories, Inc.
      7  * All or some portions of this file are derived from material licensed
      8  * to the University of California by American Telephone and Telegraph
      9  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
     10  * the permission of UNIX System Laboratories, Inc.
     11  *
     12  * Redistribution and use in source and binary forms, with or without
     13  * modification, are permitted provided that the following conditions
     14  * are met:
     15  * 1. Redistributions of source code must retain the above copyright
     16  *    notice, this list of conditions and the following disclaimer.
     17  * 2. Redistributions in binary form must reproduce the above copyright
     18  *    notice, this list of conditions and the following disclaimer in the
     19  *    documentation and/or other materials provided with the distribution.
     20  * 3. All advertising materials mentioning features or use of this software
     21  *    must display the following acknowledgement:
     22  *	This product includes software developed by the University of
     23  *	California, Berkeley and its contributors.
     24  * 4. Neither the name of the University nor the names of its contributors
     25  *    may be used to endorse or promote products derived from this software
     26  *    without specific prior written permission.
     27  *
     28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     38  * SUCH DAMAGE.
     39  */
     40 
     41 #ifndef lint
     42 #if 0
     43 static char sccsid[] = "@(#)dirs.c	8.5 (Berkeley) 8/31/94";
     44 #else
     45 static char rcsid[] = "$NetBSD: dirs.c,v 1.21 1996/11/30 18:04:45 cgd Exp $";
     46 #endif
     47 #endif /* not lint */
     48 
     49 #include <sys/param.h>
     50 #include <sys/file.h>
     51 #include <sys/stat.h>
     52 #include <sys/time.h>
     53 
     54 #include <ufs/ffs/fs.h>
     55 #include <ufs/ufs/dinode.h>
     56 #include <ufs/ufs/dir.h>
     57 #include <protocols/dumprestore.h>
     58 
     59 #include <errno.h>
     60 #include <stdio.h>
     61 #include <stdlib.h>
     62 #include <string.h>
     63 #include <unistd.h>
     64 
     65 #include <machine/endian.h>
     66 
     67 #include "pathnames.h"
     68 #include "restore.h"
     69 #include "extern.h"
     70 
     71 /*
     72  * Symbol table of directories read from tape.
     73  */
     74 #define HASHSIZE	1000
     75 #define INOHASH(val) (val % HASHSIZE)
     76 struct inotab {
     77 	struct	inotab *t_next;
     78 	ino_t	t_ino;
     79 	int32_t	t_seekpt;
     80 	int32_t	t_size;
     81 };
     82 static struct inotab *inotab[HASHSIZE];
     83 
     84 /*
     85  * Information retained about directories.
     86  */
     87 struct modeinfo {
     88 	ino_t ino;
     89 	struct timeval timep[2];
     90 	mode_t mode;
     91 	uid_t uid;
     92 	gid_t gid;
     93 	int flags;
     94 };
     95 
     96 /*
     97  * Definitions for library routines operating on directories.
     98  */
     99 #undef DIRBLKSIZ
    100 #define DIRBLKSIZ 1024
    101 struct rstdirdesc {
    102 	int	dd_fd;
    103 	int32_t	dd_loc;
    104 	int32_t	dd_size;
    105 	char	dd_buf[DIRBLKSIZ];
    106 };
    107 
    108 /*
    109  * Global variables for this file.
    110  */
    111 static long	seekpt;
    112 static FILE	*df, *mf;
    113 static RST_DIR	*dirp;
    114 static char	dirfile[MAXPATHLEN] = "#";	/* No file */
    115 static char	modefile[MAXPATHLEN] = "#";	/* No file */
    116 static char	dot[2] = ".";			/* So it can be modified */
    117 
    118 /*
    119  * Format of old style directories.
    120  */
    121 #define ODIRSIZ 14
    122 struct odirect {
    123 	u_short	d_ino;
    124 	char	d_name[ODIRSIZ];
    125 };
    126 
    127 static struct inotab	*allocinotab __P((ino_t, struct dinode *, long));
    128 static void		 dcvt __P((struct odirect *, struct direct *));
    129 static void		 flushent __P((void));
    130 static struct inotab	*inotablookup __P((ino_t));
    131 static RST_DIR		*opendirfile __P((const char *));
    132 static void		 putdir __P((char *, long));
    133 static void		 putent __P((struct direct *));
    134 static void		 rst_seekdir __P((RST_DIR *, long, long));
    135 static long		 rst_telldir __P((RST_DIR *));
    136 static struct direct	*searchdir __P((ino_t, char *));
    137 
    138 /*
    139  *	Extract directory contents, building up a directory structure
    140  *	on disk for extraction by name.
    141  *	If genmode is requested, save mode, owner, and times for all
    142  *	directories on the tape.
    143  */
    144 void
    145 extractdirs(genmode)
    146 	int genmode;
    147 {
    148 	register int i;
    149 	register struct dinode *ip;
    150 	struct inotab *itp;
    151 	struct direct nulldir;
    152 
    153 	vprintf(stdout, "Extract directories from tape\n");
    154 	(void) snprintf(dirfile, sizeof(dirfile), "%s/rstdir%d",
    155 	    _PATH_TMP, dumpdate);
    156 	if (command != 'r' && command != 'R') {
    157 		(void) snprintf(dirfile, sizeof(dirfile), "%s/rstdir%d-XXXXXX",
    158 		    _PATH_TMP, dumpdate);
    159 		if (mktemp(dirfile) == NULL) {
    160 			fprintf(stderr,
    161 			    "restore: %s - cannot mktemp directory temporary\n",
    162 			    dirfile);
    163 			exit(1);
    164 		}
    165 	}
    166 	df = fopen(dirfile, "w");
    167 	if (df == NULL) {
    168 		fprintf(stderr,
    169 		    "restore: %s - cannot create directory temporary\n",
    170 		    dirfile);
    171 		fprintf(stderr, "fopen: %s\n", strerror(errno));
    172 		exit(1);
    173 	}
    174 	if (genmode != 0) {
    175 		(void) snprintf(modefile, sizeof(modefile), "%s/rstmode%d",
    176 		    _PATH_TMP, dumpdate);
    177 		if (command != 'r' && command != 'R') {
    178 			(void) snprintf(modefile, sizeof(modefile),
    179 			    "%s/rstmode%d-XXXXXX", _PATH_TMP, dumpdate);
    180 			if (mktemp(modefile) == NULL) {
    181 				fprintf(stderr,
    182 				    "restore: %s - cannot mktemp "
    183 				    "directory temporary\n", dirfile);
    184 				exit(1);
    185 			}
    186 		}
    187 		mf = fopen(modefile, "w");
    188 		if (mf == NULL) {
    189 			fprintf(stderr,
    190 			    "restore: %s - cannot create modefile \n",
    191 			    modefile);
    192 			fprintf(stderr, "fopen: %s\n", strerror(errno));
    193 			exit(1);
    194 		}
    195 	}
    196 	nulldir.d_ino = 0;
    197 	nulldir.d_type = DT_DIR;
    198 	nulldir.d_namlen = 1;
    199 	(void) strcpy(nulldir.d_name, "/");
    200 	nulldir.d_reclen = DIRSIZ(0, &nulldir);
    201 	for (;;) {
    202 		curfile.name = "<directory file - name unknown>";
    203 		curfile.action = USING;
    204 		ip = curfile.dip;
    205 		if (ip == NULL || (ip->di_mode & IFMT) != IFDIR) {
    206 			(void) fclose(df);
    207 			dirp = opendirfile(dirfile);
    208 			if (dirp == NULL)
    209 				fprintf(stderr, "opendirfile: %s\n",
    210 				    strerror(errno));
    211 			if (mf != NULL)
    212 				(void) fclose(mf);
    213 			i = dirlookup(dot);
    214 			if (i == 0)
    215 				panic("Root directory is not on tape\n");
    216 			return;
    217 		}
    218 		itp = allocinotab(curfile.ino, ip, seekpt);
    219 		getfile(putdir, xtrnull);
    220 		putent(&nulldir);
    221 		flushent();
    222 		itp->t_size = seekpt - itp->t_seekpt;
    223 	}
    224 }
    225 
    226 /*
    227  * skip over all the directories on the tape
    228  */
    229 void
    230 skipdirs()
    231 {
    232 
    233 	while ((curfile.dip->di_mode & IFMT) == IFDIR) {
    234 		skipfile();
    235 	}
    236 }
    237 
    238 /*
    239  *	Recursively find names and inumbers of all files in subtree
    240  *	pname and pass them off to be processed.
    241  */
    242 void
    243 treescan(pname, ino, todo)
    244 	char *pname;
    245 	ino_t ino;
    246 	long (*todo) __P((char *, ino_t, int));
    247 {
    248 	register struct inotab *itp;
    249 	register struct direct *dp;
    250 	int namelen;
    251 	long bpt;
    252 	char locname[MAXPATHLEN + 1];
    253 
    254 	itp = inotablookup(ino);
    255 	if (itp == NULL) {
    256 		/*
    257 		 * Pname is name of a simple file or an unchanged directory.
    258 		 */
    259 		(void) (*todo)(pname, ino, LEAF);
    260 		return;
    261 	}
    262 	/*
    263 	 * Pname is a dumped directory name.
    264 	 */
    265 	if ((*todo)(pname, ino, NODE) == FAIL)
    266 		return;
    267 	/*
    268 	 * begin search through the directory
    269 	 * skipping over "." and ".."
    270 	 */
    271 	(void) strncpy(locname, pname, MAXPATHLEN);
    272 	(void) strncat(locname, "/", MAXPATHLEN);
    273 	namelen = strlen(locname);
    274 	rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
    275 	dp = rst_readdir(dirp); /* "." */
    276 	if (dp != NULL && strcmp(dp->d_name, ".") == 0)
    277 		dp = rst_readdir(dirp); /* ".." */
    278 	else
    279 		fprintf(stderr, "Warning: `.' missing from directory %s\n",
    280 			pname);
    281 	if (dp != NULL && strcmp(dp->d_name, "..") == 0)
    282 		dp = rst_readdir(dirp); /* first real entry */
    283 	else
    284 		fprintf(stderr, "Warning: `..' missing from directory %s\n",
    285 			pname);
    286 	bpt = rst_telldir(dirp);
    287 	/*
    288 	 * a zero inode signals end of directory
    289 	 */
    290 	while (dp != NULL) {
    291 		locname[namelen] = '\0';
    292 		if (namelen + dp->d_namlen >= MAXPATHLEN) {
    293 			fprintf(stderr, "%s%s: name exceeds %d char\n",
    294 				locname, dp->d_name, MAXPATHLEN);
    295 		} else {
    296 			(void) strncat(locname, dp->d_name, (int)dp->d_namlen);
    297 			treescan(locname, dp->d_ino, todo);
    298 			rst_seekdir(dirp, bpt, itp->t_seekpt);
    299 		}
    300 		dp = rst_readdir(dirp);
    301 		bpt = rst_telldir(dirp);
    302 	}
    303 }
    304 
    305 /*
    306  * Lookup a pathname which is always assumed to start from the ROOTINO.
    307  */
    308 struct direct *
    309 pathsearch(pathname)
    310 	const char *pathname;
    311 {
    312 	ino_t ino;
    313 	struct direct *dp;
    314 	char *path, *name, buffer[MAXPATHLEN];
    315 
    316 	strcpy(buffer, pathname);
    317 	path = buffer;
    318 	ino = ROOTINO;
    319 	while (*path == '/')
    320 		path++;
    321 	dp = NULL;
    322 	while ((name = strsep(&path, "/")) != NULL && *name != NULL) {
    323 		if ((dp = searchdir(ino, name)) == NULL)
    324 			return (NULL);
    325 		ino = dp->d_ino;
    326 	}
    327 	return (dp);
    328 }
    329 
    330 /*
    331  * Lookup the requested name in directory inum.
    332  * Return its inode number if found, zero if it does not exist.
    333  */
    334 static struct direct *
    335 searchdir(inum, name)
    336 	ino_t	inum;
    337 	char	*name;
    338 {
    339 	register struct direct *dp;
    340 	register struct inotab *itp;
    341 	int len;
    342 
    343 	itp = inotablookup(inum);
    344 	if (itp == NULL)
    345 		return (NULL);
    346 	rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
    347 	len = strlen(name);
    348 	do {
    349 		dp = rst_readdir(dirp);
    350 		if (dp == NULL)
    351 			return (NULL);
    352 	} while (dp->d_namlen != len || strncmp(dp->d_name, name, len) != 0);
    353 	return (dp);
    354 }
    355 
    356 /*
    357  * Put the directory entries in the directory file
    358  */
    359 static void
    360 putdir(buf, size)
    361 	char *buf;
    362 	long size;
    363 {
    364 	struct direct cvtbuf;
    365 	register struct odirect *odp;
    366 	struct odirect *eodp;
    367 	register struct direct *dp;
    368 	long loc, i;
    369 
    370 	if (cvtflag) {
    371 		eodp = (struct odirect *)&buf[size];
    372 		for (odp = (struct odirect *)buf; odp < eodp; odp++)
    373 			if (odp->d_ino != 0) {
    374 				dcvt(odp, &cvtbuf);
    375 				putent(&cvtbuf);
    376 			}
    377 	} else {
    378 		for (loc = 0; loc < size; ) {
    379 			dp = (struct direct *)(buf + loc);
    380 			if (Bcvt)
    381 				swabst((u_char *)"ls", (u_char *) dp);
    382 			if (oldinofmt && dp->d_ino != 0) {
    383 #				if BYTE_ORDER == BIG_ENDIAN
    384 					if (Bcvt)
    385 						dp->d_namlen = dp->d_type;
    386 #				else
    387 					if (!Bcvt)
    388 						dp->d_namlen = dp->d_type;
    389 #				endif
    390 				dp->d_type = DT_UNKNOWN;
    391 			}
    392 			i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1));
    393 			if ((dp->d_reclen & 0x3) != 0 ||
    394 			    dp->d_reclen > i ||
    395 			    dp->d_reclen < DIRSIZ(0, dp) ||
    396 			    dp->d_namlen > NAME_MAX) {
    397 				vprintf(stdout, "Mangled directory: ");
    398 				if ((dp->d_reclen & 0x3) != 0)
    399 					vprintf(stdout,
    400 					   "reclen not multiple of 4 ");
    401 				if (dp->d_reclen < DIRSIZ(0, dp))
    402 					vprintf(stdout,
    403 					   "reclen less than DIRSIZ (%d < %d) ",
    404 					   dp->d_reclen, DIRSIZ(0, dp));
    405 				if (dp->d_namlen > NAME_MAX)
    406 					vprintf(stdout,
    407 					   "reclen name too big (%d > %d) ",
    408 					   dp->d_namlen, NAME_MAX);
    409 				vprintf(stdout, "\n");
    410 				loc += i;
    411 				continue;
    412 			}
    413 			loc += dp->d_reclen;
    414 			if (dp->d_ino != 0) {
    415 				putent(dp);
    416 			}
    417 		}
    418 	}
    419 }
    420 
    421 /*
    422  * These variables are "local" to the following two functions.
    423  */
    424 char dirbuf[DIRBLKSIZ];
    425 long dirloc = 0;
    426 long prev = 0;
    427 
    428 /*
    429  * add a new directory entry to a file.
    430  */
    431 static void
    432 putent(dp)
    433 	struct direct *dp;
    434 {
    435 	dp->d_reclen = DIRSIZ(0, dp);
    436 	if (dirloc + dp->d_reclen > DIRBLKSIZ) {
    437 		((struct direct *)(dirbuf + prev))->d_reclen =
    438 		    DIRBLKSIZ - prev;
    439 		(void) fwrite(dirbuf, 1, DIRBLKSIZ, df);
    440 		dirloc = 0;
    441 	}
    442 	memcpy(dirbuf + dirloc, dp, (long)dp->d_reclen);
    443 	prev = dirloc;
    444 	dirloc += dp->d_reclen;
    445 }
    446 
    447 /*
    448  * flush out a directory that is finished.
    449  */
    450 static void
    451 flushent()
    452 {
    453 	((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev;
    454 	(void) fwrite(dirbuf, (int)dirloc, 1, df);
    455 	seekpt = ftell(df);
    456 	dirloc = 0;
    457 }
    458 
    459 static void
    460 dcvt(odp, ndp)
    461 	register struct odirect *odp;
    462 	register struct direct *ndp;
    463 {
    464 
    465 	memset(ndp, 0, (long)(sizeof *ndp));
    466 	ndp->d_ino =  odp->d_ino;
    467 	ndp->d_type = DT_UNKNOWN;
    468 	(void) strncpy(ndp->d_name, odp->d_name, ODIRSIZ);
    469 	ndp->d_namlen = strlen(ndp->d_name);
    470 	ndp->d_reclen = DIRSIZ(0, ndp);
    471 }
    472 
    473 /*
    474  * Seek to an entry in a directory.
    475  * Only values returned by rst_telldir should be passed to rst_seekdir.
    476  * This routine handles many directories in a single file.
    477  * It takes the base of the directory in the file, plus
    478  * the desired seek offset into it.
    479  */
    480 static void
    481 rst_seekdir(dirp, loc, base)
    482 	register RST_DIR *dirp;
    483 	long loc, base;
    484 {
    485 
    486 	if (loc == rst_telldir(dirp))
    487 		return;
    488 	loc -= base;
    489 	if (loc < 0)
    490 		fprintf(stderr, "bad seek pointer to rst_seekdir %d\n", loc);
    491 	(void) lseek(dirp->dd_fd, base + (loc & ~(DIRBLKSIZ - 1)), SEEK_SET);
    492 	dirp->dd_loc = loc & (DIRBLKSIZ - 1);
    493 	if (dirp->dd_loc != 0)
    494 		dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ);
    495 }
    496 
    497 /*
    498  * get next entry in a directory.
    499  */
    500 struct direct *
    501 rst_readdir(dirp)
    502 	register RST_DIR *dirp;
    503 {
    504 	register struct direct *dp;
    505 
    506 	for (;;) {
    507 		if (dirp->dd_loc == 0) {
    508 			dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf,
    509 			    DIRBLKSIZ);
    510 			if (dirp->dd_size <= 0) {
    511 				dprintf(stderr, "error reading directory\n");
    512 				return (NULL);
    513 			}
    514 		}
    515 		if (dirp->dd_loc >= dirp->dd_size) {
    516 			dirp->dd_loc = 0;
    517 			continue;
    518 		}
    519 		dp = (struct direct *)(dirp->dd_buf + dirp->dd_loc);
    520 		if (dp->d_reclen == 0 ||
    521 		    dp->d_reclen > DIRBLKSIZ + 1 - dirp->dd_loc) {
    522 			dprintf(stderr, "corrupted directory: bad reclen %d\n",
    523 				dp->d_reclen);
    524 			return (NULL);
    525 		}
    526 		dirp->dd_loc += dp->d_reclen;
    527 		if (dp->d_ino == 0 && strcmp(dp->d_name, "/") == 0)
    528 			return (NULL);
    529 		if (dp->d_ino >= maxino) {
    530 			dprintf(stderr, "corrupted directory: bad inum %d\n",
    531 				dp->d_ino);
    532 			continue;
    533 		}
    534 		return (dp);
    535 	}
    536 }
    537 
    538 /*
    539  * Simulate the opening of a directory
    540  */
    541 RST_DIR *
    542 rst_opendir(name)
    543 	const char *name;
    544 {
    545 	struct inotab *itp;
    546 	RST_DIR *dirp;
    547 	ino_t ino;
    548 
    549 	if ((ino = dirlookup(name)) > 0 &&
    550 	    (itp = inotablookup(ino)) != NULL) {
    551 		dirp = opendirfile(dirfile);
    552 		rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
    553 		return (dirp);
    554 	}
    555 	return (NULL);
    556 }
    557 
    558 /*
    559  * In our case, there is nothing to do when closing a directory.
    560  */
    561 void
    562 rst_closedir(dirp)
    563 	RST_DIR *dirp;
    564 {
    565 
    566 	(void)close(dirp->dd_fd);
    567 	free(dirp);
    568 	return;
    569 }
    570 
    571 /*
    572  * Simulate finding the current offset in the directory.
    573  */
    574 static long
    575 rst_telldir(dirp)
    576 	RST_DIR *dirp;
    577 {
    578 	return ((long)lseek(dirp->dd_fd,
    579 	    (off_t)0, SEEK_CUR) - dirp->dd_size + dirp->dd_loc);
    580 }
    581 
    582 /*
    583  * Open a directory file.
    584  */
    585 static RST_DIR *
    586 opendirfile(name)
    587 	const char *name;
    588 {
    589 	register RST_DIR *dirp;
    590 	register int fd;
    591 
    592 	if ((fd = open(name, O_RDONLY)) == -1)
    593 		return (NULL);
    594 	if ((dirp = malloc(sizeof(RST_DIR))) == NULL) {
    595 		(void)close(fd);
    596 		return (NULL);
    597 	}
    598 	dirp->dd_fd = fd;
    599 	dirp->dd_loc = 0;
    600 	return (dirp);
    601 }
    602 
    603 /*
    604  * Set the mode, owner, and times for all new or changed directories
    605  */
    606 void
    607 setdirmodes(flags)
    608 	int flags;
    609 {
    610 	FILE *mf;
    611 	struct modeinfo node;
    612 	struct entry *ep;
    613 	char *cp;
    614 
    615 	vprintf(stdout, "Set directory mode, owner, and times.\n");
    616 	if (command == 'r' || command == 'R')
    617 		(void) snprintf(modefile, sizeof(modefile), "%s/rstmode%d",
    618 		    _PATH_TMP, dumpdate);
    619 	if (modefile[0] == '#') {
    620 		panic("modefile not defined\n");
    621 		fprintf(stderr, "directory mode, owner, and times not set\n");
    622 		return;
    623 	}
    624 	mf = fopen(modefile, "r");
    625 	if (mf == NULL) {
    626 		fprintf(stderr, "fopen: %s\n", strerror(errno));
    627 		fprintf(stderr, "cannot open mode file %s\n", modefile);
    628 		fprintf(stderr, "directory mode, owner, and times not set\n");
    629 		return;
    630 	}
    631 	clearerr(mf);
    632 	for (;;) {
    633 		(void) fread((char *)&node, 1, sizeof(struct modeinfo), mf);
    634 		if (feof(mf))
    635 			break;
    636 		ep = lookupino(node.ino);
    637 		if (command == 'i' || command == 'x') {
    638 			if (ep == NULL)
    639 				continue;
    640 			if ((flags & FORCE) == 0 && ep->e_flags & EXISTED) {
    641 				ep->e_flags &= ~NEW;
    642 				continue;
    643 			}
    644 			if (node.ino == ROOTINO &&
    645 		   	    reply("set owner/mode for '.'") == FAIL)
    646 				continue;
    647 		}
    648 		if (ep == NULL) {
    649 			panic("cannot find directory inode %d\n", node.ino);
    650 		} else {
    651 			cp = myname(ep);
    652 			(void) chown(cp, node.uid, node.gid);
    653 			(void) chmod(cp, node.mode);
    654 			(void) chflags(cp, node.flags);
    655 			utimes(cp, node.timep);
    656 			ep->e_flags &= ~NEW;
    657 		}
    658 	}
    659 	if (ferror(mf))
    660 		panic("error setting directory modes\n");
    661 	(void) fclose(mf);
    662 }
    663 
    664 /*
    665  * Generate a literal copy of a directory.
    666  */
    667 int
    668 genliteraldir(name, ino)
    669 	char *name;
    670 	ino_t ino;
    671 {
    672 	register struct inotab *itp;
    673 	int ofile, dp, i, size;
    674 	char buf[BUFSIZ];
    675 
    676 	itp = inotablookup(ino);
    677 	if (itp == NULL)
    678 		panic("Cannot find directory inode %d named %s\n", ino, name);
    679 	if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) {
    680 		fprintf(stderr, "%s: ", name);
    681 		(void) fflush(stderr);
    682 		fprintf(stderr, "cannot create file: %s\n", strerror(errno));
    683 		return (FAIL);
    684 	}
    685 	rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
    686 	dp = dup(dirp->dd_fd);
    687 	for (i = itp->t_size; i > 0; i -= BUFSIZ) {
    688 		size = i < BUFSIZ ? i : BUFSIZ;
    689 		if (read(dp, buf, (int) size) == -1) {
    690 			fprintf(stderr,
    691 				"write error extracting inode %d, name %s\n",
    692 				curfile.ino, curfile.name);
    693 			fprintf(stderr, "read: %s\n", strerror(errno));
    694 			exit(1);
    695 		}
    696 		if (!Nflag && write(ofile, buf, (int) size) == -1) {
    697 			fprintf(stderr,
    698 				"write error extracting inode %d, name %s\n",
    699 				curfile.ino, curfile.name);
    700 			fprintf(stderr, "write: %s\n", strerror(errno));
    701 			exit(1);
    702 		}
    703 	}
    704 	(void) close(dp);
    705 	(void) close(ofile);
    706 	return (GOOD);
    707 }
    708 
    709 /*
    710  * Determine the type of an inode
    711  */
    712 int
    713 inodetype(ino)
    714 	ino_t ino;
    715 {
    716 	struct inotab *itp;
    717 
    718 	itp = inotablookup(ino);
    719 	if (itp == NULL)
    720 		return (LEAF);
    721 	return (NODE);
    722 }
    723 
    724 /*
    725  * Allocate and initialize a directory inode entry.
    726  * If requested, save its pertinent mode, owner, and time info.
    727  */
    728 static struct inotab *
    729 allocinotab(ino, dip, seekpt)
    730 	ino_t ino;
    731 	struct dinode *dip;
    732 	long seekpt;
    733 {
    734 	register struct inotab	*itp;
    735 	struct modeinfo node;
    736 
    737 	itp = calloc(1, sizeof(struct inotab));
    738 	if (itp == NULL)
    739 		panic("no memory directory table\n");
    740 	itp->t_next = inotab[INOHASH(ino)];
    741 	inotab[INOHASH(ino)] = itp;
    742 	itp->t_ino = ino;
    743 	itp->t_seekpt = seekpt;
    744 	if (mf == NULL)
    745 		return (itp);
    746 	node.ino = ino;
    747 	node.timep[0].tv_sec = dip->di_atime;
    748 	node.timep[0].tv_usec = dip->di_atimensec / 1000;
    749 	node.timep[1].tv_sec = dip->di_mtime;
    750 	node.timep[1].tv_usec = dip->di_mtimensec / 1000;
    751 	node.mode = dip->di_mode;
    752 	node.flags = dip->di_flags;
    753 	node.uid = dip->di_uid;
    754 	node.gid = dip->di_gid;
    755 	(void) fwrite((char *)&node, 1, sizeof(struct modeinfo), mf);
    756 	return (itp);
    757 }
    758 
    759 /*
    760  * Look up an inode in the table of directories
    761  */
    762 static struct inotab *
    763 inotablookup(ino)
    764 	ino_t	ino;
    765 {
    766 	register struct inotab *itp;
    767 
    768 	for (itp = inotab[INOHASH(ino)]; itp != NULL; itp = itp->t_next)
    769 		if (itp->t_ino == ino)
    770 			return (itp);
    771 	return (NULL);
    772 }
    773 
    774 /*
    775  * Clean up and exit
    776  */
    777 void
    778 cleanup()
    779 {
    780 
    781 	closemt();
    782 	if (modefile[0] != '#')
    783 		(void) unlink(modefile);
    784 	if (dirfile[0] != '#')
    785 		(void) unlink(dirfile);
    786 }
    787