xref: /freebsd/sbin/restore/dirs.c (revision b52b9d56d4e96089873a75f9e29062eec19fabba)
1 /*
2  * Copyright (c) 1983, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  * (c) UNIX System Laboratories, Inc.
5  * All or some portions of this file are derived from material licensed
6  * to the University of California by American Telephone and Telegraph
7  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
8  * the permission of UNIX System Laboratories, Inc.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  */
38 
39 #ifndef lint
40 #if 0
41 static char sccsid[] = "@(#)dirs.c	8.7 (Berkeley) 5/1/95";
42 #endif
43 static const char rcsid[] =
44   "$FreeBSD$";
45 #endif /* not lint */
46 
47 #include <sys/param.h>
48 #include <sys/file.h>
49 #include <sys/stat.h>
50 #include <sys/time.h>
51 
52 #include <ufs/ufs/dinode.h>
53 #include <ufs/ufs/dir.h>
54 #include <protocols/dumprestore.h>
55 
56 #include <err.h>
57 #include <errno.h>
58 #include <paths.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <unistd.h>
63 
64 #include "restore.h"
65 #include "extern.h"
66 
67 /*
68  * Symbol table of directories read from tape.
69  */
70 #define HASHSIZE	1000
71 #define INOHASH(val) (val % HASHSIZE)
72 struct inotab {
73 	struct	inotab *t_next;
74 	ino_t	t_ino;
75 	int32_t	t_seekpt;
76 	int32_t	t_size;
77 };
78 static struct inotab *inotab[HASHSIZE];
79 
80 /*
81  * Information retained about directories.
82  */
83 struct modeinfo {
84 	ino_t ino;
85 	struct timeval ctimep[2];
86 	struct timeval mtimep[2];
87 	mode_t mode;
88 	uid_t uid;
89 	gid_t gid;
90 	int flags;
91 };
92 
93 /*
94  * Definitions for library routines operating on directories.
95  */
96 #undef DIRBLKSIZ
97 #define DIRBLKSIZ 1024
98 struct rstdirdesc {
99 	int	dd_fd;
100 	int32_t	dd_loc;
101 	int32_t	dd_size;
102 	char	dd_buf[DIRBLKSIZ];
103 };
104 
105 /*
106  * Global variables for this file.
107  */
108 static long	seekpt;
109 static FILE	*df, *mf;
110 static RST_DIR	*dirp;
111 static char	dirfile[MAXPATHLEN] = "#";	/* No file */
112 static char	modefile[MAXPATHLEN] = "#";	/* No file */
113 static char	dot[2] = ".";			/* So it can be modified */
114 
115 /*
116  * Format of old style directories.
117  */
118 #define ODIRSIZ 14
119 struct odirect {
120 	u_short	d_ino;
121 	char	d_name[ODIRSIZ];
122 };
123 
124 static struct inotab	*allocinotab(struct context *, long);
125 static void		 dcvt(struct odirect *, struct direct *);
126 static void		 flushent(void);
127 static struct inotab	*inotablookup(ino_t);
128 static RST_DIR		*opendirfile(const char *);
129 static void		 putdir(char *, long);
130 static void		 putent(struct direct *);
131 static void		 rst_seekdir(RST_DIR *, long, long);
132 static long		 rst_telldir(RST_DIR *);
133 static struct direct	*searchdir(ino_t, char *);
134 
135 /*
136  *	Extract directory contents, building up a directory structure
137  *	on disk for extraction by name.
138  *	If genmode is requested, save mode, owner, and times for all
139  *	directories on the tape.
140  */
141 void
142 extractdirs(int genmode)
143 {
144 	struct inotab *itp;
145 	struct direct nulldir;
146 	int i, fd;
147 	const char *tmpdir;
148 
149 	vprintf(stdout, "Extract directories from tape\n");
150 	if ((tmpdir = getenv("TMPDIR")) == NULL || tmpdir[0] == '\0')
151 		tmpdir = _PATH_TMP;
152 	(void) sprintf(dirfile, "%s/rstdir%d", tmpdir, dumpdate);
153 	if (command != 'r' && command != 'R') {
154 		(void *) strcat(dirfile, "-XXXXXX");
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 		warn("%s - cannot create directory temporary\nfopen", dirfile);
162 		done(1);
163 	}
164 	if (genmode != 0) {
165 		(void) sprintf(modefile, "%s/rstmode%d", tmpdir, dumpdate);
166 		if (command != 'r' && command != 'R') {
167 			(void *) strcat(modefile, "-XXXXXX");
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 			warn("%s - cannot create modefile\nfopen", modefile);
175 			done(1);
176 		}
177 	}
178 	nulldir.d_ino = 0;
179 	nulldir.d_type = DT_DIR;
180 	nulldir.d_namlen = 1;
181 	(void) strcpy(nulldir.d_name, "/");
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 				fprintf(stderr, "opendirfile: %s\n",
191 				    strerror(errno));
192 			if (mf != NULL)
193 				(void) fclose(mf);
194 			i = dirlookup(dot);
195 			if (i == 0)
196 				panic("Root directory is not on tape\n");
197 			return;
198 		}
199 		itp = allocinotab(&curfile, seekpt);
200 		getfile(putdir, xtrnull);
201 		putent(&nulldir);
202 		flushent();
203 		itp->t_size = seekpt - itp->t_seekpt;
204 	}
205 }
206 
207 /*
208  * skip over all the directories on the tape
209  */
210 void
211 skipdirs(void)
212 {
213 
214 	while (curfile.ino && (curfile.mode & IFMT) == IFDIR) {
215 		skipfile();
216 	}
217 }
218 
219 /*
220  *	Recursively find names and inumbers of all files in subtree
221  *	pname and pass them off to be processed.
222  */
223 void
224 treescan(char *pname, ino_t ino, long (*todo)(char *, ino_t, int))
225 {
226 	struct inotab *itp;
227 	struct direct *dp;
228 	int namelen;
229 	long bpt;
230 	char locname[MAXPATHLEN + 1];
231 
232 	itp = inotablookup(ino);
233 	if (itp == NULL) {
234 		/*
235 		 * Pname is name of a simple file or an unchanged directory.
236 		 */
237 		(void) (*todo)(pname, ino, LEAF);
238 		return;
239 	}
240 	/*
241 	 * Pname is a dumped directory name.
242 	 */
243 	if ((*todo)(pname, ino, NODE) == FAIL)
244 		return;
245 	/*
246 	 * begin search through the directory
247 	 * skipping over "." and ".."
248 	 */
249 	(void) strncpy(locname, pname, sizeof(locname) - 1);
250 	locname[sizeof(locname) - 1] = '\0';
251 	(void) strncat(locname, "/", sizeof(locname) - strlen(locname));
252 	namelen = strlen(locname);
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 %d char\n",
273 				locname, dp->d_name, sizeof(locname) - 1);
274 		} else {
275 			(void) strncat(locname, dp->d_name, (int)dp->d_namlen);
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 	strcpy(buffer, pathname);
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, long size)
337 {
338 	struct direct cvtbuf;
339 	struct odirect *odp;
340 	struct odirect *eodp;
341 	struct direct *dp;
342 	long loc, i;
343 
344 	for (loc = 0; loc < size; ) {
345 		dp = (struct direct *)(buf + loc);
346 		if (Bcvt)
347 			swabst((u_char *)"ls", (u_char *) dp);
348 		i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1));
349 		if ((dp->d_reclen & 0x3) != 0 ||
350 		    dp->d_reclen > i ||
351 		    dp->d_reclen < DIRSIZ(0, dp) ||
352 		    dp->d_namlen > NAME_MAX) {
353 			vprintf(stdout, "Mangled directory: ");
354 			if ((dp->d_reclen & 0x3) != 0)
355 				vprintf(stdout,
356 				   "reclen not multiple of 4 ");
357 			if (dp->d_reclen < DIRSIZ(0, dp))
358 				vprintf(stdout,
359 				   "reclen less than DIRSIZ (%d < %d) ",
360 				   dp->d_reclen, DIRSIZ(0, dp));
361 			if (dp->d_namlen > NAME_MAX)
362 				vprintf(stdout,
363 				   "reclen name too big (%d > %d) ",
364 				   dp->d_namlen, NAME_MAX);
365 			vprintf(stdout, "\n");
366 			loc += i;
367 			continue;
368 		}
369 		loc += dp->d_reclen;
370 		if (dp->d_ino != 0) {
371 			putent(dp);
372 		}
373 	}
374 }
375 
376 /*
377  * These variables are "local" to the following two functions.
378  */
379 char dirbuf[DIRBLKSIZ];
380 long dirloc = 0;
381 long prev = 0;
382 
383 /*
384  * add a new directory entry to a file.
385  */
386 static void
387 putent(struct direct *dp)
388 {
389 	dp->d_reclen = DIRSIZ(0, dp);
390 	if (dirloc + dp->d_reclen > DIRBLKSIZ) {
391 		((struct direct *)(dirbuf + prev))->d_reclen =
392 		    DIRBLKSIZ - prev;
393 		(void) fwrite(dirbuf, 1, DIRBLKSIZ, df);
394 		dirloc = 0;
395 	}
396 	memmove(dirbuf + dirloc, dp, (long)dp->d_reclen);
397 	prev = dirloc;
398 	dirloc += dp->d_reclen;
399 }
400 
401 /*
402  * flush out a directory that is finished.
403  */
404 static void
405 flushent(void)
406 {
407 	((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev;
408 	(void) fwrite(dirbuf, (int)dirloc, 1, df);
409 	seekpt = ftell(df);
410 	dirloc = 0;
411 }
412 
413 static void
414 dcvt(struct odirect *odp, struct direct *ndp)
415 {
416 
417 	memset(ndp, 0, (long)(sizeof *ndp));
418 	ndp->d_ino =  odp->d_ino;
419 	ndp->d_type = DT_UNKNOWN;
420 	(void) strncpy(ndp->d_name, odp->d_name, ODIRSIZ);
421 	ndp->d_namlen = strlen(ndp->d_name);
422 	ndp->d_reclen = DIRSIZ(0, ndp);
423 }
424 
425 /*
426  * Seek to an entry in a directory.
427  * Only values returned by rst_telldir should be passed to rst_seekdir.
428  * This routine handles many directories in a single file.
429  * It takes the base of the directory in the file, plus
430  * the desired seek offset into it.
431  */
432 static void
433 rst_seekdir(RST_DIR *dirp, long loc, long base)
434 {
435 
436 	if (loc == rst_telldir(dirp))
437 		return;
438 	loc -= base;
439 	if (loc < 0)
440 		fprintf(stderr, "bad seek pointer to rst_seekdir %ld\n", loc);
441 	(void) lseek(dirp->dd_fd, base + (loc & ~(DIRBLKSIZ - 1)), SEEK_SET);
442 	dirp->dd_loc = loc & (DIRBLKSIZ - 1);
443 	if (dirp->dd_loc != 0)
444 		dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ);
445 }
446 
447 /*
448  * get next entry in a directory.
449  */
450 struct direct *
451 rst_readdir(RST_DIR *dirp)
452 {
453 	struct direct *dp;
454 
455 	for (;;) {
456 		if (dirp->dd_loc == 0) {
457 			dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf,
458 			    DIRBLKSIZ);
459 			if (dirp->dd_size <= 0) {
460 				dprintf(stderr, "error reading directory\n");
461 				return (NULL);
462 			}
463 		}
464 		if (dirp->dd_loc >= dirp->dd_size) {
465 			dirp->dd_loc = 0;
466 			continue;
467 		}
468 		dp = (struct direct *)(dirp->dd_buf + dirp->dd_loc);
469 		if (dp->d_reclen == 0 ||
470 		    dp->d_reclen > DIRBLKSIZ + 1 - dirp->dd_loc) {
471 			dprintf(stderr, "corrupted directory: bad reclen %d\n",
472 				dp->d_reclen);
473 			return (NULL);
474 		}
475 		dirp->dd_loc += dp->d_reclen;
476 		if (dp->d_ino == 0 && strcmp(dp->d_name, "/") == 0)
477 			return (NULL);
478 		if (dp->d_ino >= maxino) {
479 			dprintf(stderr, "corrupted directory: bad inum %d\n",
480 				dp->d_ino);
481 			continue;
482 		}
483 		return (dp);
484 	}
485 }
486 
487 /*
488  * Simulate the opening of a directory
489  */
490 RST_DIR *
491 rst_opendir(const char *name)
492 {
493 	struct inotab *itp;
494 	RST_DIR *dirp;
495 	ino_t ino;
496 
497 	if ((ino = dirlookup(name)) > 0 &&
498 	    (itp = inotablookup(ino)) != NULL) {
499 		dirp = opendirfile(dirfile);
500 		rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
501 		return (dirp);
502 	}
503 	return (NULL);
504 }
505 
506 /*
507  * In our case, there is nothing to do when closing a directory.
508  */
509 void
510 rst_closedir(RST_DIR *dirp)
511 {
512 
513 	(void)close(dirp->dd_fd);
514 	free(dirp);
515 	return;
516 }
517 
518 /*
519  * Simulate finding the current offset in the directory.
520  */
521 static long
522 rst_telldir(RST_DIR *dirp)
523 {
524 	return ((long)lseek(dirp->dd_fd,
525 	    (off_t)0, SEEK_CUR) - dirp->dd_size + dirp->dd_loc);
526 }
527 
528 /*
529  * Open a directory file.
530  */
531 static RST_DIR *
532 opendirfile(const char *name)
533 {
534 	RST_DIR *dirp;
535 	int fd;
536 
537 	if ((fd = open(name, O_RDONLY)) == -1)
538 		return (NULL);
539 	if ((dirp = malloc(sizeof(RST_DIR))) == NULL) {
540 		(void)close(fd);
541 		return (NULL);
542 	}
543 	dirp->dd_fd = fd;
544 	dirp->dd_loc = 0;
545 	return (dirp);
546 }
547 
548 /*
549  * Set the mode, owner, and times for all new or changed directories
550  */
551 void
552 setdirmodes(int flags)
553 {
554 	FILE *mf;
555 	struct modeinfo node;
556 	struct entry *ep;
557 	char *cp;
558 	const char *tmpdir;
559 
560 	vprintf(stdout, "Set directory mode, owner, and times.\n");
561 	if ((tmpdir = getenv("TMPDIR")) == NULL || tmpdir[0] == '\0')
562 		tmpdir = _PATH_TMP;
563 	if (command == 'r' || command == 'R')
564 		(void) sprintf(modefile, "%s/rstmode%d", tmpdir, dumpdate);
565 	if (modefile[0] == '#') {
566 		panic("modefile not defined\n");
567 		fprintf(stderr, "directory mode, owner, and times not set\n");
568 		return;
569 	}
570 	mf = fopen(modefile, "r");
571 	if (mf == NULL) {
572 		fprintf(stderr, "fopen: %s\n", strerror(errno));
573 		fprintf(stderr, "cannot open mode file %s\n", modefile);
574 		fprintf(stderr, "directory mode, owner, and times not set\n");
575 		return;
576 	}
577 	clearerr(mf);
578 	for (;;) {
579 		(void) fread((char *)&node, 1, sizeof(struct modeinfo), mf);
580 		if (feof(mf))
581 			break;
582 		ep = lookupino(node.ino);
583 		if (command == 'i' || command == 'x') {
584 			if (ep == NULL)
585 				continue;
586 			if ((flags & FORCE) == 0 && ep->e_flags & EXISTED) {
587 				ep->e_flags &= ~NEW;
588 				continue;
589 			}
590 			if (node.ino == ROOTINO &&
591 		   	    reply("set owner/mode for '.'") == FAIL)
592 				continue;
593 		}
594 		if (ep == NULL) {
595 			panic("cannot find directory inode %d\n", node.ino);
596 		} else {
597 			cp = myname(ep);
598 			if (!Nflag) {
599 				(void) chown(cp, node.uid, node.gid);
600 				(void) chmod(cp, node.mode);
601 				utimes(cp, node.ctimep);
602 				utimes(cp, node.mtimep);
603 				(void) chflags(cp, node.flags);
604 			}
605 			ep->e_flags &= ~NEW;
606 		}
607 	}
608 	if (ferror(mf))
609 		panic("error setting directory modes\n");
610 	(void) fclose(mf);
611 }
612 
613 /*
614  * Generate a literal copy of a directory.
615  */
616 int
617 genliteraldir(char *name, ino_t ino)
618 {
619 	struct inotab *itp;
620 	int ofile, dp, i, size;
621 	char buf[BUFSIZ];
622 
623 	itp = inotablookup(ino);
624 	if (itp == NULL)
625 		panic("Cannot find directory inode %d named %s\n", ino, name);
626 	if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) {
627 		fprintf(stderr, "%s: ", name);
628 		(void) fflush(stderr);
629 		fprintf(stderr, "cannot create file: %s\n", strerror(errno));
630 		return (FAIL);
631 	}
632 	rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
633 	dp = dup(dirp->dd_fd);
634 	for (i = itp->t_size; i > 0; i -= BUFSIZ) {
635 		size = i < BUFSIZ ? i : BUFSIZ;
636 		if (read(dp, buf, (int) size) == -1) {
637 			fprintf(stderr,
638 				"write error extracting inode %d, name %s\n",
639 				curfile.ino, curfile.name);
640 			fprintf(stderr, "read: %s\n", strerror(errno));
641 			done(1);
642 		}
643 		if (!Nflag && write(ofile, buf, (int) size) == -1) {
644 			fprintf(stderr,
645 				"write error extracting inode %d, name %s\n",
646 				curfile.ino, curfile.name);
647 			fprintf(stderr, "write: %s\n", strerror(errno));
648 			done(1);
649 		}
650 	}
651 	(void) close(dp);
652 	(void) close(ofile);
653 	return (GOOD);
654 }
655 
656 /*
657  * Determine the type of an inode
658  */
659 int
660 inodetype(ino_t ino)
661 {
662 	struct inotab *itp;
663 
664 	itp = inotablookup(ino);
665 	if (itp == NULL)
666 		return (LEAF);
667 	return (NODE);
668 }
669 
670 /*
671  * Allocate and initialize a directory inode entry.
672  * If requested, save its pertinent mode, owner, and time info.
673  */
674 static struct inotab *
675 allocinotab(struct context *ctxp, long seekpt)
676 {
677 	struct inotab	*itp;
678 	struct modeinfo node;
679 
680 	itp = calloc(1, sizeof(struct inotab));
681 	if (itp == NULL)
682 		panic("no memory directory table\n");
683 	itp->t_next = inotab[INOHASH(ctxp->ino)];
684 	inotab[INOHASH(ctxp->ino)] = itp;
685 	itp->t_ino = ctxp->ino;
686 	itp->t_seekpt = seekpt;
687 	if (mf == NULL)
688 		return (itp);
689 	node.ino = ctxp->ino;
690 	node.mtimep[0].tv_sec = ctxp->atime_sec;
691 	node.mtimep[0].tv_usec = ctxp->atime_nsec / 1000;
692 	node.mtimep[1].tv_sec = ctxp->mtime_sec;
693 	node.mtimep[1].tv_usec = ctxp->mtime_nsec / 1000;
694 	node.ctimep[0].tv_sec = ctxp->atime_sec;
695 	node.ctimep[0].tv_usec = ctxp->atime_nsec / 1000;
696 	node.ctimep[1].tv_sec = ctxp->birthtime_sec;
697 	node.ctimep[1].tv_usec = ctxp->birthtime_nsec / 1000;
698 	node.mode = ctxp->mode;
699 	node.flags = ctxp->file_flags;
700 	node.uid = ctxp->uid;
701 	node.gid = ctxp->gid;
702 	(void) fwrite((char *)&node, 1, sizeof(struct modeinfo), mf);
703 	return (itp);
704 }
705 
706 /*
707  * Look up an inode in the table of directories
708  */
709 static struct inotab *
710 inotablookup(ino_t ino)
711 {
712 	struct inotab *itp;
713 
714 	for (itp = inotab[INOHASH(ino)]; itp != NULL; itp = itp->t_next)
715 		if (itp->t_ino == ino)
716 			return (itp);
717 	return (NULL);
718 }
719 
720 /*
721  * Clean up and exit
722  */
723 void
724 done(int exitcode)
725 {
726 
727 	closemt();
728 	if (modefile[0] != '#')
729 		(void) unlink(modefile);
730 	if (dirfile[0] != '#')
731 		(void) unlink(dirfile);
732 	exit(exitcode);
733 }
734