11558Srgrimes/*
21558Srgrimes * Copyright (c) 1983, 1993
31558Srgrimes *	The Regents of the University of California.  All rights reserved.
41558Srgrimes * (c) UNIX System Laboratories, Inc.
51558Srgrimes * All or some portions of this file are derived from material licensed
61558Srgrimes * to the University of California by American Telephone and Telegraph
71558Srgrimes * Co. or Unix System Laboratories, Inc. and are reproduced herein with
81558Srgrimes * the permission of UNIX System Laboratories, Inc.
91558Srgrimes *
101558Srgrimes * Redistribution and use in source and binary forms, with or without
111558Srgrimes * modification, are permitted provided that the following conditions
121558Srgrimes * are met:
131558Srgrimes * 1. Redistributions of source code must retain the above copyright
141558Srgrimes *    notice, this list of conditions and the following disclaimer.
151558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
161558Srgrimes *    notice, this list of conditions and the following disclaimer in the
171558Srgrimes *    documentation and/or other materials provided with the distribution.
181558Srgrimes * 4. Neither the name of the University nor the names of its contributors
191558Srgrimes *    may be used to endorse or promote products derived from this software
201558Srgrimes *    without specific prior written permission.
211558Srgrimes *
221558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
231558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
241558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
251558Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
261558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
271558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
281558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
291558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
301558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
311558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
321558Srgrimes * SUCH DAMAGE.
331558Srgrimes */
341558Srgrimes
351558Srgrimes#ifndef lint
3637906Scharnier#if 0
3723685Speterstatic char sccsid[] = "@(#)dirs.c	8.7 (Berkeley) 5/1/95";
3837906Scharnier#endif
3937906Scharnierstatic const char rcsid[] =
4050476Speter  "$FreeBSD$";
411558Srgrimes#endif /* not lint */
421558Srgrimes
431558Srgrimes#include <sys/param.h>
441558Srgrimes#include <sys/file.h>
451558Srgrimes#include <sys/stat.h>
4666907Swollman#include <sys/time.h>
471558Srgrimes
481558Srgrimes#include <ufs/ufs/dinode.h>
491558Srgrimes#include <ufs/ufs/dir.h>
501558Srgrimes#include <protocols/dumprestore.h>
511558Srgrimes
5237906Scharnier#include <err.h>
531558Srgrimes#include <errno.h>
54103949Smike#include <limits.h>
5573986Sobrien#include <paths.h>
56203155Sjh#include <stdint.h>
571558Srgrimes#include <stdio.h>
581558Srgrimes#include <stdlib.h>
591558Srgrimes#include <string.h>
601558Srgrimes#include <unistd.h>
611558Srgrimes
621558Srgrimes#include "restore.h"
631558Srgrimes#include "extern.h"
641558Srgrimes
651558Srgrimes/*
661558Srgrimes * Symbol table of directories read from tape.
671558Srgrimes */
681558Srgrimes#define HASHSIZE	1000
691558Srgrimes#define INOHASH(val) (val % HASHSIZE)
701558Srgrimesstruct inotab {
711558Srgrimes	struct	inotab *t_next;
721558Srgrimes	ino_t	t_ino;
7340668Sdima	int32_t	t_seekpt;
7440668Sdima	int32_t	t_size;
751558Srgrimes};
761558Srgrimesstatic struct inotab *inotab[HASHSIZE];
771558Srgrimes
781558Srgrimes/*
791558Srgrimes * Information retained about directories.
801558Srgrimes */
811558Srgrimesstruct modeinfo {
821558Srgrimes	ino_t ino;
83100207Smckusick	struct timeval ctimep[2];
84100207Smckusick	struct timeval mtimep[2];
8523685Speter	mode_t mode;
8623685Speter	uid_t uid;
8723685Speter	gid_t gid;
8823685Speter	int flags;
89167011Smckusick	int extsize;
901558Srgrimes};
911558Srgrimes
921558Srgrimes/*
931558Srgrimes * Definitions for library routines operating on directories.
941558Srgrimes */
951558Srgrimes#undef DIRBLKSIZ
961558Srgrimes#define DIRBLKSIZ 1024
971558Srgrimesstruct rstdirdesc {
981558Srgrimes	int	dd_fd;
9940668Sdima	int32_t	dd_loc;
10040668Sdima	int32_t	dd_size;
1011558Srgrimes	char	dd_buf[DIRBLKSIZ];
1021558Srgrimes};
1031558Srgrimes
1041558Srgrimes/*
1051558Srgrimes * Global variables for this file.
1061558Srgrimes */
1071558Srgrimesstatic long	seekpt;
1081558Srgrimesstatic FILE	*df, *mf;
1091558Srgrimesstatic RST_DIR	*dirp;
11021149Simpstatic char	dirfile[MAXPATHLEN] = "#";	/* No file */
11121149Simpstatic char	modefile[MAXPATHLEN] = "#";	/* No file */
11221149Simpstatic char	dot[2] = ".";			/* So it can be modified */
1131558Srgrimes
11498542Smckusickstatic struct inotab	*allocinotab(struct context *, long);
11592837Simpstatic void		 flushent(void);
11692837Simpstatic struct inotab	*inotablookup(ino_t);
11792837Simpstatic RST_DIR		*opendirfile(const char *);
11892837Simpstatic void		 putdir(char *, long);
119167011Smckusickstatic void		 putdirattrs(char *, long);
12092837Simpstatic void		 putent(struct direct *);
12192837Simpstatic void		 rst_seekdir(RST_DIR *, long, long);
12292837Simpstatic long		 rst_telldir(RST_DIR *);
12392837Simpstatic struct direct	*searchdir(ino_t, char *);
124178205Smckusickstatic void		 fail_dirtmp(char *);
1251558Srgrimes
1261558Srgrimes/*
1271558Srgrimes *	Extract directory contents, building up a directory structure
1281558Srgrimes *	on disk for extraction by name.
1291558Srgrimes *	If genmode is requested, save mode, owner, and times for all
1301558Srgrimes *	directories on the tape.
1311558Srgrimes */
1321558Srgrimesvoid
13392837Simpextractdirs(int genmode)
1341558Srgrimes{
1351558Srgrimes	struct inotab *itp;
1361558Srgrimes	struct direct nulldir;
13798542Smckusick	int i, fd;
13881579Ssobomax	const char *tmpdir;
1391558Srgrimes
1401558Srgrimes	vprintf(stdout, "Extract directories from tape\n");
14181579Ssobomax	if ((tmpdir = getenv("TMPDIR")) == NULL || tmpdir[0] == '\0')
14281579Ssobomax		tmpdir = _PATH_TMP;
143203155Sjh	(void) sprintf(dirfile, "%s/rstdir%jd", tmpdir, (intmax_t)dumpdate);
14421149Simp	if (command != 'r' && command != 'R') {
145203155Sjh		(void) strcat(dirfile, "-XXXXXX");
14621149Simp		fd = mkstemp(dirfile);
14721149Simp	} else
14821149Simp		fd = open(dirfile, O_RDWR|O_CREAT|O_EXCL, 0666);
14921149Simp	if (fd == -1 || (df = fdopen(fd, "w")) == NULL) {
15021149Simp		if (fd != -1)
15121149Simp			close(fd);
152178205Smckusick		warn("%s: cannot create directory database", dirfile);
1531558Srgrimes		done(1);
1541558Srgrimes	}
1551558Srgrimes	if (genmode != 0) {
156203155Sjh		(void) sprintf(modefile, "%s/rstmode%jd", tmpdir,
157203155Sjh		    (intmax_t)dumpdate);
15821149Simp		if (command != 'r' && command != 'R') {
159203155Sjh			(void) strcat(modefile, "-XXXXXX");
16021149Simp			fd = mkstemp(modefile);
16121149Simp		} else
16221149Simp			fd = open(modefile, O_RDWR|O_CREAT|O_EXCL, 0666);
16321149Simp		if (fd == -1 || (mf = fdopen(fd, "w")) == NULL) {
16421149Simp			if (fd != -1)
16521149Simp				close(fd);
166178205Smckusick			warn("%s: cannot create modefile", modefile);
1671558Srgrimes			done(1);
1681558Srgrimes		}
1691558Srgrimes	}
1701558Srgrimes	nulldir.d_ino = 0;
1711558Srgrimes	nulldir.d_type = DT_DIR;
1721558Srgrimes	nulldir.d_namlen = 1;
1731558Srgrimes	(void) strcpy(nulldir.d_name, "/");
1741558Srgrimes	nulldir.d_reclen = DIRSIZ(0, &nulldir);
1751558Srgrimes	for (;;) {
1761558Srgrimes		curfile.name = "<directory file - name unknown>";
1771558Srgrimes		curfile.action = USING;
178178205Smckusick		if (curfile.mode == 0 || (curfile.mode & IFMT) != IFDIR)
179178205Smckusick			break;
18098542Smckusick		itp = allocinotab(&curfile, seekpt);
181167011Smckusick		getfile(putdir, putdirattrs, xtrnull);
1821558Srgrimes		putent(&nulldir);
1831558Srgrimes		flushent();
1841558Srgrimes		itp->t_size = seekpt - itp->t_seekpt;
1851558Srgrimes	}
186178205Smckusick	if (fclose(df) != 0)
187178205Smckusick		fail_dirtmp(dirfile);
188178205Smckusick	dirp = opendirfile(dirfile);
189178205Smckusick	if (dirp == NULL)
190178205Smckusick		fprintf(stderr, "opendirfile: %s\n", strerror(errno));
191178205Smckusick	if (mf != NULL && fclose(mf) != 0)
192178205Smckusick		fail_dirtmp(modefile);
193178205Smckusick	i = dirlookup(dot);
194178205Smckusick	if (i == 0)
195178205Smckusick		panic("Root directory is not on tape\n");
1961558Srgrimes}
1971558Srgrimes
1981558Srgrimes/*
1991558Srgrimes * skip over all the directories on the tape
2001558Srgrimes */
2011558Srgrimesvoid
20292837Simpskipdirs(void)
2031558Srgrimes{
2041558Srgrimes
20598542Smckusick	while (curfile.ino && (curfile.mode & IFMT) == IFDIR) {
2061558Srgrimes		skipfile();
2071558Srgrimes	}
2081558Srgrimes}
2091558Srgrimes
2101558Srgrimes/*
2118871Srgrimes *	Recursively find names and inumbers of all files in subtree
2121558Srgrimes *	pname and pass them off to be processed.
2131558Srgrimes */
2141558Srgrimesvoid
21592837Simptreescan(char *pname, ino_t ino, long (*todo)(char *, ino_t, int))
2161558Srgrimes{
21792806Sobrien	struct inotab *itp;
21892806Sobrien	struct direct *dp;
2191558Srgrimes	int namelen;
2201558Srgrimes	long bpt;
221177894Simp	char locname[MAXPATHLEN];
2221558Srgrimes
2231558Srgrimes	itp = inotablookup(ino);
2241558Srgrimes	if (itp == NULL) {
2251558Srgrimes		/*
2261558Srgrimes		 * Pname is name of a simple file or an unchanged directory.
2271558Srgrimes		 */
2281558Srgrimes		(void) (*todo)(pname, ino, LEAF);
2291558Srgrimes		return;
2301558Srgrimes	}
2311558Srgrimes	/*
2321558Srgrimes	 * Pname is a dumped directory name.
2331558Srgrimes	 */
2341558Srgrimes	if ((*todo)(pname, ino, NODE) == FAIL)
2351558Srgrimes		return;
2361558Srgrimes	/*
2371558Srgrimes	 * begin search through the directory
2381558Srgrimes	 * skipping over "." and ".."
2391558Srgrimes	 */
240177894Simp	(void) strlcpy(locname, pname, sizeof(locname));
241177894Simp	(void) strlcat(locname, "/", sizeof(locname));
2421558Srgrimes	namelen = strlen(locname);
2431558Srgrimes	rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
2441558Srgrimes	dp = rst_readdir(dirp); /* "." */
2451558Srgrimes	if (dp != NULL && strcmp(dp->d_name, ".") == 0)
2461558Srgrimes		dp = rst_readdir(dirp); /* ".." */
2471558Srgrimes	else
2481558Srgrimes		fprintf(stderr, "Warning: `.' missing from directory %s\n",
2491558Srgrimes			pname);
2501558Srgrimes	if (dp != NULL && strcmp(dp->d_name, "..") == 0)
2511558Srgrimes		dp = rst_readdir(dirp); /* first real entry */
2521558Srgrimes	else
2531558Srgrimes		fprintf(stderr, "Warning: `..' missing from directory %s\n",
2541558Srgrimes			pname);
2551558Srgrimes	bpt = rst_telldir(dirp);
2561558Srgrimes	/*
2571558Srgrimes	 * a zero inode signals end of directory
2581558Srgrimes	 */
25923685Speter	while (dp != NULL) {
2601558Srgrimes		locname[namelen] = '\0';
26121149Simp		if (namelen + dp->d_namlen >= sizeof(locname)) {
262203155Sjh			fprintf(stderr, "%s%s: name exceeds %zu char\n",
263203155Sjh			    locname, dp->d_name, sizeof(locname) - 1);
2641558Srgrimes		} else {
265177894Simp			(void)strlcat(locname, dp->d_name, sizeof(locname));
2661558Srgrimes			treescan(locname, dp->d_ino, todo);
2671558Srgrimes			rst_seekdir(dirp, bpt, itp->t_seekpt);
2681558Srgrimes		}
2691558Srgrimes		dp = rst_readdir(dirp);
2701558Srgrimes		bpt = rst_telldir(dirp);
2711558Srgrimes	}
2721558Srgrimes}
2731558Srgrimes
2741558Srgrimes/*
2751558Srgrimes * Lookup a pathname which is always assumed to start from the ROOTINO.
2761558Srgrimes */
2771558Srgrimesstruct direct *
27892837Simppathsearch(const char *pathname)
2791558Srgrimes{
2801558Srgrimes	ino_t ino;
2811558Srgrimes	struct direct *dp;
2821558Srgrimes	char *path, *name, buffer[MAXPATHLEN];
2831558Srgrimes
2841558Srgrimes	strcpy(buffer, pathname);
2851558Srgrimes	path = buffer;
2861558Srgrimes	ino = ROOTINO;
2871558Srgrimes	while (*path == '/')
2881558Srgrimes		path++;
2891558Srgrimes	dp = NULL;
29029574Sphk	while ((name = strsep(&path, "/")) != NULL && *name != '\0') {
2911558Srgrimes		if ((dp = searchdir(ino, name)) == NULL)
2921558Srgrimes			return (NULL);
2931558Srgrimes		ino = dp->d_ino;
2941558Srgrimes	}
2951558Srgrimes	return (dp);
2961558Srgrimes}
2971558Srgrimes
2981558Srgrimes/*
2991558Srgrimes * Lookup the requested name in directory inum.
3001558Srgrimes * Return its inode number if found, zero if it does not exist.
3011558Srgrimes */
3021558Srgrimesstatic struct direct *
30392837Simpsearchdir(ino_t	inum, char *name)
3041558Srgrimes{
30592806Sobrien	struct direct *dp;
30692806Sobrien	struct inotab *itp;
3071558Srgrimes	int len;
3081558Srgrimes
3091558Srgrimes	itp = inotablookup(inum);
3101558Srgrimes	if (itp == NULL)
3111558Srgrimes		return (NULL);
3121558Srgrimes	rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
3131558Srgrimes	len = strlen(name);
3141558Srgrimes	do {
3151558Srgrimes		dp = rst_readdir(dirp);
31623685Speter		if (dp == NULL)
3171558Srgrimes			return (NULL);
3181558Srgrimes	} while (dp->d_namlen != len || strncmp(dp->d_name, name, len) != 0);
3191558Srgrimes	return (dp);
3201558Srgrimes}
3211558Srgrimes
3221558Srgrimes/*
3231558Srgrimes * Put the directory entries in the directory file
3241558Srgrimes */
3251558Srgrimesstatic void
32692837Simpputdir(char *buf, long size)
3271558Srgrimes{
32892806Sobrien	struct direct *dp;
3291558Srgrimes	long loc, i;
3301558Srgrimes
33198542Smckusick	for (loc = 0; loc < size; ) {
33298542Smckusick		dp = (struct direct *)(buf + loc);
33398542Smckusick		if (Bcvt)
33498542Smckusick			swabst((u_char *)"ls", (u_char *) dp);
335144099Simp		if (oldinofmt && dp->d_ino != 0) {
336144099Simp#if BYTE_ORDER == BIG_ENDIAN
337144099Simp			if (Bcvt)
338144099Simp				dp->d_namlen = dp->d_type;
339144099Simp#else
340144099Simp			if (!Bcvt && dp->d_namlen == 0)
341144099Simp				dp->d_namlen = dp->d_type;
342144099Simp#endif
343144099Simp			dp->d_type = DT_UNKNOWN;
344144099Simp		}
34598542Smckusick		i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1));
34698542Smckusick		if ((dp->d_reclen & 0x3) != 0 ||
34798542Smckusick		    dp->d_reclen > i ||
348121541Speter		    dp->d_reclen < DIRSIZ(0, dp)
349121541Speter#if NAME_MAX < 255
350121541Speter		    || dp->d_namlen > NAME_MAX
351121541Speter#endif
352121541Speter		    ) {
35398542Smckusick			vprintf(stdout, "Mangled directory: ");
35498542Smckusick			if ((dp->d_reclen & 0x3) != 0)
35598542Smckusick				vprintf(stdout,
35698542Smckusick				   "reclen not multiple of 4 ");
35798542Smckusick			if (dp->d_reclen < DIRSIZ(0, dp))
35898542Smckusick				vprintf(stdout,
359203155Sjh				   "reclen less than DIRSIZ (%d < %zu) ",
36098542Smckusick				   dp->d_reclen, DIRSIZ(0, dp));
361121541Speter#if NAME_MAX < 255
36298542Smckusick			if (dp->d_namlen > NAME_MAX)
36398542Smckusick				vprintf(stdout,
36498542Smckusick				   "reclen name too big (%d > %d) ",
36598542Smckusick				   dp->d_namlen, NAME_MAX);
366121541Speter#endif
36798542Smckusick			vprintf(stdout, "\n");
36898542Smckusick			loc += i;
36998542Smckusick			continue;
3701558Srgrimes		}
37198542Smckusick		loc += dp->d_reclen;
37298542Smckusick		if (dp->d_ino != 0) {
37398542Smckusick			putent(dp);
37498542Smckusick		}
3751558Srgrimes	}
3761558Srgrimes}
3771558Srgrimes
3781558Srgrimes/*
3791558Srgrimes * These variables are "local" to the following two functions.
3801558Srgrimes */
3811558Srgrimeschar dirbuf[DIRBLKSIZ];
3821558Srgrimeslong dirloc = 0;
3831558Srgrimeslong prev = 0;
3841558Srgrimes
3851558Srgrimes/*
3861558Srgrimes * add a new directory entry to a file.
3871558Srgrimes */
3881558Srgrimesstatic void
38992837Simpputent(struct direct *dp)
3901558Srgrimes{
3911558Srgrimes	dp->d_reclen = DIRSIZ(0, dp);
3921558Srgrimes	if (dirloc + dp->d_reclen > DIRBLKSIZ) {
3931558Srgrimes		((struct direct *)(dirbuf + prev))->d_reclen =
3941558Srgrimes		    DIRBLKSIZ - prev;
395178205Smckusick		if (fwrite(dirbuf, DIRBLKSIZ, 1, df) != 1)
396178205Smckusick			fail_dirtmp(dirfile);
3971558Srgrimes		dirloc = 0;
3981558Srgrimes	}
39923685Speter	memmove(dirbuf + dirloc, dp, (long)dp->d_reclen);
4001558Srgrimes	prev = dirloc;
4011558Srgrimes	dirloc += dp->d_reclen;
4021558Srgrimes}
4031558Srgrimes
4041558Srgrimes/*
4051558Srgrimes * flush out a directory that is finished.
4061558Srgrimes */
4071558Srgrimesstatic void
40892837Simpflushent(void)
4091558Srgrimes{
4101558Srgrimes	((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev;
411178205Smckusick	if (fwrite(dirbuf, (int)dirloc, 1, df) != 1)
412178205Smckusick		fail_dirtmp(dirfile);
4131558Srgrimes	seekpt = ftell(df);
4141558Srgrimes	dirloc = 0;
4151558Srgrimes}
4161558Srgrimes
4171558Srgrimes/*
418167011Smckusick * Save extended attributes for a directory entry to a file.
419167011Smckusick */
420167011Smckusickstatic void
421167011Smckusickputdirattrs(char *buf, long size)
422167011Smckusick{
423167011Smckusick
424178205Smckusick	if (mf != NULL && fwrite(buf, size, 1, mf) != 1)
425178205Smckusick		fail_dirtmp(modefile);
426167011Smckusick}
427167011Smckusick
428167011Smckusick/*
4291558Srgrimes * Seek to an entry in a directory.
4301558Srgrimes * Only values returned by rst_telldir should be passed to rst_seekdir.
4311558Srgrimes * This routine handles many directories in a single file.
4321558Srgrimes * It takes the base of the directory in the file, plus
4331558Srgrimes * the desired seek offset into it.
4341558Srgrimes */
4351558Srgrimesstatic void
43692837Simprst_seekdir(RST_DIR *dirp, long loc, long base)
4371558Srgrimes{
4381558Srgrimes
4391558Srgrimes	if (loc == rst_telldir(dirp))
4401558Srgrimes		return;
4411558Srgrimes	loc -= base;
4421558Srgrimes	if (loc < 0)
44337240Sbde		fprintf(stderr, "bad seek pointer to rst_seekdir %ld\n", loc);
4441558Srgrimes	(void) lseek(dirp->dd_fd, base + (loc & ~(DIRBLKSIZ - 1)), SEEK_SET);
4451558Srgrimes	dirp->dd_loc = loc & (DIRBLKSIZ - 1);
4461558Srgrimes	if (dirp->dd_loc != 0)
4471558Srgrimes		dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ);
4481558Srgrimes}
4491558Srgrimes
4501558Srgrimes/*
4511558Srgrimes * get next entry in a directory.
4521558Srgrimes */
4531558Srgrimesstruct direct *
45492837Simprst_readdir(RST_DIR *dirp)
4551558Srgrimes{
45692806Sobrien	struct direct *dp;
4571558Srgrimes
4581558Srgrimes	for (;;) {
4591558Srgrimes		if (dirp->dd_loc == 0) {
4608871Srgrimes			dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf,
4611558Srgrimes			    DIRBLKSIZ);
4621558Srgrimes			if (dirp->dd_size <= 0) {
4631558Srgrimes				dprintf(stderr, "error reading directory\n");
4641558Srgrimes				return (NULL);
4651558Srgrimes			}
4661558Srgrimes		}
4671558Srgrimes		if (dirp->dd_loc >= dirp->dd_size) {
4681558Srgrimes			dirp->dd_loc = 0;
4691558Srgrimes			continue;
4701558Srgrimes		}
4711558Srgrimes		dp = (struct direct *)(dirp->dd_buf + dirp->dd_loc);
4721558Srgrimes		if (dp->d_reclen == 0 ||
4731558Srgrimes		    dp->d_reclen > DIRBLKSIZ + 1 - dirp->dd_loc) {
4741558Srgrimes			dprintf(stderr, "corrupted directory: bad reclen %d\n",
4751558Srgrimes				dp->d_reclen);
4761558Srgrimes			return (NULL);
4771558Srgrimes		}
4781558Srgrimes		dirp->dd_loc += dp->d_reclen;
47923685Speter		if (dp->d_ino == 0 && strcmp(dp->d_name, "/") == 0)
48023685Speter			return (NULL);
4811558Srgrimes		if (dp->d_ino >= maxino) {
4821558Srgrimes			dprintf(stderr, "corrupted directory: bad inum %d\n",
4831558Srgrimes				dp->d_ino);
4841558Srgrimes			continue;
4851558Srgrimes		}
4861558Srgrimes		return (dp);
4871558Srgrimes	}
4881558Srgrimes}
4891558Srgrimes
4901558Srgrimes/*
4911558Srgrimes * Simulate the opening of a directory
4921558Srgrimes */
493129666Sstefanfvoid *
49492837Simprst_opendir(const char *name)
4951558Srgrimes{
4961558Srgrimes	struct inotab *itp;
4971558Srgrimes	RST_DIR *dirp;
4981558Srgrimes	ino_t ino;
4991558Srgrimes
5001558Srgrimes	if ((ino = dirlookup(name)) > 0 &&
5011558Srgrimes	    (itp = inotablookup(ino)) != NULL) {
5021558Srgrimes		dirp = opendirfile(dirfile);
5031558Srgrimes		rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
5041558Srgrimes		return (dirp);
5051558Srgrimes	}
5061558Srgrimes	return (NULL);
5071558Srgrimes}
5081558Srgrimes
5091558Srgrimes/*
5101558Srgrimes * In our case, there is nothing to do when closing a directory.
5111558Srgrimes */
5121558Srgrimesvoid
513129666Sstefanfrst_closedir(void *arg)
5141558Srgrimes{
515129666Sstefanf	RST_DIR *dirp;
5161558Srgrimes
517129666Sstefanf	dirp = arg;
5181558Srgrimes	(void)close(dirp->dd_fd);
5191558Srgrimes	free(dirp);
5201558Srgrimes	return;
5211558Srgrimes}
5221558Srgrimes
5231558Srgrimes/*
5241558Srgrimes * Simulate finding the current offset in the directory.
5251558Srgrimes */
5261558Srgrimesstatic long
52792837Simprst_telldir(RST_DIR *dirp)
5281558Srgrimes{
5291558Srgrimes	return ((long)lseek(dirp->dd_fd,
5301558Srgrimes	    (off_t)0, SEEK_CUR) - dirp->dd_size + dirp->dd_loc);
5311558Srgrimes}
5321558Srgrimes
5331558Srgrimes/*
5341558Srgrimes * Open a directory file.
5351558Srgrimes */
5361558Srgrimesstatic RST_DIR *
53792837Simpopendirfile(const char *name)
5381558Srgrimes{
53992806Sobrien	RST_DIR *dirp;
54092806Sobrien	int fd;
5411558Srgrimes
5421558Srgrimes	if ((fd = open(name, O_RDONLY)) == -1)
5431558Srgrimes		return (NULL);
5441558Srgrimes	if ((dirp = malloc(sizeof(RST_DIR))) == NULL) {
5451558Srgrimes		(void)close(fd);
5461558Srgrimes		return (NULL);
5471558Srgrimes	}
5481558Srgrimes	dirp->dd_fd = fd;
5491558Srgrimes	dirp->dd_loc = 0;
5501558Srgrimes	return (dirp);
5511558Srgrimes}
5521558Srgrimes
5531558Srgrimes/*
5541558Srgrimes * Set the mode, owner, and times for all new or changed directories
5551558Srgrimes */
5561558Srgrimesvoid
55792837Simpsetdirmodes(int flags)
5581558Srgrimes{
5591558Srgrimes	FILE *mf;
5601558Srgrimes	struct modeinfo node;
5611558Srgrimes	struct entry *ep;
562167011Smckusick	char *cp, *buf;
56381579Ssobomax	const char *tmpdir;
564167011Smckusick	int bufsize;
565178125Smckusick	uid_t myuid;
5668871Srgrimes
5671558Srgrimes	vprintf(stdout, "Set directory mode, owner, and times.\n");
56881579Ssobomax	if ((tmpdir = getenv("TMPDIR")) == NULL || tmpdir[0] == '\0')
56981579Ssobomax		tmpdir = _PATH_TMP;
57021149Simp	if (command == 'r' || command == 'R')
571203155Sjh		(void) sprintf(modefile, "%s/rstmode%jd", tmpdir,
572203155Sjh		    (intmax_t)dumpdate);
57321149Simp	if (modefile[0] == '#') {
57421149Simp		panic("modefile not defined\n");
57521149Simp		fprintf(stderr, "directory mode, owner, and times not set\n");
57621149Simp		return;
57721149Simp	}
5781558Srgrimes	mf = fopen(modefile, "r");
5791558Srgrimes	if (mf == NULL) {
5801558Srgrimes		fprintf(stderr, "fopen: %s\n", strerror(errno));
5811558Srgrimes		fprintf(stderr, "cannot open mode file %s\n", modefile);
5821558Srgrimes		fprintf(stderr, "directory mode, owner, and times not set\n");
5831558Srgrimes		return;
5841558Srgrimes	}
5851558Srgrimes	clearerr(mf);
586167011Smckusick	bufsize = 0;
587178125Smckusick	myuid = getuid();
5881558Srgrimes	for (;;) {
5891558Srgrimes		(void) fread((char *)&node, 1, sizeof(struct modeinfo), mf);
590178205Smckusick		if (ferror(mf)) {
591178205Smckusick			warn("%s: cannot read modefile.", modefile);
592178205Smckusick			fprintf(stderr, "Mode, owner, and times not set.\n");
593178205Smckusick			break;
594178205Smckusick		}
5951558Srgrimes		if (feof(mf))
5961558Srgrimes			break;
597167011Smckusick		if (node.extsize > 0) {
598167011Smckusick			if (bufsize < node.extsize) {
599167011Smckusick				if (bufsize > 0)
600167011Smckusick					free(buf);
601167011Smckusick				if ((buf = malloc(node.extsize)) != 0) {
602167011Smckusick					bufsize = node.extsize;
603167011Smckusick				} else {
604167011Smckusick					bufsize = 0;
605167011Smckusick				}
606167011Smckusick			}
607167011Smckusick			if (bufsize >= node.extsize) {
608167011Smckusick				(void) fread(buf, 1, node.extsize, mf);
609178205Smckusick				if (ferror(mf)) {
610178205Smckusick					warn("%s: cannot read modefile.",
611178205Smckusick					    modefile);
612178205Smckusick					fprintf(stderr, "Not all external ");
613178205Smckusick					fprintf(stderr, "attributes set.\n");
614178205Smckusick					break;
615178205Smckusick				}
616167011Smckusick			} else {
617167011Smckusick				(void) fseek(mf, node.extsize, SEEK_CUR);
618178205Smckusick				if (ferror(mf)) {
619178205Smckusick					warn("%s: cannot seek in modefile.",
620178205Smckusick					    modefile);
621178205Smckusick					fprintf(stderr, "Not all directory ");
622178205Smckusick					fprintf(stderr, "attributes set.\n");
623178205Smckusick					break;
624178205Smckusick				}
625167011Smckusick			}
626167011Smckusick		}
6271558Srgrimes		ep = lookupino(node.ino);
6281558Srgrimes		if (command == 'i' || command == 'x') {
6291558Srgrimes			if (ep == NULL)
6301558Srgrimes				continue;
6311558Srgrimes			if ((flags & FORCE) == 0 && ep->e_flags & EXISTED) {
6321558Srgrimes				ep->e_flags &= ~NEW;
6331558Srgrimes				continue;
6341558Srgrimes			}
6351558Srgrimes			if (node.ino == ROOTINO &&
6361558Srgrimes		   	    reply("set owner/mode for '.'") == FAIL)
6371558Srgrimes				continue;
6381558Srgrimes		}
6391558Srgrimes		if (ep == NULL) {
6401558Srgrimes			panic("cannot find directory inode %d\n", node.ino);
641167011Smckusick			continue;
642167011Smckusick		}
643167011Smckusick		cp = myname(ep);
644167011Smckusick		if (!Nflag) {
645167011Smckusick			if (node.extsize > 0) {
646167011Smckusick				if (bufsize >= node.extsize) {
647167011Smckusick					set_extattr_file(cp, buf, node.extsize);
648167011Smckusick				} else {
649167011Smckusick					fprintf(stderr, "Cannot restore %s%s\n",
650167011Smckusick					    "extended attributes for ", cp);
651167011Smckusick				}
65281940Sdd			}
653178125Smckusick			if (myuid != 0)
654178125Smckusick				(void) chown(cp, myuid, node.gid);
655178125Smckusick			else
656178125Smckusick				(void) chown(cp, node.uid, node.gid);
657167011Smckusick			(void) chmod(cp, node.mode);
658167011Smckusick			utimes(cp, node.ctimep);
659167011Smckusick			utimes(cp, node.mtimep);
660167011Smckusick			(void) chflags(cp, node.flags);
6611558Srgrimes		}
662167011Smckusick		ep->e_flags &= ~NEW;
6631558Srgrimes	}
664167011Smckusick	if (bufsize > 0)
665167011Smckusick		free(buf);
6661558Srgrimes	(void) fclose(mf);
6671558Srgrimes}
6681558Srgrimes
6691558Srgrimes/*
6701558Srgrimes * Generate a literal copy of a directory.
6711558Srgrimes */
6721558Srgrimesint
67392837Simpgenliteraldir(char *name, ino_t ino)
6741558Srgrimes{
67592806Sobrien	struct inotab *itp;
6761558Srgrimes	int ofile, dp, i, size;
6771558Srgrimes	char buf[BUFSIZ];
6781558Srgrimes
6791558Srgrimes	itp = inotablookup(ino);
6801558Srgrimes	if (itp == NULL)
6811558Srgrimes		panic("Cannot find directory inode %d named %s\n", ino, name);
68221149Simp	if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) {
6831558Srgrimes		fprintf(stderr, "%s: ", name);
6841558Srgrimes		(void) fflush(stderr);
6851558Srgrimes		fprintf(stderr, "cannot create file: %s\n", strerror(errno));
6861558Srgrimes		return (FAIL);
6871558Srgrimes	}
6881558Srgrimes	rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
6891558Srgrimes	dp = dup(dirp->dd_fd);
6901558Srgrimes	for (i = itp->t_size; i > 0; i -= BUFSIZ) {
6911558Srgrimes		size = i < BUFSIZ ? i : BUFSIZ;
6921558Srgrimes		if (read(dp, buf, (int) size) == -1) {
6931558Srgrimes			fprintf(stderr,
6941558Srgrimes				"write error extracting inode %d, name %s\n",
6951558Srgrimes				curfile.ino, curfile.name);
6961558Srgrimes			fprintf(stderr, "read: %s\n", strerror(errno));
6971558Srgrimes			done(1);
6981558Srgrimes		}
6991558Srgrimes		if (!Nflag && write(ofile, buf, (int) size) == -1) {
7001558Srgrimes			fprintf(stderr,
7011558Srgrimes				"write error extracting inode %d, name %s\n",
7021558Srgrimes				curfile.ino, curfile.name);
7031558Srgrimes			fprintf(stderr, "write: %s\n", strerror(errno));
7041558Srgrimes			done(1);
7051558Srgrimes		}
7061558Srgrimes	}
7071558Srgrimes	(void) close(dp);
7081558Srgrimes	(void) close(ofile);
7091558Srgrimes	return (GOOD);
7101558Srgrimes}
7111558Srgrimes
7121558Srgrimes/*
7131558Srgrimes * Determine the type of an inode
7141558Srgrimes */
7151558Srgrimesint
71692837Simpinodetype(ino_t ino)
7171558Srgrimes{
7181558Srgrimes	struct inotab *itp;
7191558Srgrimes
7201558Srgrimes	itp = inotablookup(ino);
7211558Srgrimes	if (itp == NULL)
7221558Srgrimes		return (LEAF);
7231558Srgrimes	return (NODE);
7241558Srgrimes}
7251558Srgrimes
7261558Srgrimes/*
7271558Srgrimes * Allocate and initialize a directory inode entry.
7281558Srgrimes * If requested, save its pertinent mode, owner, and time info.
7291558Srgrimes */
7301558Srgrimesstatic struct inotab *
73198542Smckusickallocinotab(struct context *ctxp, long seekpt)
7321558Srgrimes{
73392806Sobrien	struct inotab	*itp;
7341558Srgrimes	struct modeinfo node;
7351558Srgrimes
7361558Srgrimes	itp = calloc(1, sizeof(struct inotab));
7371558Srgrimes	if (itp == NULL)
738167011Smckusick		panic("no memory for directory table\n");
73998542Smckusick	itp->t_next = inotab[INOHASH(ctxp->ino)];
74098542Smckusick	inotab[INOHASH(ctxp->ino)] = itp;
74198542Smckusick	itp->t_ino = ctxp->ino;
7421558Srgrimes	itp->t_seekpt = seekpt;
7431558Srgrimes	if (mf == NULL)
7441558Srgrimes		return (itp);
74598542Smckusick	node.ino = ctxp->ino;
746100207Smckusick	node.mtimep[0].tv_sec = ctxp->atime_sec;
747100207Smckusick	node.mtimep[0].tv_usec = ctxp->atime_nsec / 1000;
748100207Smckusick	node.mtimep[1].tv_sec = ctxp->mtime_sec;
749100207Smckusick	node.mtimep[1].tv_usec = ctxp->mtime_nsec / 1000;
750100207Smckusick	node.ctimep[0].tv_sec = ctxp->atime_sec;
751100207Smckusick	node.ctimep[0].tv_usec = ctxp->atime_nsec / 1000;
752100207Smckusick	node.ctimep[1].tv_sec = ctxp->birthtime_sec;
753100207Smckusick	node.ctimep[1].tv_usec = ctxp->birthtime_nsec / 1000;
754167011Smckusick	node.extsize = ctxp->extsize;
75598542Smckusick	node.mode = ctxp->mode;
75698542Smckusick	node.flags = ctxp->file_flags;
75798542Smckusick	node.uid = ctxp->uid;
75898542Smckusick	node.gid = ctxp->gid;
759178205Smckusick	if (fwrite((char *)&node, sizeof(struct modeinfo), 1, mf) != 1)
760178205Smckusick		fail_dirtmp(modefile);
7611558Srgrimes	return (itp);
7621558Srgrimes}
7631558Srgrimes
7641558Srgrimes/*
7651558Srgrimes * Look up an inode in the table of directories
7661558Srgrimes */
7671558Srgrimesstatic struct inotab *
76892837Simpinotablookup(ino_t ino)
7691558Srgrimes{
77092806Sobrien	struct inotab *itp;
7711558Srgrimes
7721558Srgrimes	for (itp = inotab[INOHASH(ino)]; itp != NULL; itp = itp->t_next)
7731558Srgrimes		if (itp->t_ino == ino)
7741558Srgrimes			return (itp);
7751558Srgrimes	return (NULL);
7761558Srgrimes}
7771558Srgrimes
7781558Srgrimes/*
7791558Srgrimes * Clean up and exit
7801558Srgrimes */
78118286Sbdevoid
78292837Simpdone(int exitcode)
7831558Srgrimes{
7841558Srgrimes
7851558Srgrimes	closemt();
786178205Smckusick	if (modefile[0] != '#') {
787178205Smckusick		(void) truncate(modefile, 0);
7881558Srgrimes		(void) unlink(modefile);
789178205Smckusick	}
790178205Smckusick	if (dirfile[0] != '#') {
791178205Smckusick		(void) truncate(dirfile, 0);
7921558Srgrimes		(void) unlink(dirfile);
793178205Smckusick	}
7941558Srgrimes	exit(exitcode);
7951558Srgrimes}
796178205Smckusick
797178205Smckusick/*
798178205Smckusick * Print out information about the failure to save directory,
799178205Smckusick * extended attribute, and mode information.
800178205Smckusick */
801178205Smckusickstatic void
802178205Smckusickfail_dirtmp(char *filename)
803178205Smckusick{
804178205Smckusick	const char *tmpdir;
805178205Smckusick
806178205Smckusick	warn("%s: cannot write directory database", filename);
807178205Smckusick	if (errno == ENOSPC) {
808178205Smckusick		if ((tmpdir = getenv("TMPDIR")) == NULL || tmpdir[0] == '\0')
809178205Smckusick			tmpdir = _PATH_TMP;
810178205Smckusick		fprintf(stderr, "Try making space in %s, %s\n%s\n", tmpdir,
811178205Smckusick		    "or set environment variable TMPDIR",
812178205Smckusick		    "to an alternate location with more disk space.");
813178205Smckusick	}
814178205Smckusick	done(1);
815178205Smckusick}
816