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