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