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