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