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