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