xref: /openbsd-src/sbin/restore/dirs.c (revision da5362d567d5bc29ee9e27984ebc5dd0bad1b0e2)
1*da5362d5Sguenther /*	$OpenBSD: dirs.c,v 1.43 2024/01/09 03:16:00 guenther Exp $	*/
2a916033eSmillert /*	$NetBSD: dirs.c,v 1.26 1997/07/01 05:37:49 lukem Exp $	*/
3df930be7Sderaadt 
4df930be7Sderaadt /*
5df930be7Sderaadt  * Copyright (c) 1983, 1993
6df930be7Sderaadt  *	The Regents of the University of California.  All rights reserved.
7df930be7Sderaadt  * (c) UNIX System Laboratories, Inc.
8df930be7Sderaadt  * All or some portions of this file are derived from material licensed
9df930be7Sderaadt  * to the University of California by American Telephone and Telegraph
10df930be7Sderaadt  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
11df930be7Sderaadt  * the permission of UNIX System Laboratories, Inc.
12df930be7Sderaadt  *
13df930be7Sderaadt  * Redistribution and use in source and binary forms, with or without
14df930be7Sderaadt  * modification, are permitted provided that the following conditions
15df930be7Sderaadt  * are met:
16df930be7Sderaadt  * 1. Redistributions of source code must retain the above copyright
17df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer.
18df930be7Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
19df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer in the
20df930be7Sderaadt  *    documentation and/or other materials provided with the distribution.
211ef0d710Smillert  * 3. Neither the name of the University nor the names of its contributors
22df930be7Sderaadt  *    may be used to endorse or promote products derived from this software
23df930be7Sderaadt  *    without specific prior written permission.
24df930be7Sderaadt  *
25df930be7Sderaadt  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26df930be7Sderaadt  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27df930be7Sderaadt  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28df930be7Sderaadt  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29df930be7Sderaadt  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30df930be7Sderaadt  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31df930be7Sderaadt  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32df930be7Sderaadt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33df930be7Sderaadt  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34df930be7Sderaadt  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35df930be7Sderaadt  * SUCH DAMAGE.
36df930be7Sderaadt  */
37df930be7Sderaadt 
38df930be7Sderaadt #include <sys/stat.h>
39df930be7Sderaadt #include <sys/time.h>
40df930be7Sderaadt 
41df930be7Sderaadt #include <ufs/ffs/fs.h>
42df930be7Sderaadt #include <ufs/ufs/dinode.h>
43df930be7Sderaadt #include <ufs/ufs/dir.h>
44df930be7Sderaadt #include <protocols/dumprestore.h>
45df930be7Sderaadt 
46be9b7050Sguenther #include <endian.h>
47a916033eSmillert #include <err.h>
48ffb4dd05Sguenther #include <errno.h>
4901a83688Smillert #include <fcntl.h>
50ad417092Smillert #include <paths.h>
51df930be7Sderaadt #include <stdio.h>
52df930be7Sderaadt #include <stdlib.h>
53df930be7Sderaadt #include <string.h>
54df930be7Sderaadt #include <unistd.h>
55b9fc9a72Sderaadt #include <limits.h>
56df930be7Sderaadt 
57df930be7Sderaadt #include "restore.h"
58df930be7Sderaadt #include "extern.h"
59df930be7Sderaadt 
60df930be7Sderaadt /*
61df930be7Sderaadt  * Symbol table of directories read from tape.
62df930be7Sderaadt  */
63df930be7Sderaadt #define HASHSIZE	1000
64df930be7Sderaadt #define INOHASH(val) (val % HASHSIZE)
65df930be7Sderaadt struct inotab {
66df930be7Sderaadt 	struct	inotab *t_next;
67df930be7Sderaadt 	ino_t	t_ino;
68d739c122Sderaadt 	int32_t	t_seekpt;
69d739c122Sderaadt 	int32_t	t_size;
70df930be7Sderaadt };
71df930be7Sderaadt static struct inotab *inotab[HASHSIZE];
72df930be7Sderaadt 
73df930be7Sderaadt /*
74df930be7Sderaadt  * Information retained about directories.
75df930be7Sderaadt  */
76df930be7Sderaadt struct modeinfo {
77df930be7Sderaadt 	ino_t ino;
78a740969bSguenther 	struct timespec ctimep[2];
79a740969bSguenther 	struct timespec mtimep[2];
80df930be7Sderaadt 	mode_t mode;
81df930be7Sderaadt 	uid_t uid;
82df930be7Sderaadt 	gid_t gid;
83230b6680Smillert 	u_int flags;
84df930be7Sderaadt };
85df930be7Sderaadt 
86df930be7Sderaadt /*
87df930be7Sderaadt  * Definitions for library routines operating on directories.
88df930be7Sderaadt  */
89df930be7Sderaadt #undef DIRBLKSIZ
90df930be7Sderaadt #define DIRBLKSIZ 1024
91df930be7Sderaadt struct rstdirdesc {
92df930be7Sderaadt 	int	dd_fd;
93d739c122Sderaadt 	int32_t	dd_loc;
94d739c122Sderaadt 	int32_t	dd_size;
95df930be7Sderaadt 	char	dd_buf[DIRBLKSIZ];
96df930be7Sderaadt };
97df930be7Sderaadt 
98df930be7Sderaadt /*
99df930be7Sderaadt  * Global variables for this file.
100df930be7Sderaadt  */
101df930be7Sderaadt static long	seekpt;
102df930be7Sderaadt static FILE	*df, *mf;
103df930be7Sderaadt static RST_DIR	*dirp;
104b9fc9a72Sderaadt static char	dirfile[PATH_MAX] = "#";	/* No file */
105b9fc9a72Sderaadt static char	modefile[PATH_MAX] = "#";	/* No file */
106df930be7Sderaadt static char	dot[2] = ".";			/* So it can be modified */
107df930be7Sderaadt 
108df930be7Sderaadt /*
109df930be7Sderaadt  * Format of old style directories.
110df930be7Sderaadt  */
111df930be7Sderaadt #define ODIRSIZ 14
112df930be7Sderaadt struct odirect {
113df930be7Sderaadt 	u_short	d_ino;
114df930be7Sderaadt 	char	d_name[ODIRSIZ];
115df930be7Sderaadt };
116df930be7Sderaadt 
1170155e653Smillert static struct inotab	*allocinotab(FILE *, struct context *, long);
118c72b5b24Smillert static void		 dcvt(struct odirect *, struct direct *);
119c72b5b24Smillert static void		 flushent(void);
120c72b5b24Smillert static struct inotab	*inotablookup(ino_t);
121c72b5b24Smillert static RST_DIR		*opendirfile(const char *);
1222d2a7b95Shenning static void		 putdir(char *, size_t);
123c72b5b24Smillert static void		 putent(struct direct *);
124c72b5b24Smillert static void		 rst_seekdir(RST_DIR *, long, long);
125c72b5b24Smillert static long		 rst_telldir(RST_DIR *);
126c72b5b24Smillert static struct direct	*searchdir(ino_t, char *);
127df930be7Sderaadt 
128df930be7Sderaadt /*
129df930be7Sderaadt  *	Extract directory contents, building up a directory structure
130df930be7Sderaadt  *	on disk for extraction by name.
131df930be7Sderaadt  *	If genmode is requested, save mode, owner, and times for all
132df930be7Sderaadt  *	directories on the tape.
133df930be7Sderaadt  */
134df930be7Sderaadt void
extractdirs(int genmode)1354e95fccfSderaadt extractdirs(int genmode)
136df930be7Sderaadt {
137e073c79dSmpech 	int i;
138df930be7Sderaadt 	struct inotab *itp;
139df930be7Sderaadt 	struct direct nulldir;
1405d92347aSderaadt 	int fd;
141df930be7Sderaadt 
142a916033eSmillert 	Vprintf(stdout, "Extract directories from tape\n");
143520eb379Sotto 	(void)snprintf(dirfile, sizeof(dirfile), "%s/rstdir%lld", tmpdir,
144520eb379Sotto 	    (long long)dumpdate);
145f79cb2fdSderaadt 	if (command != 'r' && command != 'R') {
146f4dedfb2Savsm 		strlcat(dirfile, "-XXXXXXXXXX", sizeof(dirfile));
1479f6f356fSderaadt 		fd = mkstemp(dirfile);
1489f6f356fSderaadt 	} else
1499f6f356fSderaadt 		fd = open(dirfile, O_RDWR|O_CREAT|O_EXCL, 0666);
1509f6f356fSderaadt 	if (fd == -1 || (df = fdopen(fd, "w")) == NULL) {
151ffb4dd05Sguenther 		int saved_errno = errno;
1525d92347aSderaadt 		if (fd != -1)
1535d92347aSderaadt 			close(fd);
154ffb4dd05Sguenther 		errc(1, saved_errno,
155ffb4dd05Sguenther 		    "cannot create directory temporary %s", dirfile);
156df930be7Sderaadt 	}
157df930be7Sderaadt 	if (genmode != 0) {
158520eb379Sotto 		(void)snprintf(modefile, sizeof(modefile), "%s/rstmode%lld",
159520eb379Sotto 		    tmpdir, (long long)dumpdate);
160f79cb2fdSderaadt 		if (command != 'r' && command != 'R') {
161f4dedfb2Savsm 			strlcat(modefile, "-XXXXXXXXXX", sizeof(modefile));
1629f6f356fSderaadt 			fd = mkstemp(modefile);
1639f6f356fSderaadt 		} else
1649f6f356fSderaadt 			fd = open(modefile, O_RDWR|O_CREAT|O_EXCL, 0666);
1659f6f356fSderaadt 		if (fd == -1 || (mf = fdopen(fd, "w")) == NULL) {
166ffb4dd05Sguenther 			int saved_errno = errno;
1675d92347aSderaadt 			if (fd != -1)
1685d92347aSderaadt 				close(fd);
169ffb4dd05Sguenther 			errc(1, saved_errno,
170ffb4dd05Sguenther 			    "cannot create modefile %s", modefile);
171df930be7Sderaadt 		}
172df930be7Sderaadt 	}
173df930be7Sderaadt 	nulldir.d_ino = 0;
174df930be7Sderaadt 	nulldir.d_type = DT_DIR;
175df930be7Sderaadt 	nulldir.d_namlen = 1;
176a916033eSmillert 	nulldir.d_name[0] = '/';
177a916033eSmillert 	nulldir.d_name[1] = '\0';
178*da5362d5Sguenther 	nulldir.d_reclen = DIRSIZ(&nulldir);
179df930be7Sderaadt 	for (;;) {
180df930be7Sderaadt 		curfile.name = "<directory file - name unknown>";
181df930be7Sderaadt 		curfile.action = USING;
1820155e653Smillert 		if (curfile.mode == 0 || (curfile.mode & IFMT) != IFDIR) {
183df930be7Sderaadt 			(void)fclose(df);
184df930be7Sderaadt 			dirp = opendirfile(dirfile);
185df930be7Sderaadt 			if (dirp == NULL)
186a916033eSmillert 				warn("opendirfile");
187df930be7Sderaadt 			if (mf != NULL)
188df930be7Sderaadt 				(void)fclose(mf);
189df930be7Sderaadt 			i = dirlookup(dot);
190df930be7Sderaadt 			if (i == 0)
191df930be7Sderaadt 				panic("Root directory is not on tape\n");
192df930be7Sderaadt 			return;
193df930be7Sderaadt 		}
1940155e653Smillert 		itp = allocinotab(mf, &curfile, seekpt);
195df930be7Sderaadt 		getfile(putdir, xtrnull);
196df930be7Sderaadt 		putent(&nulldir);
197df930be7Sderaadt 		flushent();
198df930be7Sderaadt 		itp->t_size = seekpt - itp->t_seekpt;
199df930be7Sderaadt 	}
200df930be7Sderaadt }
201df930be7Sderaadt 
202df930be7Sderaadt /*
203df930be7Sderaadt  * skip over all the directories on the tape
204df930be7Sderaadt  */
205df930be7Sderaadt void
skipdirs(void)2064e95fccfSderaadt skipdirs(void)
207df930be7Sderaadt {
208df930be7Sderaadt 
2090155e653Smillert 	while (curfile.ino && (curfile.mode & IFMT) == IFDIR) {
210df930be7Sderaadt 		skipfile();
211df930be7Sderaadt 	}
212df930be7Sderaadt }
213df930be7Sderaadt 
214df930be7Sderaadt /*
215df930be7Sderaadt  *	Recursively find names and inumbers of all files in subtree
216df930be7Sderaadt  *	pname and pass them off to be processed.
217df930be7Sderaadt  */
218df930be7Sderaadt void
treescan(char * pname,ino_t ino,long (* todo)(char *,ino_t,int))2194e95fccfSderaadt treescan(char *pname, ino_t ino, long (*todo)(char *, ino_t, int))
220df930be7Sderaadt {
221e073c79dSmpech 	struct inotab *itp;
222e073c79dSmpech 	struct direct *dp;
223e88add51Smillert 	size_t namelen;
224df930be7Sderaadt 	long bpt;
225b9fc9a72Sderaadt 	char locname[PATH_MAX + 1];
226df930be7Sderaadt 
227df930be7Sderaadt 	itp = inotablookup(ino);
228df930be7Sderaadt 	if (itp == NULL) {
229df930be7Sderaadt 		/*
230df930be7Sderaadt 		 * Pname is name of a simple file or an unchanged directory.
231df930be7Sderaadt 		 */
232df930be7Sderaadt 		(void)(*todo)(pname, ino, LEAF);
233df930be7Sderaadt 		return;
234df930be7Sderaadt 	}
235df930be7Sderaadt 	/*
236df930be7Sderaadt 	 * Pname is a dumped directory name.
237df930be7Sderaadt 	 */
238df930be7Sderaadt 	if ((*todo)(pname, ino, NODE) == FAIL)
239df930be7Sderaadt 		return;
240df930be7Sderaadt 	/*
241df930be7Sderaadt 	 * begin search through the directory
242df930be7Sderaadt 	 * skipping over "." and ".."
243df930be7Sderaadt 	 */
244e88add51Smillert 	namelen = strlcpy(locname, pname, sizeof(locname));
245e88add51Smillert 	if (namelen >= sizeof(locname) - 1)
246e88add51Smillert 		namelen = sizeof(locname) - 2;
247e88add51Smillert 	locname[namelen++] = '/';
248e88add51Smillert 	locname[namelen] = '\0';
249df930be7Sderaadt 	rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
250df930be7Sderaadt 	dp = rst_readdir(dirp); /* "." */
251df930be7Sderaadt 	if (dp != NULL && strcmp(dp->d_name, ".") == 0)
252df930be7Sderaadt 		dp = rst_readdir(dirp); /* ".." */
253df930be7Sderaadt 	else
254df930be7Sderaadt 		fprintf(stderr, "Warning: `.' missing from directory %s\n",
255df930be7Sderaadt 			pname);
256df930be7Sderaadt 	if (dp != NULL && strcmp(dp->d_name, "..") == 0)
257df930be7Sderaadt 		dp = rst_readdir(dirp); /* first real entry */
258df930be7Sderaadt 	else
259df930be7Sderaadt 		fprintf(stderr, "Warning: `..' missing from directory %s\n",
260df930be7Sderaadt 			pname);
261df930be7Sderaadt 	bpt = rst_telldir(dirp);
262df930be7Sderaadt 	/*
263df930be7Sderaadt 	 * a zero inode signals end of directory
264df930be7Sderaadt 	 */
265df930be7Sderaadt 	while (dp != NULL) {
26628b0d5b1Smillert 		locname[namelen] = '\0';
26728b0d5b1Smillert 		if (namelen + dp->d_namlen >= sizeof(locname)) {
2680155e653Smillert 			fprintf(stderr, "%s%s: name exceeds %zd char\n",
26979f5c397Smillert 				locname, dp->d_name, sizeof(locname) - 1);
270df930be7Sderaadt 		} else {
271f4dedfb2Savsm 			(void)strlcat(locname, dp->d_name, sizeof(locname));
272df930be7Sderaadt 			treescan(locname, dp->d_ino, todo);
273df930be7Sderaadt 			rst_seekdir(dirp, bpt, itp->t_seekpt);
274df930be7Sderaadt 		}
275df930be7Sderaadt 		dp = rst_readdir(dirp);
276df930be7Sderaadt 		bpt = rst_telldir(dirp);
277df930be7Sderaadt 	}
278df930be7Sderaadt }
279df930be7Sderaadt 
280df930be7Sderaadt /*
281df930be7Sderaadt  * Lookup a pathname which is always assumed to start from the ROOTINO.
282df930be7Sderaadt  */
283df930be7Sderaadt struct direct *
pathsearch(const char * pathname)2844e95fccfSderaadt pathsearch(const char *pathname)
285df930be7Sderaadt {
286df930be7Sderaadt 	ino_t ino;
287df930be7Sderaadt 	struct direct *dp;
288b9fc9a72Sderaadt 	char *path, *name, buffer[PATH_MAX];
289df930be7Sderaadt 
29015f407e7Sderaadt 	strlcpy(buffer, pathname, sizeof buffer);
291df930be7Sderaadt 	path = buffer;
292df930be7Sderaadt 	ino = ROOTINO;
293df930be7Sderaadt 	while (*path == '/')
294df930be7Sderaadt 		path++;
295df930be7Sderaadt 	dp = NULL;
296a916033eSmillert 	while ((name = strsep(&path, "/")) != NULL && *name != '\0') {
297df930be7Sderaadt 		if ((dp = searchdir(ino, name)) == NULL)
298df930be7Sderaadt 			return (NULL);
299df930be7Sderaadt 		ino = dp->d_ino;
300df930be7Sderaadt 	}
301df930be7Sderaadt 	return (dp);
302df930be7Sderaadt }
303df930be7Sderaadt 
304df930be7Sderaadt /*
305df930be7Sderaadt  * Lookup the requested name in directory inum.
306df930be7Sderaadt  * Return its inode number if found, zero if it does not exist.
307df930be7Sderaadt  */
308df930be7Sderaadt static struct direct *
searchdir(ino_t inum,char * name)3094e95fccfSderaadt searchdir(ino_t inum, char *name)
310df930be7Sderaadt {
311e073c79dSmpech 	struct direct *dp;
312e073c79dSmpech 	struct inotab *itp;
313df930be7Sderaadt 	int len;
314df930be7Sderaadt 
315df930be7Sderaadt 	itp = inotablookup(inum);
316df930be7Sderaadt 	if (itp == NULL)
317df930be7Sderaadt 		return (NULL);
318df930be7Sderaadt 	rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
319df930be7Sderaadt 	len = strlen(name);
320df930be7Sderaadt 	do {
321df930be7Sderaadt 		dp = rst_readdir(dirp);
322df930be7Sderaadt 		if (dp == NULL)
323df930be7Sderaadt 			return (NULL);
324df930be7Sderaadt 	} while (dp->d_namlen != len || strncmp(dp->d_name, name, len) != 0);
325df930be7Sderaadt 	return (dp);
326df930be7Sderaadt }
327df930be7Sderaadt 
328df930be7Sderaadt /*
329df930be7Sderaadt  * Put the directory entries in the directory file
330df930be7Sderaadt  */
331df930be7Sderaadt static void
putdir(char * buf,size_t size)3324e95fccfSderaadt putdir(char *buf, size_t size)
333df930be7Sderaadt {
334df930be7Sderaadt 	struct direct cvtbuf;
335e073c79dSmpech 	struct odirect *odp;
336df930be7Sderaadt 	struct odirect *eodp;
337e073c79dSmpech 	struct direct *dp;
3382d2a7b95Shenning 	size_t loc, i;
339df930be7Sderaadt 
340df930be7Sderaadt 	if (cvtflag) {
341df930be7Sderaadt 		eodp = (struct odirect *)&buf[size];
342df930be7Sderaadt 		for (odp = (struct odirect *)buf; odp < eodp; odp++)
343df930be7Sderaadt 			if (odp->d_ino != 0) {
344df930be7Sderaadt 				dcvt(odp, &cvtbuf);
345df930be7Sderaadt 				putent(&cvtbuf);
346df930be7Sderaadt 			}
347df930be7Sderaadt 	} else {
348df930be7Sderaadt 		for (loc = 0; loc < size; ) {
349df930be7Sderaadt 			dp = (struct direct *)(buf + loc);
350de5f1e61Smillert 			if (Bcvt) {
351de5f1e61Smillert 				dp->d_ino = swap32(dp->d_ino);
352de5f1e61Smillert 				dp->d_reclen = swap16(dp->d_reclen);
353de5f1e61Smillert 			}
354df930be7Sderaadt 			if (oldinofmt && dp->d_ino != 0) {
355df930be7Sderaadt #				if BYTE_ORDER == BIG_ENDIAN
356df930be7Sderaadt 					if (Bcvt)
357df930be7Sderaadt 						dp->d_namlen = dp->d_type;
358df930be7Sderaadt #				else
359df930be7Sderaadt 					if (!Bcvt)
360df930be7Sderaadt 						dp->d_namlen = dp->d_type;
361df930be7Sderaadt #				endif
362df930be7Sderaadt 				dp->d_type = DT_UNKNOWN;
363df930be7Sderaadt 			}
364df930be7Sderaadt 			i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1));
365df930be7Sderaadt 			if ((dp->d_reclen & 0x3) != 0 ||
366df930be7Sderaadt 			    dp->d_reclen > i ||
367*da5362d5Sguenther 			    dp->d_reclen < DIRSIZ(dp) ||
368df930be7Sderaadt 			    dp->d_namlen > NAME_MAX) {
369a916033eSmillert 				Vprintf(stdout, "Mangled directory: ");
370df930be7Sderaadt 				if ((dp->d_reclen & 0x3) != 0)
371a916033eSmillert 					Vprintf(stdout,
372df930be7Sderaadt 					   "reclen not multiple of 4 ");
373*da5362d5Sguenther 				if (dp->d_reclen < DIRSIZ(dp))
374a916033eSmillert 					Vprintf(stdout,
3752d2a7b95Shenning 					   "reclen less than DIRSIZ (%u < %u) ",
3762d2a7b95Shenning 					   (unsigned)dp->d_reclen,
377*da5362d5Sguenther 					   (unsigned)DIRSIZ(dp));
378df930be7Sderaadt 				if (dp->d_namlen > NAME_MAX)
379a916033eSmillert 					Vprintf(stdout,
3802d2a7b95Shenning 					   "reclen name too big (%u > %u) ",
3812d2a7b95Shenning 					   (unsigned)dp->d_namlen, NAME_MAX);
382a916033eSmillert 				Vprintf(stdout, "\n");
383df930be7Sderaadt 				loc += i;
384df930be7Sderaadt 				continue;
385df930be7Sderaadt 			}
386df930be7Sderaadt 			loc += dp->d_reclen;
387df930be7Sderaadt 			if (dp->d_ino != 0) {
388df930be7Sderaadt 				putent(dp);
389df930be7Sderaadt 			}
390df930be7Sderaadt 		}
391df930be7Sderaadt 	}
392df930be7Sderaadt }
393df930be7Sderaadt 
394df930be7Sderaadt /*
395df930be7Sderaadt  * These variables are "local" to the following two functions.
396df930be7Sderaadt  */
397df930be7Sderaadt char dirbuf[DIRBLKSIZ];
398df930be7Sderaadt long dirloc = 0;
399df930be7Sderaadt long prev = 0;
400df930be7Sderaadt 
401df930be7Sderaadt /*
402df930be7Sderaadt  * add a new directory entry to a file.
403df930be7Sderaadt  */
404df930be7Sderaadt static void
putent(struct direct * dp)4054e95fccfSderaadt putent(struct direct *dp)
406df930be7Sderaadt {
407*da5362d5Sguenther 	dp->d_reclen = DIRSIZ(dp);
408df930be7Sderaadt 	if (dirloc + dp->d_reclen > DIRBLKSIZ) {
409df930be7Sderaadt 		((struct direct *)(dirbuf + prev))->d_reclen =
410df930be7Sderaadt 		    DIRBLKSIZ - prev;
411df930be7Sderaadt 		(void)fwrite(dirbuf, 1, DIRBLKSIZ, df);
412df930be7Sderaadt 		dirloc = 0;
413df930be7Sderaadt 	}
41434278641Stedu 	memcpy(dirbuf + dirloc, dp, dp->d_reclen);
415df930be7Sderaadt 	prev = dirloc;
416df930be7Sderaadt 	dirloc += dp->d_reclen;
417df930be7Sderaadt }
418df930be7Sderaadt 
419df930be7Sderaadt /*
420df930be7Sderaadt  * flush out a directory that is finished.
421df930be7Sderaadt  */
422df930be7Sderaadt static void
flushent(void)4234e95fccfSderaadt flushent(void)
424df930be7Sderaadt {
425df930be7Sderaadt 	((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev;
4263085c8d2Sguenther 	(void)fwrite(dirbuf, dirloc, 1, df);
427df930be7Sderaadt 	seekpt = ftell(df);
428df930be7Sderaadt 	dirloc = 0;
429df930be7Sderaadt }
430df930be7Sderaadt 
431df930be7Sderaadt static void
dcvt(struct odirect * odp,struct direct * ndp)4324e95fccfSderaadt dcvt(struct odirect *odp, struct direct *ndp)
433df930be7Sderaadt {
434df930be7Sderaadt 
43534278641Stedu 	memset(ndp, 0, sizeof *ndp);
436de5f1e61Smillert 	if (Bcvt)
437de5f1e61Smillert 	    ndp->d_ino = swap16(odp->d_ino);
438de5f1e61Smillert 	else
439df930be7Sderaadt 	    ndp->d_ino = odp->d_ino;
440df930be7Sderaadt 	ndp->d_type = DT_UNKNOWN;
441df930be7Sderaadt 	(void)strncpy(ndp->d_name, odp->d_name, ODIRSIZ);
442df930be7Sderaadt 	ndp->d_namlen = strlen(ndp->d_name);
443*da5362d5Sguenther 	ndp->d_reclen = DIRSIZ(ndp);
444df930be7Sderaadt }
445df930be7Sderaadt 
446df930be7Sderaadt /*
447df930be7Sderaadt  * Seek to an entry in a directory.
448df930be7Sderaadt  * Only values returned by rst_telldir should be passed to rst_seekdir.
449df930be7Sderaadt  * This routine handles many directories in a single file.
450df930be7Sderaadt  * It takes the base of the directory in the file, plus
451df930be7Sderaadt  * the desired seek offset into it.
452df930be7Sderaadt  */
453df930be7Sderaadt static void
rst_seekdir(RST_DIR * dirp,long loc,long base)4544e95fccfSderaadt rst_seekdir(RST_DIR *dirp, long loc, long base)
455df930be7Sderaadt {
456df930be7Sderaadt 
457df930be7Sderaadt 	if (loc == rst_telldir(dirp))
458df930be7Sderaadt 		return;
459df930be7Sderaadt 	loc -= base;
460df930be7Sderaadt 	if (loc < 0)
461a916033eSmillert 		fprintf(stderr, "bad seek pointer to rst_seekdir %ld\n", loc);
462df930be7Sderaadt 	(void)lseek(dirp->dd_fd, base + (loc & ~(DIRBLKSIZ - 1)), SEEK_SET);
463df930be7Sderaadt 	dirp->dd_loc = loc & (DIRBLKSIZ - 1);
464df930be7Sderaadt 	if (dirp->dd_loc != 0)
465df930be7Sderaadt 		dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ);
466df930be7Sderaadt }
467df930be7Sderaadt 
468df930be7Sderaadt /*
469df930be7Sderaadt  * get next entry in a directory.
470df930be7Sderaadt  */
471df930be7Sderaadt struct direct *
rst_readdir(RST_DIR * dirp)4724e95fccfSderaadt rst_readdir(RST_DIR *dirp)
473df930be7Sderaadt {
474e073c79dSmpech 	struct direct *dp;
475df930be7Sderaadt 
476df930be7Sderaadt 	for (;;) {
477df930be7Sderaadt 		if (dirp->dd_loc == 0) {
478df930be7Sderaadt 			dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf,
479df930be7Sderaadt 			    DIRBLKSIZ);
480df930be7Sderaadt 			if (dirp->dd_size <= 0) {
481a916033eSmillert 				Dprintf(stderr, "error reading directory\n");
482df930be7Sderaadt 				return (NULL);
483df930be7Sderaadt 			}
484df930be7Sderaadt 		}
485df930be7Sderaadt 		if (dirp->dd_loc >= dirp->dd_size) {
486df930be7Sderaadt 			dirp->dd_loc = 0;
487df930be7Sderaadt 			continue;
488df930be7Sderaadt 		}
489df930be7Sderaadt 		dp = (struct direct *)(dirp->dd_buf + dirp->dd_loc);
490df930be7Sderaadt 		if (dp->d_reclen == 0 ||
491df930be7Sderaadt 		    dp->d_reclen > DIRBLKSIZ + 1 - dirp->dd_loc) {
492a916033eSmillert 			Dprintf(stderr, "corrupted directory: bad reclen %d\n",
493df930be7Sderaadt 				dp->d_reclen);
494df930be7Sderaadt 			return (NULL);
495df930be7Sderaadt 		}
496df930be7Sderaadt 		dirp->dd_loc += dp->d_reclen;
497df930be7Sderaadt 		if (dp->d_ino == 0 && strcmp(dp->d_name, "/") == 0)
498df930be7Sderaadt 			return (NULL);
499df930be7Sderaadt 		if (dp->d_ino >= maxino) {
5003b92bd08Sderaadt 			Dprintf(stderr, "corrupted directory: bad inum %llu\n",
5013b92bd08Sderaadt 			    (unsigned long long)dp->d_ino);
502df930be7Sderaadt 			continue;
503df930be7Sderaadt 		}
504df930be7Sderaadt 		return (dp);
505df930be7Sderaadt 	}
506df930be7Sderaadt }
507df930be7Sderaadt 
508df930be7Sderaadt /*
509df930be7Sderaadt  * Simulate the opening of a directory
510df930be7Sderaadt  */
511df930be7Sderaadt RST_DIR *
rst_opendir(const char * name)5124e95fccfSderaadt rst_opendir(const char *name)
513df930be7Sderaadt {
514df930be7Sderaadt 	struct inotab *itp;
515df930be7Sderaadt 	RST_DIR *dirp;
516df930be7Sderaadt 	ino_t ino;
517df930be7Sderaadt 
518df930be7Sderaadt 	if ((ino = dirlookup(name)) > 0 &&
519df930be7Sderaadt 	    (itp = inotablookup(ino)) != NULL) {
520df930be7Sderaadt 		dirp = opendirfile(dirfile);
521df930be7Sderaadt 		rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
522df930be7Sderaadt 		return (dirp);
523df930be7Sderaadt 	}
524df930be7Sderaadt 	return (NULL);
525df930be7Sderaadt }
526df930be7Sderaadt 
527df930be7Sderaadt /*
528df930be7Sderaadt  * In our case, there is nothing to do when closing a directory.
529df930be7Sderaadt  */
530df930be7Sderaadt void
rst_closedir(RST_DIR * dirp)5314e95fccfSderaadt rst_closedir(RST_DIR *dirp)
532df930be7Sderaadt {
533df930be7Sderaadt 	(void)close(dirp->dd_fd);
534df930be7Sderaadt 	free(dirp);
535df930be7Sderaadt 	return;
536df930be7Sderaadt }
537df930be7Sderaadt 
538df930be7Sderaadt /*
539df930be7Sderaadt  * Simulate finding the current offset in the directory.
540df930be7Sderaadt  */
541df930be7Sderaadt static long
rst_telldir(RST_DIR * dirp)5424e95fccfSderaadt rst_telldir(RST_DIR *dirp)
543df930be7Sderaadt {
544df930be7Sderaadt 	return ((long)lseek(dirp->dd_fd,
545df930be7Sderaadt 	    (off_t)0, SEEK_CUR) - dirp->dd_size + dirp->dd_loc);
546df930be7Sderaadt }
547df930be7Sderaadt 
548df930be7Sderaadt /*
549df930be7Sderaadt  * Open a directory file.
550df930be7Sderaadt  */
551df930be7Sderaadt static RST_DIR *
opendirfile(const char * name)5524e95fccfSderaadt opendirfile(const char *name)
553df930be7Sderaadt {
554e073c79dSmpech 	RST_DIR *dirp;
555e073c79dSmpech 	int fd;
556df930be7Sderaadt 
557df930be7Sderaadt 	if ((fd = open(name, O_RDONLY)) == -1)
558df930be7Sderaadt 		return (NULL);
559df930be7Sderaadt 	if ((dirp = malloc(sizeof(RST_DIR))) == NULL) {
560df930be7Sderaadt 		(void)close(fd);
561df930be7Sderaadt 		return (NULL);
562df930be7Sderaadt 	}
563df930be7Sderaadt 	dirp->dd_fd = fd;
564df930be7Sderaadt 	dirp->dd_loc = 0;
565df930be7Sderaadt 	return (dirp);
566df930be7Sderaadt }
567df930be7Sderaadt 
568df930be7Sderaadt /*
569df930be7Sderaadt  * Set the mode, owner, and times for all new or changed directories
570df930be7Sderaadt  */
571df930be7Sderaadt void
setdirmodes(int flags)5724e95fccfSderaadt setdirmodes(int flags)
573df930be7Sderaadt {
574df930be7Sderaadt 	FILE *mf;
575df930be7Sderaadt 	struct modeinfo node;
576df930be7Sderaadt 	struct entry *ep;
577df930be7Sderaadt 	char *cp;
578df930be7Sderaadt 
579a916033eSmillert 	Vprintf(stdout, "Set directory mode, owner, and times.\n");
580f79cb2fdSderaadt 	if (command == 'r' || command == 'R')
581520eb379Sotto 		(void)snprintf(modefile, sizeof(modefile), "%s/rstmode%lld",
582520eb379Sotto 		    tmpdir, (long long)dumpdate);
583f79cb2fdSderaadt 	if (modefile[0] == '#') {
584f79cb2fdSderaadt 		panic("modefile not defined\n");
585a916033eSmillert 		fputs("directory mode, owner, and times not set\n", stderr);
586f79cb2fdSderaadt 		return;
587f79cb2fdSderaadt 	}
588df930be7Sderaadt 	mf = fopen(modefile, "r");
589df930be7Sderaadt 	if (mf == NULL) {
590a916033eSmillert 		warn("fopen");
591df930be7Sderaadt 		fprintf(stderr, "cannot open mode file %s\n", modefile);
592df930be7Sderaadt 		fprintf(stderr, "directory mode, owner, and times not set\n");
593df930be7Sderaadt 		return;
594df930be7Sderaadt 	}
595df930be7Sderaadt 	clearerr(mf);
596df930be7Sderaadt 	for (;;) {
597df930be7Sderaadt 		(void)fread((char *)&node, 1, sizeof(struct modeinfo), mf);
598df930be7Sderaadt 		if (feof(mf))
599df930be7Sderaadt 			break;
600df930be7Sderaadt 		ep = lookupino(node.ino);
601df930be7Sderaadt 		if (command == 'i' || command == 'x') {
602df930be7Sderaadt 			if (ep == NULL)
603df930be7Sderaadt 				continue;
604df930be7Sderaadt 			if ((flags & FORCE) == 0 && ep->e_flags & EXISTED) {
605df930be7Sderaadt 				ep->e_flags &= ~NEW;
606df930be7Sderaadt 				continue;
607df930be7Sderaadt 			}
608df930be7Sderaadt 			if (node.ino == ROOTINO &&
609df930be7Sderaadt 		   	    reply("set owner/mode for '.'") == FAIL)
610df930be7Sderaadt 				continue;
611df930be7Sderaadt 		}
612df930be7Sderaadt 		if (ep == NULL) {
6133b92bd08Sderaadt 			panic("cannot find directory inode %llu\n",
6143b92bd08Sderaadt 			    (unsigned long long)node.ino);
615df930be7Sderaadt 		} else {
6160155e653Smillert 			if (!Nflag) {
617df930be7Sderaadt 				cp = myname(ep);
618df930be7Sderaadt 				(void)chown(cp, node.uid, node.gid);
619df930be7Sderaadt 				(void)chmod(cp, node.mode);
620df930be7Sderaadt 				(void)chflags(cp, node.flags);
621a740969bSguenther 				(void)utimensat(AT_FDCWD, cp, node.ctimep, 0);
622a740969bSguenther 				(void)utimensat(AT_FDCWD, cp, node.mtimep, 0);
6230155e653Smillert 			}
624df930be7Sderaadt 			ep->e_flags &= ~NEW;
625df930be7Sderaadt 		}
626df930be7Sderaadt 	}
627df930be7Sderaadt 	if (ferror(mf))
628df930be7Sderaadt 		panic("error setting directory modes\n");
629df930be7Sderaadt 	(void)fclose(mf);
630df930be7Sderaadt }
631df930be7Sderaadt 
632df930be7Sderaadt /*
633df930be7Sderaadt  * Generate a literal copy of a directory.
634df930be7Sderaadt  */
635df930be7Sderaadt int
genliteraldir(char * name,ino_t ino)6364e95fccfSderaadt genliteraldir(char *name, ino_t ino)
637df930be7Sderaadt {
638e073c79dSmpech 	struct inotab *itp;
639df930be7Sderaadt 	int ofile, dp, i, size;
640df930be7Sderaadt 	char buf[BUFSIZ];
641df930be7Sderaadt 
642df930be7Sderaadt 	itp = inotablookup(ino);
643df930be7Sderaadt 	if (itp == NULL)
6443b92bd08Sderaadt 		panic("Cannot find directory inode %llu named %s\n",
6453b92bd08Sderaadt 		    (unsigned long long)ino, name);
646df69c215Sderaadt 	if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1) {
647a916033eSmillert 		warn("%s: cannot create file", name);
648df930be7Sderaadt 		return (FAIL);
649df930be7Sderaadt 	}
650df930be7Sderaadt 	rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
651df930be7Sderaadt 	dp = dup(dirp->dd_fd);
652df930be7Sderaadt 	for (i = itp->t_size; i > 0; i -= BUFSIZ) {
653df930be7Sderaadt 		size = i < BUFSIZ ? i : BUFSIZ;
6543085c8d2Sguenther 		if (read(dp, buf, size) == -1)
6553085c8d2Sguenther 			err(1, "read error extracting inode %llu, name %s",
6563b92bd08Sderaadt 			    (unsigned long long)curfile.ino, curfile.name);
6573085c8d2Sguenther 		xtrfile(buf, size);
658df930be7Sderaadt 	}
659df930be7Sderaadt 	(void)close(dp);
660df930be7Sderaadt 	(void)close(ofile);
661df930be7Sderaadt 	return (GOOD);
662df930be7Sderaadt }
663df930be7Sderaadt 
664df930be7Sderaadt /*
665df930be7Sderaadt  * Determine the type of an inode
666df930be7Sderaadt  */
667df930be7Sderaadt int
inodetype(ino_t ino)6684e95fccfSderaadt inodetype(ino_t ino)
669df930be7Sderaadt {
670df930be7Sderaadt 	struct inotab *itp;
671df930be7Sderaadt 
672df930be7Sderaadt 	itp = inotablookup(ino);
673df930be7Sderaadt 	if (itp == NULL)
674df930be7Sderaadt 		return (LEAF);
675df930be7Sderaadt 	return (NODE);
676df930be7Sderaadt }
677df930be7Sderaadt 
678df930be7Sderaadt /*
679df930be7Sderaadt  * Allocate and initialize a directory inode entry.
680df930be7Sderaadt  * If requested, save its pertinent mode, owner, and time info.
681df930be7Sderaadt  */
682df930be7Sderaadt static struct inotab *
allocinotab(FILE * mf,struct context * ctxp,long seekpt)6830155e653Smillert allocinotab(FILE *mf, struct context *ctxp, long seekpt)
684df930be7Sderaadt {
685e073c79dSmpech 	struct inotab	*itp;
686df930be7Sderaadt 	struct modeinfo node;
687df930be7Sderaadt 
688df930be7Sderaadt 	itp = calloc(1, sizeof(struct inotab));
689df930be7Sderaadt 	if (itp == NULL)
690df930be7Sderaadt 		panic("no memory directory table\n");
6910155e653Smillert 	itp->t_next = inotab[INOHASH(ctxp->ino)];
6920155e653Smillert 	inotab[INOHASH(ctxp->ino)] = itp;
6930155e653Smillert 	itp->t_ino = ctxp->ino;
694df930be7Sderaadt 	itp->t_seekpt = seekpt;
695df930be7Sderaadt 	if (mf == NULL)
696df930be7Sderaadt 		return (itp);
6970155e653Smillert 	node.ino = ctxp->ino;
6980155e653Smillert 	node.mtimep[0].tv_sec = ctxp->atime_sec;
699a740969bSguenther 	node.mtimep[0].tv_nsec = ctxp->atime_nsec;
7000155e653Smillert 	node.mtimep[1].tv_sec = ctxp->mtime_sec;
701a740969bSguenther 	node.mtimep[1].tv_nsec = ctxp->mtime_nsec;
7020155e653Smillert 	node.ctimep[0].tv_sec = ctxp->atime_sec;
703a740969bSguenther 	node.ctimep[0].tv_nsec = ctxp->atime_nsec;
7040155e653Smillert 	node.ctimep[1].tv_sec = ctxp->birthtime_sec;
705a740969bSguenther 	node.ctimep[1].tv_nsec = ctxp->birthtime_nsec;
7060155e653Smillert 	node.mode = ctxp->mode;
7070155e653Smillert 	node.flags = ctxp->file_flags;
7080155e653Smillert 	node.uid = ctxp->uid;
7090155e653Smillert 	node.gid = ctxp->gid;
710df930be7Sderaadt 	(void)fwrite((char *)&node, 1, sizeof(struct modeinfo), mf);
711df930be7Sderaadt 	return (itp);
712df930be7Sderaadt }
713df930be7Sderaadt 
714df930be7Sderaadt /*
715df930be7Sderaadt  * Look up an inode in the table of directories
716df930be7Sderaadt  */
717df930be7Sderaadt static struct inotab *
inotablookup(ino_t ino)7184e95fccfSderaadt inotablookup(ino_t ino)
719df930be7Sderaadt {
720e073c79dSmpech 	struct inotab *itp;
721df930be7Sderaadt 
722df930be7Sderaadt 	for (itp = inotab[INOHASH(ino)]; itp != NULL; itp = itp->t_next)
723df930be7Sderaadt 		if (itp->t_ino == ino)
724df930be7Sderaadt 			return (itp);
725df930be7Sderaadt 	return (NULL);
726df930be7Sderaadt }
727df930be7Sderaadt 
728df930be7Sderaadt /*
729df930be7Sderaadt  * Clean up and exit
730df930be7Sderaadt  */
731df930be7Sderaadt void
cleanup(void)7324e95fccfSderaadt cleanup(void)
733df930be7Sderaadt {
734df930be7Sderaadt 
735df930be7Sderaadt 	closemt();
736df930be7Sderaadt 	if (modefile[0] != '#')
737df930be7Sderaadt 		(void)unlink(modefile);
738df930be7Sderaadt 	if (dirfile[0] != '#')
739df930be7Sderaadt 		(void)unlink(dirfile);
740df930be7Sderaadt }
741