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