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