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