xref: /netbsd/sbin/restore/dirs.c (revision bf9ec67e)
1 /*	$NetBSD: dirs.c,v 1.37 2002/05/09 02:55:50 simonb 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.7 (Berkeley) 5/1/95";
45 #else
46 __RCSID("$NetBSD: dirs.c,v 1.37 2002/05/09 02:55:50 simonb 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/ufs/dinode.h>
56 #include <ufs/ufs/dir.h>
57 #include <ufs/ffs/fs.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;
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((FILE *, 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 	FILE *mf;
151 	int i, dfd, mfd;
152 	struct dinode *ip;
153 	struct inotab *itp;
154 	struct direct nulldir;
155 
156 	mf = NULL;
157 	vprintf(stdout, "Extract directories from tape\n");
158 	(void) snprintf(dirfile, sizeof(dirfile), "%s/rstdir%d",
159 	    tmpdir, (int)dumpdate);
160 	if (command != 'r' && command != 'R') {
161 		(void) snprintf(dirfile, sizeof(dirfile), "%s/rstdir%d-XXXXXX",
162 		    tmpdir, (int)dumpdate);
163 		if ((dfd = mkstemp(dirfile)) == -1)
164 			err(1, "cannot mkstemp temporary file %s", dirfile);
165 		df = fdopen(dfd, "w");
166 	}
167 	else
168 		df = fopen(dirfile, "w");
169 	if (df == NULL)
170 		err(1, "cannot open temporary file %s", dirfile);
171 
172 	if (genmode != 0) {
173 		(void) snprintf(modefile, sizeof(modefile), "%s/rstmode%d",
174 		    tmpdir, (int)dumpdate);
175 		if (command != 'r' && command != 'R') {
176 			(void) snprintf(modefile, sizeof(modefile),
177 			    "%s/rstmode%d-XXXXXX", tmpdir, (int)dumpdate);
178 			if ((mfd = mkstemp(modefile)) == -1)
179 				err(1, "cannot mkstemp temporary file %s",
180 				    modefile);
181 			mf = fdopen(mfd, "w");
182 		}
183 		else
184 			mf = fopen(modefile, "w");
185 		if (mf == NULL)
186 			err(1, "cannot open temporary file %s", modefile);
187 	}
188 	nulldir.d_ino = 0;
189 	nulldir.d_type = DT_DIR;
190 	nulldir.d_namlen = 1;
191 	(void) strcpy(nulldir.d_name, "/");
192 	nulldir.d_reclen = DIRSIZ(0, &nulldir, 0);
193 	for (;;) {
194 		curfile.name = "<directory file - name unknown>";
195 		curfile.action = USING;
196 		ip = curfile.dip;
197 		if (ip == NULL || (ip->di_mode & IFMT) != IFDIR) {
198 			(void) fclose(df);
199 			dirp = opendirfile(dirfile);
200 			if (dirp == NULL)
201 				fprintf(stderr, "opendirfile: %s\n",
202 				    strerror(errno));
203 			if (mf != NULL)
204 				(void) fclose(mf);
205 			i = dirlookup(dot);
206 			if (i == 0)
207 				panic("Root directory is not on tape\n");
208 			return;
209 		}
210 		itp = allocinotab(mf, curfile.ino, ip, seekpt);
211 		getfile(putdir, xtrnull);
212 		putent(&nulldir);
213 		flushent();
214 		itp->t_size = seekpt - itp->t_seekpt;
215 	}
216 }
217 
218 /*
219  * skip over all the directories on the tape
220  */
221 void
222 skipdirs()
223 {
224 
225 	while (curfile.dip && (curfile.dip->di_mode & IFMT) == IFDIR) {
226 		skipfile();
227 	}
228 }
229 
230 /*
231  *	Recursively find names and inumbers of all files in subtree
232  *	pname and pass them off to be processed.
233  */
234 void
235 treescan(pname, ino, todo)
236 	char *pname;
237 	ino_t ino;
238 	long (*todo) __P((char *, ino_t, int));
239 {
240 	struct inotab *itp;
241 	struct direct *dp;
242 	int namelen;
243 	long bpt;
244 	char locname[MAXPATHLEN + 1];
245 
246 	itp = inotablookup(ino);
247 	if (itp == NULL) {
248 		/*
249 		 * Pname is name of a simple file or an unchanged directory.
250 		 */
251 		(void) (*todo)(pname, ino, LEAF);
252 		return;
253 	}
254 	/*
255 	 * Pname is a dumped directory name.
256 	 */
257 	if ((*todo)(pname, ino, NODE) == FAIL)
258 		return;
259 	/*
260 	 * begin search through the directory
261 	 * skipping over "." and ".."
262 	 */
263 	(void) snprintf(locname, sizeof(locname), "%s/", pname);
264 	namelen = strlen(locname);
265 	rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
266 	dp = rst_readdir(dirp); /* "." */
267 	if (dp != NULL && strcmp(dp->d_name, ".") == 0)
268 		dp = rst_readdir(dirp); /* ".." */
269 	else
270 		fprintf(stderr, "Warning: `.' missing from directory %s\n",
271 			pname);
272 	if (dp != NULL && strcmp(dp->d_name, "..") == 0)
273 		dp = rst_readdir(dirp); /* first real entry */
274 	else
275 		fprintf(stderr, "Warning: `..' missing from directory %s\n",
276 			pname);
277 	bpt = rst_telldir(dirp);
278 	/*
279 	 * a zero inode signals end of directory
280 	 */
281 	while (dp != NULL) {
282 		locname[namelen] = '\0';
283 		if (namelen + dp->d_namlen >= sizeof(locname)) {
284 			fprintf(stderr, "%s%s: name exceeds %lu char\n",
285 			    locname, dp->d_name, (u_long)(sizeof(locname) - 1));
286 		} else {
287 			(void) strncat(locname, dp->d_name, (int)dp->d_namlen);
288 			locname[namelen + dp->d_namlen] = '\0';
289 			treescan(locname, dp->d_ino, todo);
290 			rst_seekdir(dirp, bpt, itp->t_seekpt);
291 		}
292 		dp = rst_readdir(dirp);
293 		bpt = rst_telldir(dirp);
294 	}
295 }
296 
297 /*
298  * Lookup a pathname which is always assumed to start from the ROOTINO.
299  */
300 struct direct *
301 pathsearch(pathname)
302 	const char *pathname;
303 {
304 	ino_t ino;
305 	struct direct *dp;
306 	char *path, *name, buffer[MAXPATHLEN];
307 
308 	strcpy(buffer, pathname);
309 	path = buffer;
310 	ino = ROOTINO;
311 	while (*path == '/')
312 		path++;
313 	dp = NULL;
314 	while ((name = strsep(&path, "/")) != NULL && *name != '\0') {
315 		if ((dp = searchdir(ino, name)) == NULL)
316 			return (NULL);
317 		ino = dp->d_ino;
318 	}
319 	return (dp);
320 }
321 
322 /*
323  * Lookup the requested name in directory inum.
324  * Return its inode number if found, zero if it does not exist.
325  */
326 static struct direct *
327 searchdir(inum, name)
328 	ino_t	inum;
329 	char	*name;
330 {
331 	struct direct *dp;
332 	struct inotab *itp;
333 	int len;
334 
335 	itp = inotablookup(inum);
336 	if (itp == NULL)
337 		return (NULL);
338 	rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
339 	len = strlen(name);
340 	do {
341 		dp = rst_readdir(dirp);
342 		if (dp == NULL)
343 			return (NULL);
344 	} while (dp->d_namlen != len || strncmp(dp->d_name, name, len) != 0);
345 	return (dp);
346 }
347 
348 /*
349  * Put the directory entries in the directory file
350  */
351 static void
352 putdir(buf, size)
353 	char *buf;
354 	long size;
355 {
356 	struct direct cvtbuf;
357 	struct odirect *odp;
358 	struct odirect *eodp;
359 	struct direct *dp;
360 	long loc, i;
361 
362 	if (cvtflag) {
363 		eodp = (struct odirect *)&buf[size];
364 		for (odp = (struct odirect *)buf; odp < eodp; odp++)
365 			if (odp->d_ino != 0) {
366 				dcvt(odp, &cvtbuf);
367 				putent(&cvtbuf);
368 			}
369 	} else {
370 		for (loc = 0; loc < size; ) {
371 			dp = (struct direct *)(buf + loc);
372 			if (Bcvt)
373 				swabst((u_char *)"ls", (u_char *) dp);
374 			if (oldinofmt && dp->d_ino != 0) {
375 #				if BYTE_ORDER == BIG_ENDIAN
376 					if (Bcvt)
377 						dp->d_namlen = dp->d_type;
378 #				else
379 					if (!Bcvt)
380 						dp->d_namlen = dp->d_type;
381 #				endif
382 				dp->d_type = DT_UNKNOWN;
383 			}
384 			i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1));
385 			if ((dp->d_reclen & 0x3) != 0 ||
386 			    dp->d_reclen > i ||
387 			    dp->d_reclen < DIRSIZ(0, dp, 0) /* ||
388 			    dp->d_namlen > NAME_MAX */) {
389 				vprintf(stdout, "Mangled directory: ");
390 				if ((dp->d_reclen & 0x3) != 0)
391 					vprintf(stdout,
392 					   "reclen not multiple of 4 ");
393 				if (dp->d_reclen < DIRSIZ(0, dp, 0))
394 					vprintf(stdout,
395 					   "reclen less than DIRSIZ (%d < %lu) ",
396 					   dp->d_reclen, (u_long)DIRSIZ(0, dp, 0));
397 #if 0	/* dp->d_namlen is a uint8_t, always < NAME_MAX */
398 				if (dp->d_namlen > NAME_MAX)
399 					vprintf(stdout,
400 					   "reclen name too big (%d > %d) ",
401 					   dp->d_namlen, NAME_MAX);
402 #endif
403 				vprintf(stdout, "\n");
404 				loc += i;
405 				continue;
406 			}
407 			loc += dp->d_reclen;
408 			if (dp->d_ino != 0) {
409 				putent(dp);
410 			}
411 		}
412 	}
413 }
414 
415 /*
416  * These variables are "local" to the following two functions.
417  */
418 char dirbuf[DIRBLKSIZ];
419 long dirloc = 0;
420 long prev = 0;
421 
422 /*
423  * add a new directory entry to a file.
424  */
425 static void
426 putent(dp)
427 	struct direct *dp;
428 {
429 	dp->d_reclen = DIRSIZ(0, dp, 0);
430 	if (dirloc + dp->d_reclen > DIRBLKSIZ) {
431 		((struct direct *)(dirbuf + prev))->d_reclen =
432 		    DIRBLKSIZ - prev;
433 		(void) fwrite(dirbuf, 1, DIRBLKSIZ, df);
434 		dirloc = 0;
435 	}
436 	memmove(dirbuf + dirloc, dp, (long)dp->d_reclen);
437 	prev = dirloc;
438 	dirloc += dp->d_reclen;
439 }
440 
441 /*
442  * flush out a directory that is finished.
443  */
444 static void
445 flushent()
446 {
447 	((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev;
448 	(void) fwrite(dirbuf, (int)dirloc, 1, df);
449 	seekpt = ftell(df);
450 	dirloc = 0;
451 }
452 
453 static void
454 dcvt(odp, ndp)
455 	struct odirect *odp;
456 	struct direct *ndp;
457 {
458 
459 	memset(ndp, 0, (size_t)(sizeof *ndp));
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.timep);
649 				(void) chown(cp, node.uid, node.gid);
650 				(void) chmod(cp, node.mode);
651 				(void) chflags(cp, node.flags);
652 			}
653 			ep->e_flags &= ~NEW;
654 		}
655 	}
656 	if (ferror(mf))
657 		panic("error setting directory modes\n");
658 	(void) fclose(mf);
659 }
660 
661 /*
662  * Generate a literal copy of a directory.
663  */
664 int
665 genliteraldir(name, ino)
666 	char *name;
667 	ino_t ino;
668 {
669 	struct inotab *itp;
670 	int ofile, dp, i, size;
671 	char buf[BUFSIZ];
672 
673 	itp = inotablookup(ino);
674 	if (itp == NULL)
675 		panic("Cannot find directory inode %d named %s\n", ino, name);
676 	if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) {
677 		fprintf(stderr, "%s: ", name);
678 		(void) fflush(stderr);
679 		fprintf(stderr, "cannot create file: %s\n", strerror(errno));
680 		return (FAIL);
681 	}
682 	rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
683 	dp = dup(dirp->dd_fd);
684 	for (i = itp->t_size; i > 0; i -= BUFSIZ) {
685 		size = i < BUFSIZ ? i : BUFSIZ;
686 		if (read(dp, buf, (int) size) == -1) {
687 			fprintf(stderr,
688 				"write error extracting inode %d, name %s\n",
689 				curfile.ino, curfile.name);
690 			fprintf(stderr, "read: %s\n", strerror(errno));
691 			exit(1);
692 		}
693 		if (!Nflag && write(ofile, buf, (int) size) == -1) {
694 			fprintf(stderr,
695 				"write error extracting inode %d, name %s\n",
696 				curfile.ino, curfile.name);
697 			fprintf(stderr, "write: %s\n", strerror(errno));
698 			exit(1);
699 		}
700 	}
701 	(void) close(dp);
702 	(void) close(ofile);
703 	return (GOOD);
704 }
705 
706 /*
707  * Determine the type of an inode
708  */
709 int
710 inodetype(ino)
711 	ino_t ino;
712 {
713 	struct inotab *itp;
714 
715 	itp = inotablookup(ino);
716 	if (itp == NULL)
717 		return (LEAF);
718 	return (NODE);
719 }
720 
721 /*
722  * Allocate and initialize a directory inode entry.
723  * If requested, save its pertinent mode, owner, and time info.
724  */
725 static struct inotab *
726 allocinotab(mf, ino, dip, aseekpt)
727 	FILE *mf;
728 	ino_t ino;
729 	struct dinode *dip;
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(ino)];
739 	inotab[INOHASH(ino)] = itp;
740 	itp->t_ino = ino;
741 	itp->t_seekpt = aseekpt;
742 	if (mf == NULL)
743 		return (itp);
744 	node.ino = ino;
745 	node.timep[0].tv_sec = dip->di_atime;
746 	node.timep[0].tv_usec = dip->di_atimensec / 1000;
747 	node.timep[1].tv_sec = dip->di_mtime;
748 	node.timep[1].tv_usec = dip->di_mtimensec / 1000;
749 	node.mode = dip->di_mode;
750 	node.flags = dip->di_flags;
751 	node.uid = dip->di_uid;
752 	node.gid = dip->di_gid;
753 	(void) fwrite((char *)&node, 1, sizeof(struct modeinfo), mf);
754 	return (itp);
755 }
756 
757 /*
758  * Look up an inode in the table of directories
759  */
760 static struct inotab *
761 inotablookup(ino)
762 	ino_t	ino;
763 {
764 	struct inotab *itp;
765 
766 	for (itp = inotab[INOHASH(ino)]; itp != NULL; itp = itp->t_next)
767 		if (itp->t_ino == ino)
768 			return (itp);
769 	return (NULL);
770 }
771 
772 /*
773  * Clean up and exit
774  */
775 void
776 cleanup()
777 {
778 
779 	closemt();
780 	if (modefile[0] != '#')
781 		(void) unlink(modefile);
782 	if (dirfile[0] != '#')
783 		(void) unlink(dirfile);
784 }
785