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