xref: /netbsd-src/sbin/restore/dirs.c (revision de1dfb1250df962f1ff3a011772cf58e605aed11)
1 /*	$NetBSD: dirs.c,v 1.40 2003/11/05 22:27:16 fvdl 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. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 #include <sys/cdefs.h>
38 #ifndef lint
39 #if 0
40 static char sccsid[] = "@(#)dirs.c	8.7 (Berkeley) 5/1/95";
41 #else
42 __RCSID("$NetBSD: dirs.c,v 1.40 2003/11/05 22:27:16 fvdl Exp $");
43 #endif
44 #endif /* not lint */
45 
46 #include <sys/param.h>
47 #include <sys/file.h>
48 #include <sys/stat.h>
49 #include <sys/time.h>
50 
51 #include <ufs/ufs/dinode.h>
52 #include <ufs/ufs/dir.h>
53 #include <ufs/ffs/fs.h>
54 #include <protocols/dumprestore.h>
55 
56 #include <err.h>
57 #include <errno.h>
58 #include <paths.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <unistd.h>
63 
64 #include <machine/endian.h>
65 
66 #include "restore.h"
67 #include "extern.h"
68 
69 /*
70  * Symbol table of directories read from tape.
71  */
72 #define HASHSIZE	1000
73 #define INOHASH(val) (val % HASHSIZE)
74 struct inotab {
75 	struct	inotab *t_next;
76 	ino_t	t_ino;
77 	int32_t	t_seekpt;
78 	int32_t	t_size;
79 };
80 static struct inotab *inotab[HASHSIZE];
81 
82 /*
83  * Information retained about directories.
84  */
85 struct modeinfo {
86 	ino_t ino;
87 	struct timeval ctimep[2];
88 	struct timeval mtimep[2];
89 	mode_t mode;
90 	uid_t uid;
91 	gid_t gid;
92 	int flags;
93 };
94 
95 /*
96  * Definitions for library routines operating on directories.
97  */
98 #undef DIRBLKSIZ
99 #define DIRBLKSIZ 1024
100 struct rstdirdesc {
101 	int	dd_fd;
102 	int32_t	dd_loc;
103 	int32_t	dd_size;
104 	char	dd_buf[DIRBLKSIZ];
105 };
106 
107 /*
108  * Global variables for this file.
109  */
110 static long	seekpt;
111 static FILE	*df;
112 static RST_DIR	*dirp;
113 static char	dirfile[MAXPATHLEN] = "#";	/* No file */
114 static char	modefile[MAXPATHLEN] = "#";	/* No file */
115 static char	dot[2] = ".";			/* So it can be modified */
116 
117 /*
118  * Format of old style directories.
119  */
120 #define ODIRSIZ 14
121 struct odirect {
122 	u_short	d_ino;
123 	char	d_name[ODIRSIZ];
124 };
125 
126 static struct inotab	*allocinotab __P((FILE *, struct context *, long));
127 static void		 dcvt __P((struct odirect *, struct direct *));
128 static void		 flushent __P((void));
129 static struct inotab	*inotablookup __P((ino_t));
130 static RST_DIR		*opendirfile __P((const char *));
131 static void		 putdir __P((char *, long));
132 static void		 putent __P((struct direct *));
133 static void		 rst_seekdir __P((RST_DIR *, long, long));
134 static long		 rst_telldir __P((RST_DIR *));
135 static struct direct	*searchdir __P((ino_t, char *));
136 
137 /*
138  *	Extract directory contents, building up a directory structure
139  *	on disk for extraction by name.
140  *	If genmode is requested, save mode, owner, and times for all
141  *	directories on the tape.
142  */
143 void
144 extractdirs(genmode)
145 	int genmode;
146 {
147 	FILE *mf;
148 	int i, dfd, mfd;
149 	struct inotab *itp;
150 	struct direct nulldir;
151 
152 	mf = NULL;
153 	vprintf(stdout, "Extract directories from tape\n");
154 	(void) snprintf(dirfile, sizeof(dirfile), "%s/rstdir%d",
155 	    tmpdir, (int)dumpdate);
156 	if (command != 'r' && command != 'R') {
157 		(void) snprintf(dirfile, sizeof(dirfile), "%s/rstdir%d-XXXXXX",
158 		    tmpdir, (int)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 		    tmpdir, (int)dumpdate);
171 		if (command != 'r' && command != 'R') {
172 			(void) snprintf(modefile, sizeof(modefile),
173 			    "%s/rstmode%d-XXXXXX", tmpdir, (int)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, 0);
189 	for (;;) {
190 		curfile.name = "<directory file - name unknown>";
191 		curfile.action = USING;
192 		if (curfile.mode == 0 || (curfile.mode & IFMT) != IFDIR) {
193 			(void) fclose(df);
194 			dirp = opendirfile(dirfile);
195 			if (dirp == NULL)
196 				fprintf(stderr, "opendirfile: %s\n",
197 				    strerror(errno));
198 			if (mf != NULL)
199 				(void) fclose(mf);
200 			i = dirlookup(dot);
201 			if (i == 0)
202 				panic("Root directory is not on tape\n");
203 			return;
204 		}
205 		itp = allocinotab(mf, &curfile, seekpt);
206 		getfile(putdir, xtrnull);
207 		putent(&nulldir);
208 		flushent();
209 		itp->t_size = seekpt - itp->t_seekpt;
210 	}
211 }
212 
213 /*
214  * skip over all the directories on the tape
215  */
216 void
217 skipdirs()
218 {
219 
220 	while (curfile.ino && (curfile.mode & IFMT) == IFDIR) {
221 		skipfile();
222 	}
223 }
224 
225 /*
226  *	Recursively find names and inumbers of all files in subtree
227  *	pname and pass them off to be processed.
228  */
229 void
230 treescan(pname, ino, todo)
231 	char *pname;
232 	ino_t ino;
233 	long (*todo) __P((char *, ino_t, int));
234 {
235 	struct inotab *itp;
236 	struct direct *dp;
237 	int namelen;
238 	long bpt;
239 	char locname[MAXPATHLEN + 1];
240 
241 	itp = inotablookup(ino);
242 	if (itp == NULL) {
243 		/*
244 		 * Pname is name of a simple file or an unchanged directory.
245 		 */
246 		(void) (*todo)(pname, ino, LEAF);
247 		return;
248 	}
249 	/*
250 	 * Pname is a dumped directory name.
251 	 */
252 	if ((*todo)(pname, ino, NODE) == FAIL)
253 		return;
254 	/*
255 	 * begin search through the directory
256 	 * skipping over "." and ".."
257 	 */
258 	(void) snprintf(locname, sizeof(locname), "%s/", pname);
259 	namelen = strlen(locname);
260 	rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
261 	dp = rst_readdir(dirp); /* "." */
262 	if (dp != NULL && strcmp(dp->d_name, ".") == 0)
263 		dp = rst_readdir(dirp); /* ".." */
264 	else
265 		fprintf(stderr, "Warning: `.' missing from directory %s\n",
266 			pname);
267 	if (dp != NULL && strcmp(dp->d_name, "..") == 0)
268 		dp = rst_readdir(dirp); /* first real entry */
269 	else
270 		fprintf(stderr, "Warning: `..' missing from directory %s\n",
271 			pname);
272 	bpt = rst_telldir(dirp);
273 	/*
274 	 * a zero inode signals end of directory
275 	 */
276 	while (dp != NULL) {
277 		locname[namelen] = '\0';
278 		if (namelen + dp->d_namlen >= sizeof(locname)) {
279 			fprintf(stderr, "%s%s: name exceeds %lu char\n",
280 			    locname, dp->d_name, (u_long)(sizeof(locname) - 1));
281 		} else {
282 			(void) strncat(locname, dp->d_name, (int)dp->d_namlen);
283 			locname[namelen + dp->d_namlen] = '\0';
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 				dp->d_ino = bswap32(dp->d_ino);
369 				dp->d_reclen = bswap16(dp->d_reclen);
370 			}
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, 0) /* ||
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, 0))
391 					vprintf(stdout,
392 					   "reclen less than DIRSIZ (%d < %lu) ",
393 					   dp->d_reclen, (u_long)DIRSIZ(0, dp, 0));
394 #if 0	/* dp->d_namlen is a uint8_t, always < NAME_MAX */
395 				if (dp->d_namlen > NAME_MAX)
396 					vprintf(stdout,
397 					   "reclen name too big (%d > %d) ",
398 					   dp->d_namlen, NAME_MAX);
399 #endif
400 				vprintf(stdout, "\n");
401 				loc += i;
402 				continue;
403 			}
404 			loc += dp->d_reclen;
405 			if (dp->d_ino != 0) {
406 				putent(dp);
407 			}
408 		}
409 	}
410 }
411 
412 /*
413  * These variables are "local" to the following two functions.
414  */
415 char dirbuf[DIRBLKSIZ];
416 long dirloc = 0;
417 long prev = 0;
418 
419 /*
420  * add a new directory entry to a file.
421  */
422 static void
423 putent(dp)
424 	struct direct *dp;
425 {
426 	dp->d_reclen = DIRSIZ(0, dp, 0);
427 	if (dirloc + dp->d_reclen > DIRBLKSIZ) {
428 		((struct direct *)(dirbuf + prev))->d_reclen =
429 		    DIRBLKSIZ - prev;
430 		(void) fwrite(dirbuf, 1, DIRBLKSIZ, df);
431 		dirloc = 0;
432 	}
433 	memmove(dirbuf + dirloc, dp, (long)dp->d_reclen);
434 	prev = dirloc;
435 	dirloc += dp->d_reclen;
436 }
437 
438 /*
439  * flush out a directory that is finished.
440  */
441 static void
442 flushent()
443 {
444 	((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev;
445 	(void) fwrite(dirbuf, (int)dirloc, 1, df);
446 	seekpt = ftell(df);
447 	dirloc = 0;
448 }
449 
450 static void
451 dcvt(odp, ndp)
452 	struct odirect *odp;
453 	struct direct *ndp;
454 {
455 
456 	memset(ndp, 0, (size_t)(sizeof *ndp));
457 	if (Bcvt)
458 		ndp->d_ino = bswap16(odp->d_ino);
459 	else
460 		ndp->d_ino = odp->d_ino;
461 	ndp->d_type = DT_UNKNOWN;
462 	(void) strncpy(ndp->d_name, odp->d_name, ODIRSIZ);
463 	ndp->d_namlen = strlen(ndp->d_name);
464 	ndp->d_reclen = DIRSIZ(0, ndp, 0);
465 }
466 
467 /*
468  * Seek to an entry in a directory.
469  * Only values returned by rst_telldir should be passed to rst_seekdir.
470  * This routine handles many directories in a single file.
471  * It takes the base of the directory in the file, plus
472  * the desired seek offset into it.
473  */
474 static void
475 rst_seekdir(rdirp, loc, base)
476 	RST_DIR *rdirp;
477 	long loc, base;
478 {
479 
480 	if (loc == rst_telldir(rdirp))
481 		return;
482 	loc -= base;
483 	if (loc < 0)
484 		fprintf(stderr, "bad seek pointer to rst_seekdir %d\n",
485 		    (int)loc);
486 	(void) lseek(rdirp->dd_fd, base + (loc & ~(DIRBLKSIZ - 1)), SEEK_SET);
487 	rdirp->dd_loc = loc & (DIRBLKSIZ - 1);
488 	if (rdirp->dd_loc != 0)
489 		rdirp->dd_size = read(rdirp->dd_fd, rdirp->dd_buf, DIRBLKSIZ);
490 }
491 
492 /*
493  * get next entry in a directory.
494  */
495 struct direct *
496 rst_readdir(rdirp)
497 	RST_DIR *rdirp;
498 {
499 	struct direct *dp;
500 
501 	for (;;) {
502 		if (rdirp->dd_loc == 0) {
503 			rdirp->dd_size = read(rdirp->dd_fd, rdirp->dd_buf,
504 			    DIRBLKSIZ);
505 			if (rdirp->dd_size <= 0) {
506 				dprintf(stderr, "error reading directory\n");
507 				return (NULL);
508 			}
509 		}
510 		if (rdirp->dd_loc >= rdirp->dd_size) {
511 			rdirp->dd_loc = 0;
512 			continue;
513 		}
514 		dp = (struct direct *)(rdirp->dd_buf + rdirp->dd_loc);
515 		if (dp->d_reclen == 0 ||
516 		    dp->d_reclen > DIRBLKSIZ + 1 - rdirp->dd_loc) {
517 			dprintf(stderr, "corrupted directory: bad reclen %d\n",
518 				dp->d_reclen);
519 			return (NULL);
520 		}
521 		rdirp->dd_loc += dp->d_reclen;
522 		if (dp->d_ino == 0 && strcmp(dp->d_name, "/") == 0)
523 			return (NULL);
524 		if (dp->d_ino >= maxino) {
525 			dprintf(stderr, "corrupted directory: bad inum %d\n",
526 				dp->d_ino);
527 			continue;
528 		}
529 		return (dp);
530 	}
531 }
532 
533 /*
534  * Simulate the opening of a directory
535  */
536 RST_DIR *
537 rst_opendir(name)
538 	const char *name;
539 {
540 	struct inotab *itp;
541 	RST_DIR *rdirp;
542 	ino_t ino;
543 
544 	if ((ino = dirlookup(name)) > 0 &&
545 	    (itp = inotablookup(ino)) != NULL) {
546 		rdirp = opendirfile(dirfile);
547 		rst_seekdir(rdirp, itp->t_seekpt, itp->t_seekpt);
548 		return (rdirp);
549 	}
550 	return (NULL);
551 }
552 
553 /*
554  * In our case, there is nothing to do when closing a directory.
555  */
556 void
557 rst_closedir(rdirp)
558 	RST_DIR *rdirp;
559 {
560 
561 	(void)close(rdirp->dd_fd);
562 	free(rdirp);
563 	return;
564 }
565 
566 /*
567  * Simulate finding the current offset in the directory.
568  */
569 static long
570 rst_telldir(rdirp)
571 	RST_DIR *rdirp;
572 {
573 	return ((long)lseek(rdirp->dd_fd,
574 	    (off_t)0, SEEK_CUR) - rdirp->dd_size + rdirp->dd_loc);
575 }
576 
577 /*
578  * Open a directory file.
579  */
580 static RST_DIR *
581 opendirfile(name)
582 	const char *name;
583 {
584 	RST_DIR *rdirp;
585 	int fd;
586 
587 	if ((fd = open(name, O_RDONLY)) == -1)
588 		return (NULL);
589 	if ((rdirp = malloc(sizeof(RST_DIR))) == NULL) {
590 		(void)close(fd);
591 		return (NULL);
592 	}
593 	rdirp->dd_fd = fd;
594 	rdirp->dd_loc = 0;
595 	return (rdirp);
596 }
597 
598 /*
599  * Set the mode, owner, and times for all new or changed directories
600  */
601 void
602 setdirmodes(flags)
603 	int flags;
604 {
605 	FILE *mf;
606 	struct modeinfo node;
607 	struct entry *ep;
608 	char *cp;
609 
610 	vprintf(stdout, "Set directory mode, owner, and times.\n");
611 	if (command == 'r' || command == 'R')
612 		(void) snprintf(modefile, sizeof(modefile), "%s/rstmode%d",
613 		    tmpdir, (int)dumpdate);
614 	if (modefile[0] == '#') {
615 		panic("modefile not defined\n");
616 		fprintf(stderr, "directory mode, owner, and times not set\n");
617 		return;
618 	}
619 	mf = fopen(modefile, "r");
620 	if (mf == NULL) {
621 		fprintf(stderr, "fopen: %s\n", strerror(errno));
622 		fprintf(stderr, "cannot open mode file %s\n", modefile);
623 		fprintf(stderr, "directory mode, owner, and times not set\n");
624 		return;
625 	}
626 	clearerr(mf);
627 	for (;;) {
628 		(void) fread((char *)&node, 1, sizeof(struct modeinfo), mf);
629 		if (feof(mf))
630 			break;
631 		ep = lookupino(node.ino);
632 		if (command == 'i' || command == 'x') {
633 			if (ep == NULL)
634 				continue;
635 			if ((flags & FORCE) == 0 && ep->e_flags & EXISTED) {
636 				ep->e_flags &= ~NEW;
637 				continue;
638 			}
639 			if (node.ino == ROOTINO &&
640 		   	    reply("set owner/mode for '.'") == FAIL)
641 				continue;
642 		}
643 		if (ep == NULL) {
644 			panic("cannot find directory inode %d\n", node.ino);
645 		} else {
646 			if (!Nflag) {
647 				cp = myname(ep);
648 				(void) utimes(cp, node.ctimep);
649 				(void) utimes(cp, node.mtimep);
650 				(void) chown(cp, node.uid, node.gid);
651 				(void) chmod(cp, node.mode);
652 				(void) chflags(cp, node.flags);
653 			}
654 			ep->e_flags &= ~NEW;
655 		}
656 	}
657 	if (ferror(mf))
658 		panic("error setting directory modes\n");
659 	(void) fclose(mf);
660 }
661 
662 /*
663  * Generate a literal copy of a directory.
664  */
665 int
666 genliteraldir(name, ino)
667 	char *name;
668 	ino_t ino;
669 {
670 	struct inotab *itp;
671 	int ofile, dp, i, size;
672 	char buf[BUFSIZ];
673 
674 	itp = inotablookup(ino);
675 	if (itp == NULL)
676 		panic("Cannot find directory inode %d named %s\n", ino, name);
677 	if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) {
678 		fprintf(stderr, "%s: ", name);
679 		(void) fflush(stderr);
680 		fprintf(stderr, "cannot create file: %s\n", strerror(errno));
681 		return (FAIL);
682 	}
683 	rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
684 	dp = dup(dirp->dd_fd);
685 	for (i = itp->t_size; i > 0; i -= BUFSIZ) {
686 		size = i < BUFSIZ ? i : BUFSIZ;
687 		if (read(dp, buf, (int) size) == -1) {
688 			fprintf(stderr,
689 				"write error extracting inode %d, name %s\n",
690 				curfile.ino, curfile.name);
691 			fprintf(stderr, "read: %s\n", strerror(errno));
692 			exit(1);
693 		}
694 		if (!Nflag && write(ofile, buf, (int) size) == -1) {
695 			fprintf(stderr,
696 				"write error extracting inode %d, name %s\n",
697 				curfile.ino, curfile.name);
698 			fprintf(stderr, "write: %s\n", strerror(errno));
699 			exit(1);
700 		}
701 	}
702 	(void) close(dp);
703 	(void) close(ofile);
704 	return (GOOD);
705 }
706 
707 /*
708  * Determine the type of an inode
709  */
710 int
711 inodetype(ino)
712 	ino_t ino;
713 {
714 	struct inotab *itp;
715 
716 	itp = inotablookup(ino);
717 	if (itp == NULL)
718 		return (LEAF);
719 	return (NODE);
720 }
721 
722 /*
723  * Allocate and initialize a directory inode entry.
724  * If requested, save its pertinent mode, owner, and time info.
725  */
726 static struct inotab *
727 allocinotab(mf, ctxp, aseekpt)
728 	FILE *mf;
729 	struct context *ctxp;
730 	long aseekpt;
731 {
732 	struct inotab	*itp;
733 	struct modeinfo node;
734 
735 	itp = calloc(1, sizeof(struct inotab));
736 	if (itp == NULL)
737 		panic("no memory directory table\n");
738 	itp->t_next = inotab[INOHASH(ctxp->ino)];
739 	inotab[INOHASH(ctxp->ino)] = itp;
740 	itp->t_ino = ctxp->ino;
741 	itp->t_seekpt = aseekpt;
742 	if (mf == NULL)
743 		return (itp);
744 	node.ino = ctxp->ino;
745 	node.mtimep[0].tv_sec = ctxp->atime_sec;
746 	node.mtimep[0].tv_usec = ctxp->atime_nsec / 1000;
747 	node.mtimep[1].tv_sec = ctxp->mtime_sec;
748 	node.mtimep[1].tv_usec = ctxp->mtime_nsec / 1000;
749 	node.ctimep[0].tv_sec = ctxp->atime_sec;
750 	node.ctimep[0].tv_usec = ctxp->atime_nsec / 1000;
751 	node.ctimep[1].tv_sec = ctxp->birthtime_sec;
752 	node.ctimep[1].tv_usec = ctxp->birthtime_nsec / 1000;
753 	node.mode = ctxp->mode;
754 	node.flags = ctxp->file_flags;
755 	node.uid = ctxp->uid;
756 	node.gid = ctxp->gid;
757 	(void) fwrite((char *)&node, 1, sizeof(struct modeinfo), mf);
758 	return (itp);
759 }
760 
761 /*
762  * Look up an inode in the table of directories
763  */
764 static struct inotab *
765 inotablookup(ino)
766 	ino_t	ino;
767 {
768 	struct inotab *itp;
769 
770 	for (itp = inotab[INOHASH(ino)]; itp != NULL; itp = itp->t_next)
771 		if (itp->t_ino == ino)
772 			return (itp);
773 	return (NULL);
774 }
775 
776 /*
777  * Clean up and exit
778  */
779 void
780 cleanup()
781 {
782 
783 	closemt();
784 	if (modefile[0] != '#')
785 		(void) unlink(modefile);
786 	if (dirfile[0] != '#')
787 		(void) unlink(dirfile);
788 }
789