xref: /openbsd/sbin/restore/dirs.c (revision d485f761)
1 /*	$OpenBSD: dirs.c,v 1.18 2001/11/05 07:39:17 mpech Exp $	*/
2 /*	$NetBSD: dirs.c,v 1.26 1997/07/01 05:37:49 lukem Exp $	*/
3 
4 /*
5  * Copyright (c) 1983, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  * (c) UNIX System Laboratories, Inc.
8  * All or some portions of this file are derived from material licensed
9  * to the University of California by American Telephone and Telegraph
10  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
11  * the permission of UNIX System Laboratories, Inc.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. All advertising materials mentioning features or use of this software
22  *    must display the following acknowledgement:
23  *	This product includes software developed by the University of
24  *	California, Berkeley and its contributors.
25  * 4. Neither the name of the University nor the names of its contributors
26  *    may be used to endorse or promote products derived from this software
27  *    without specific prior written permission.
28  *
29  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39  * SUCH DAMAGE.
40  */
41 
42 #ifndef lint
43 #if 0
44 static char sccsid[] = "@(#)dirs.c	8.5 (Berkeley) 8/31/94";
45 #else
46 static char rcsid[] = "$OpenBSD: dirs.c,v 1.18 2001/11/05 07:39:17 mpech Exp $";
47 #endif
48 #endif /* not lint */
49 
50 #include <sys/param.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 <fcntl.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 	u_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;
150 	struct dinode *ip;
151 	struct inotab *itp;
152 	struct direct nulldir;
153 	int fd;
154 
155 	Vprintf(stdout, "Extract directories from tape\n");
156 	(void)snprintf(dirfile, sizeof(dirfile), "%s/rstdir%d", tmpdir,
157 	    dumpdate);
158 	if (command != 'r' && command != 'R') {
159 		strncat(dirfile, "-XXXXXXXXXX",
160 		    sizeof(dirfile) - strlen(dirfile));
161 		fd = mkstemp(dirfile);
162 	} else
163 		fd = open(dirfile, O_RDWR|O_CREAT|O_EXCL, 0666);
164 	if (fd == -1 || (df = fdopen(fd, "w")) == NULL) {
165 		if (fd != -1)
166 			close(fd);
167 		err(1, "cannot create directory temporary %s", dirfile);
168 	}
169 	if (genmode != 0) {
170 		(void)snprintf(modefile, sizeof(modefile), "%s/rstmode%d",
171 		    tmpdir, dumpdate);
172 		if (command != 'r' && command != 'R') {
173 			strncat(modefile, "-XXXXXXXXXX",
174 			    sizeof(modefile) - strlen(modefile));
175 			fd = mkstemp(modefile);
176 		} else
177 			fd = open(modefile, O_RDWR|O_CREAT|O_EXCL, 0666);
178 		if (fd == -1 || (mf = fdopen(fd, "w")) == NULL) {
179 			if (fd != -1)
180 				close(fd);
181 			err(1, "cannot create modefile %s", modefile);
182 		}
183 	}
184 	nulldir.d_ino = 0;
185 	nulldir.d_type = DT_DIR;
186 	nulldir.d_namlen = 1;
187 	nulldir.d_name[0] = '/';
188 	nulldir.d_name[1] = '\0';
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 				warn("opendirfile");
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 	namelen = snprintf(locname, sizeof(locname), "%s/", pname);
260 	if (namelen >= sizeof(locname))
261 		namelen = sizeof(locname) - 1;
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 			treescan(locname, dp->d_ino, todo);
286 			rst_seekdir(dirp, bpt, itp->t_seekpt);
287 		}
288 		dp = rst_readdir(dirp);
289 		bpt = rst_telldir(dirp);
290 	}
291 }
292 
293 /*
294  * Lookup a pathname which is always assumed to start from the ROOTINO.
295  */
296 struct direct *
297 pathsearch(pathname)
298 	const char *pathname;
299 {
300 	ino_t ino;
301 	struct direct *dp;
302 	char *path, *name, buffer[MAXPATHLEN];
303 
304 	strcpy(buffer, pathname);
305 	path = buffer;
306 	ino = ROOTINO;
307 	while (*path == '/')
308 		path++;
309 	dp = NULL;
310 	while ((name = strsep(&path, "/")) != NULL && *name != '\0') {
311 		if ((dp = searchdir(ino, name)) == NULL)
312 			return (NULL);
313 		ino = dp->d_ino;
314 	}
315 	return (dp);
316 }
317 
318 /*
319  * Lookup the requested name in directory inum.
320  * Return its inode number if found, zero if it does not exist.
321  */
322 static struct direct *
323 searchdir(inum, name)
324 	ino_t	inum;
325 	char	*name;
326 {
327 	struct direct *dp;
328 	struct inotab *itp;
329 	int len;
330 
331 	itp = inotablookup(inum);
332 	if (itp == NULL)
333 		return (NULL);
334 	rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
335 	len = strlen(name);
336 	do {
337 		dp = rst_readdir(dirp);
338 		if (dp == NULL)
339 			return (NULL);
340 	} while (dp->d_namlen != len || strncmp(dp->d_name, name, len) != 0);
341 	return (dp);
342 }
343 
344 /*
345  * Put the directory entries in the directory file
346  */
347 static void
348 putdir(buf, size)
349 	char *buf;
350 	long size;
351 {
352 	struct direct cvtbuf;
353 	struct odirect *odp;
354 	struct odirect *eodp;
355 	struct direct *dp;
356 	long loc, i;
357 
358 	if (cvtflag) {
359 		eodp = (struct odirect *)&buf[size];
360 		for (odp = (struct odirect *)buf; odp < eodp; odp++)
361 			if (odp->d_ino != 0) {
362 				dcvt(odp, &cvtbuf);
363 				putent(&cvtbuf);
364 			}
365 	} else {
366 		for (loc = 0; loc < size; ) {
367 			dp = (struct direct *)(buf + loc);
368 			if (Bcvt)
369 				swabst((u_char *)"ls", (u_char *) dp);
370 			if (oldinofmt && dp->d_ino != 0) {
371 #				if BYTE_ORDER == BIG_ENDIAN
372 					if (Bcvt)
373 						dp->d_namlen = dp->d_type;
374 #				else
375 					if (!Bcvt)
376 						dp->d_namlen = dp->d_type;
377 #				endif
378 				dp->d_type = DT_UNKNOWN;
379 			}
380 			i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1));
381 			if ((dp->d_reclen & 0x3) != 0 ||
382 			    dp->d_reclen > i ||
383 			    dp->d_reclen < DIRSIZ(0, dp) ||
384 			    dp->d_namlen > NAME_MAX) {
385 				Vprintf(stdout, "Mangled directory: ");
386 				if ((dp->d_reclen & 0x3) != 0)
387 					Vprintf(stdout,
388 					   "reclen not multiple of 4 ");
389 				if (dp->d_reclen < DIRSIZ(0, dp))
390 					Vprintf(stdout,
391 					   "reclen less than DIRSIZ (%d < %d) ",
392 					   dp->d_reclen, DIRSIZ(0, dp));
393 				if (dp->d_namlen > NAME_MAX)
394 					Vprintf(stdout,
395 					   "reclen name too big (%d > %d) ",
396 					   dp->d_namlen, NAME_MAX);
397 				Vprintf(stdout, "\n");
398 				loc += i;
399 				continue;
400 			}
401 			loc += dp->d_reclen;
402 			if (dp->d_ino != 0) {
403 				putent(dp);
404 			}
405 		}
406 	}
407 }
408 
409 /*
410  * These variables are "local" to the following two functions.
411  */
412 char dirbuf[DIRBLKSIZ];
413 long dirloc = 0;
414 long prev = 0;
415 
416 /*
417  * add a new directory entry to a file.
418  */
419 static void
420 putent(dp)
421 	struct direct *dp;
422 {
423 	dp->d_reclen = DIRSIZ(0, dp);
424 	if (dirloc + dp->d_reclen > DIRBLKSIZ) {
425 		((struct direct *)(dirbuf + prev))->d_reclen =
426 		    DIRBLKSIZ - prev;
427 		(void)fwrite(dirbuf, 1, DIRBLKSIZ, df);
428 		dirloc = 0;
429 	}
430 	memcpy(dirbuf + dirloc, dp, (long)dp->d_reclen);
431 	prev = dirloc;
432 	dirloc += dp->d_reclen;
433 }
434 
435 /*
436  * flush out a directory that is finished.
437  */
438 static void
439 flushent()
440 {
441 	((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev;
442 	(void)fwrite(dirbuf, (int)dirloc, 1, df);
443 	seekpt = ftell(df);
444 	dirloc = 0;
445 }
446 
447 static void
448 dcvt(odp, ndp)
449 	struct odirect *odp;
450 	struct direct *ndp;
451 {
452 
453 	memset(ndp, 0, (size_t)(sizeof *ndp));
454 	ndp->d_ino =  odp->d_ino;
455 	ndp->d_type = DT_UNKNOWN;
456 	(void)strncpy(ndp->d_name, odp->d_name, ODIRSIZ);
457 	ndp->d_namlen = strlen(ndp->d_name);
458 	ndp->d_reclen = DIRSIZ(0, ndp);
459 }
460 
461 /*
462  * Seek to an entry in a directory.
463  * Only values returned by rst_telldir should be passed to rst_seekdir.
464  * This routine handles many directories in a single file.
465  * It takes the base of the directory in the file, plus
466  * the desired seek offset into it.
467  */
468 static void
469 rst_seekdir(dirp, loc, base)
470 	RST_DIR *dirp;
471 	long loc, base;
472 {
473 
474 	if (loc == rst_telldir(dirp))
475 		return;
476 	loc -= base;
477 	if (loc < 0)
478 		fprintf(stderr, "bad seek pointer to rst_seekdir %ld\n", loc);
479 	(void)lseek(dirp->dd_fd, base + (loc & ~(DIRBLKSIZ - 1)), SEEK_SET);
480 	dirp->dd_loc = loc & (DIRBLKSIZ - 1);
481 	if (dirp->dd_loc != 0)
482 		dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ);
483 }
484 
485 /*
486  * get next entry in a directory.
487  */
488 struct direct *
489 rst_readdir(dirp)
490 	RST_DIR *dirp;
491 {
492 	struct direct *dp;
493 
494 	for (;;) {
495 		if (dirp->dd_loc == 0) {
496 			dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf,
497 			    DIRBLKSIZ);
498 			if (dirp->dd_size <= 0) {
499 				Dprintf(stderr, "error reading directory\n");
500 				return (NULL);
501 			}
502 		}
503 		if (dirp->dd_loc >= dirp->dd_size) {
504 			dirp->dd_loc = 0;
505 			continue;
506 		}
507 		dp = (struct direct *)(dirp->dd_buf + dirp->dd_loc);
508 		if (dp->d_reclen == 0 ||
509 		    dp->d_reclen > DIRBLKSIZ + 1 - dirp->dd_loc) {
510 			Dprintf(stderr, "corrupted directory: bad reclen %d\n",
511 				dp->d_reclen);
512 			return (NULL);
513 		}
514 		dirp->dd_loc += dp->d_reclen;
515 		if (dp->d_ino == 0 && strcmp(dp->d_name, "/") == 0)
516 			return (NULL);
517 		if (dp->d_ino >= maxino) {
518 			Dprintf(stderr, "corrupted directory: bad inum %d\n",
519 				dp->d_ino);
520 			continue;
521 		}
522 		return (dp);
523 	}
524 }
525 
526 /*
527  * Simulate the opening of a directory
528  */
529 RST_DIR *
530 rst_opendir(name)
531 	const char *name;
532 {
533 	struct inotab *itp;
534 	RST_DIR *dirp;
535 	ino_t ino;
536 
537 	if ((ino = dirlookup(name)) > 0 &&
538 	    (itp = inotablookup(ino)) != NULL) {
539 		dirp = opendirfile(dirfile);
540 		rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
541 		return (dirp);
542 	}
543 	return (NULL);
544 }
545 
546 /*
547  * In our case, there is nothing to do when closing a directory.
548  */
549 void
550 rst_closedir(dirp)
551 	RST_DIR *dirp;
552 {
553 
554 	(void)close(dirp->dd_fd);
555 	free(dirp);
556 	return;
557 }
558 
559 /*
560  * Simulate finding the current offset in the directory.
561  */
562 static long
563 rst_telldir(dirp)
564 	RST_DIR *dirp;
565 {
566 	return ((long)lseek(dirp->dd_fd,
567 	    (off_t)0, SEEK_CUR) - dirp->dd_size + dirp->dd_loc);
568 }
569 
570 /*
571  * Open a directory file.
572  */
573 static RST_DIR *
574 opendirfile(name)
575 	const char *name;
576 {
577 	RST_DIR *dirp;
578 	int fd;
579 
580 	if ((fd = open(name, O_RDONLY)) == -1)
581 		return (NULL);
582 	if ((dirp = malloc(sizeof(RST_DIR))) == NULL) {
583 		(void)close(fd);
584 		return (NULL);
585 	}
586 	dirp->dd_fd = fd;
587 	dirp->dd_loc = 0;
588 	return (dirp);
589 }
590 
591 /*
592  * Set the mode, owner, and times for all new or changed directories
593  */
594 void
595 setdirmodes(flags)
596 	int flags;
597 {
598 	FILE *mf;
599 	struct modeinfo node;
600 	struct entry *ep;
601 	char *cp;
602 
603 	Vprintf(stdout, "Set directory mode, owner, and times.\n");
604 	if (command == 'r' || command == 'R')
605 		(void)snprintf(modefile, sizeof(modefile), "%s/rstmode%d",
606 		    tmpdir, dumpdate);
607 	if (modefile[0] == '#') {
608 		panic("modefile not defined\n");
609 		fputs("directory mode, owner, and times not set\n", stderr);
610 		return;
611 	}
612 	mf = fopen(modefile, "r");
613 	if (mf == NULL) {
614 		warn("fopen");
615 		fprintf(stderr, "cannot open mode file %s\n", modefile);
616 		fprintf(stderr, "directory mode, owner, and times not set\n");
617 		return;
618 	}
619 	clearerr(mf);
620 	for (;;) {
621 		(void)fread((char *)&node, 1, sizeof(struct modeinfo), mf);
622 		if (feof(mf))
623 			break;
624 		ep = lookupino(node.ino);
625 		if (command == 'i' || command == 'x') {
626 			if (ep == NULL)
627 				continue;
628 			if ((flags & FORCE) == 0 && ep->e_flags & EXISTED) {
629 				ep->e_flags &= ~NEW;
630 				continue;
631 			}
632 			if (node.ino == ROOTINO &&
633 		   	    reply("set owner/mode for '.'") == FAIL)
634 				continue;
635 		}
636 		if (ep == NULL) {
637 			panic("cannot find directory inode %d\n", node.ino);
638 		} else {
639 			cp = myname(ep);
640 			(void)chown(cp, node.uid, node.gid);
641 			(void)chmod(cp, node.mode);
642 			(void)chflags(cp, node.flags);
643 			utimes(cp, node.timep);
644 			ep->e_flags &= ~NEW;
645 		}
646 	}
647 	if (ferror(mf))
648 		panic("error setting directory modes\n");
649 	(void)fclose(mf);
650 }
651 
652 /*
653  * Generate a literal copy of a directory.
654  */
655 int
656 genliteraldir(name, ino)
657 	char *name;
658 	ino_t ino;
659 {
660 	struct inotab *itp;
661 	int ofile, dp, i, size;
662 	char buf[BUFSIZ];
663 
664 	itp = inotablookup(ino);
665 	if (itp == NULL)
666 		panic("Cannot find directory inode %d named %s\n", ino, name);
667 	if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) {
668 		warn("%s: cannot create file", name);
669 		return (FAIL);
670 	}
671 	rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
672 	dp = dup(dirp->dd_fd);
673 	for (i = itp->t_size; i > 0; i -= BUFSIZ) {
674 		size = i < BUFSIZ ? i : BUFSIZ;
675 		if (read(dp, buf, (int) size) == -1) {
676 			warnx("write error extracting inode %d, name %s",
677 			    curfile.ino, curfile.name);
678 			err(1, "read");
679 		}
680 		if (!Nflag && write(ofile, buf, (int) size) == -1) {
681 			fprintf(stderr,
682 				"write error extracting inode %d, name %s\n",
683 				curfile.ino, curfile.name);
684 			err(1, "write");
685 		}
686 	}
687 	(void)close(dp);
688 	(void)close(ofile);
689 	return (GOOD);
690 }
691 
692 /*
693  * Determine the type of an inode
694  */
695 int
696 inodetype(ino)
697 	ino_t ino;
698 {
699 	struct inotab *itp;
700 
701 	itp = inotablookup(ino);
702 	if (itp == NULL)
703 		return (LEAF);
704 	return (NODE);
705 }
706 
707 /*
708  * Allocate and initialize a directory inode entry.
709  * If requested, save its pertinent mode, owner, and time info.
710  */
711 static struct inotab *
712 allocinotab(ino, dip, seekpt)
713 	ino_t ino;
714 	struct dinode *dip;
715 	long seekpt;
716 {
717 	struct inotab	*itp;
718 	struct modeinfo node;
719 
720 	itp = calloc(1, sizeof(struct inotab));
721 	if (itp == NULL)
722 		panic("no memory directory table\n");
723 	itp->t_next = inotab[INOHASH(ino)];
724 	inotab[INOHASH(ino)] = itp;
725 	itp->t_ino = ino;
726 	itp->t_seekpt = seekpt;
727 	if (mf == NULL)
728 		return (itp);
729 	node.ino = ino;
730 	node.timep[0].tv_sec = dip->di_atime;
731 	node.timep[0].tv_usec = dip->di_atimensec / 1000;
732 	node.timep[1].tv_sec = dip->di_mtime;
733 	node.timep[1].tv_usec = dip->di_mtimensec / 1000;
734 	node.mode = dip->di_mode;
735 	node.flags = dip->di_flags;
736 	node.uid = dip->di_uid;
737 	node.gid = dip->di_gid;
738 	(void)fwrite((char *)&node, 1, sizeof(struct modeinfo), mf);
739 	return (itp);
740 }
741 
742 /*
743  * Look up an inode in the table of directories
744  */
745 static struct inotab *
746 inotablookup(ino)
747 	ino_t	ino;
748 {
749 	struct inotab *itp;
750 
751 	for (itp = inotab[INOHASH(ino)]; itp != NULL; itp = itp->t_next)
752 		if (itp->t_ino == ino)
753 			return (itp);
754 	return (NULL);
755 }
756 
757 /*
758  * Clean up and exit
759  */
760 void
761 cleanup()
762 {
763 
764 	closemt();
765 	if (modefile[0] != '#')
766 		(void)unlink(modefile);
767 	if (dirfile[0] != '#')
768 		(void)unlink(dirfile);
769 }
770