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