Home | History | Annotate | Line # | Download | only in restore
      1  1.55       chs /*	$NetBSD: dirs.c,v 1.55 2022/12/12 16:53:30 chs 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.55       chs __RCSID("$NetBSD: dirs.c,v 1.55 2022/12/12 16:53:30 chs 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.53  riastrad static void		 fail_dirtmp(char *) __dead;
    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.54       chs 	uid_t myuid;
    602  1.24        pk 
    603   1.6       cgd 	vprintf(stdout, "Set directory mode, owner, and times.\n");
    604  1.20     lukem 	if (command == 'r' || command == 'R')
    605  1.52  christos 		(void) snprintf(modefile, sizeof(modefile), "%s/rstmode%jd",
    606  1.52  christos 		    tmpdir, (intmax_t)dumpdate);
    607  1.20     lukem 	if (modefile[0] == '#') {
    608  1.20     lukem 		panic("modefile not defined\n");
    609  1.20     lukem 		fprintf(stderr, "directory mode, owner, and times not set\n");
    610  1.20     lukem 		return;
    611  1.20     lukem 	}
    612   1.6       cgd 	mf = fopen(modefile, "r");
    613   1.6       cgd 	if (mf == NULL) {
    614   1.6       cgd 		fprintf(stderr, "fopen: %s\n", strerror(errno));
    615   1.6       cgd 		fprintf(stderr, "cannot open mode file %s\n", modefile);
    616   1.6       cgd 		fprintf(stderr, "directory mode, owner, and times not set\n");
    617   1.6       cgd 		return;
    618   1.6       cgd 	}
    619   1.6       cgd 	clearerr(mf);
    620  1.52  christos 	bufsize = 0;
    621  1.52  christos 	buf = NULL;
    622  1.54       chs 	myuid = getuid();
    623   1.6       cgd 	for (;;) {
    624   1.6       cgd 		(void) fread((char *)&node, 1, sizeof(struct modeinfo), mf);
    625  1.52  christos 		if (ferror(mf)) {
    626  1.52  christos 			warn("%s: cannot read modefile.", modefile);
    627  1.52  christos 			fprintf(stderr, "Mode, owner, and times not set.\n");
    628  1.52  christos 			break;
    629  1.52  christos 		}
    630   1.6       cgd 		if (feof(mf))
    631   1.6       cgd 			break;
    632  1.52  christos 		if (node.extsize > 0) {
    633  1.52  christos 			if (bufsize < node.extsize) {
    634  1.52  christos 				if (bufsize > 0)
    635  1.52  christos 					free(buf);
    636  1.52  christos 				if ((buf = malloc(node.extsize)) != NULL) {
    637  1.52  christos 					bufsize = node.extsize;
    638  1.52  christos 				} else {
    639  1.52  christos 					bufsize = 0;
    640  1.52  christos 				}
    641  1.52  christos 			}
    642  1.52  christos 			if (bufsize >= node.extsize) {
    643  1.52  christos 				(void) fread(buf, 1, node.extsize, mf);
    644  1.52  christos 				if (ferror(mf)) {
    645  1.52  christos 					warn("%s: cannot read modefile.",
    646  1.52  christos 					    modefile);
    647  1.52  christos 					fprintf(stderr, "Not all external ");
    648  1.52  christos 					fprintf(stderr, "attributes set.\n");
    649  1.52  christos 					break;
    650  1.52  christos 				}
    651  1.52  christos 			} else {
    652  1.52  christos 				(void) fseek(mf, node.extsize, SEEK_CUR);
    653  1.52  christos 				if (ferror(mf)) {
    654  1.52  christos 					warn("%s: cannot seek in modefile.",
    655  1.52  christos 					    modefile);
    656  1.52  christos 					fprintf(stderr, "Not all directory ");
    657  1.52  christos 					fprintf(stderr, "attributes set.\n");
    658  1.52  christos 					break;
    659  1.52  christos 				}
    660  1.52  christos 			}
    661  1.52  christos 		}
    662   1.6       cgd 		ep = lookupino(node.ino);
    663   1.6       cgd 		if (command == 'i' || command == 'x') {
    664   1.6       cgd 			if (ep == NULL)
    665   1.6       cgd 				continue;
    666   1.6       cgd 			if ((flags & FORCE) == 0 && ep->e_flags & EXISTED) {
    667   1.6       cgd 				ep->e_flags &= ~NEW;
    668   1.6       cgd 				continue;
    669   1.6       cgd 			}
    670  1.49  dholland 			if (node.ino == UFS_ROOTINO && dotflag == 0)
    671   1.6       cgd 				continue;
    672   1.6       cgd 		}
    673   1.6       cgd 		if (ep == NULL) {
    674  1.52  christos 			panic("cannot find directory inode %ju\n",
    675  1.52  christos 			    (uintmax_t)node.ino);
    676  1.52  christos 			continue;
    677  1.52  christos 		}
    678  1.55       chs 		cp = myname(ep);
    679  1.52  christos 		if (!Nflag) {
    680  1.55       chs 			if (myuid != 0)
    681  1.55       chs 				(void) chown(cp, myuid, node.gid);
    682  1.55       chs 			else
    683  1.55       chs 				(void) chown(cp, node.uid, node.gid);
    684  1.55       chs 			(void) chmod(cp, node.mode);
    685  1.52  christos 			if (node.extsize > 0) {
    686  1.52  christos 				if (bufsize >= node.extsize) {
    687  1.52  christos 					set_extattr(-1, cp, buf, node.extsize, SXA_FILE);
    688  1.52  christos 				} else {
    689  1.52  christos 					fprintf(stderr, "Cannot restore %s%s\n",
    690  1.52  christos 					    "extended attributes for ", cp);
    691  1.52  christos 				}
    692  1.33     enami 			}
    693  1.52  christos 			(void) utimens(cp, node.ctimep);
    694  1.52  christos 			(void) utimens(cp, node.mtimep);
    695  1.52  christos 			if (Mtreefile) {
    696  1.52  christos 				writemtree(cp, "dir",
    697  1.52  christos 				    node.uid, node.gid, node.mode,
    698  1.52  christos 				    node.flags);
    699  1.52  christos 			} else
    700  1.52  christos 				(void) chflags(cp, node.flags);
    701   1.6       cgd 		}
    702  1.52  christos 		ep->e_flags &= ~NEW;
    703   1.6       cgd 	}
    704  1.52  christos 	if (bufsize > 0)
    705  1.52  christos 		free(buf);
    706   1.6       cgd 	(void) fclose(mf);
    707   1.6       cgd }
    708   1.6       cgd 
    709   1.6       cgd /*
    710   1.6       cgd  * Generate a literal copy of a directory.
    711   1.6       cgd  */
    712   1.6       cgd int
    713  1.45  christos genliteraldir(const char *name, ino_t ino)
    714   1.6       cgd {
    715  1.22     lukem 	struct inotab *itp;
    716   1.6       cgd 	int ofile, dp, i, size;
    717   1.6       cgd 	char buf[BUFSIZ];
    718   1.6       cgd 
    719   1.6       cgd 	itp = inotablookup(ino);
    720   1.6       cgd 	if (itp == NULL)
    721  1.52  christos 		panic("Cannot find directory inode %ju named %s\n",
    722  1.52  christos 		    (uintmax_t)ino, name);
    723  1.10   mycroft 	if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) {
    724   1.6       cgd 		fprintf(stderr, "%s: ", name);
    725   1.6       cgd 		(void) fflush(stderr);
    726   1.6       cgd 		fprintf(stderr, "cannot create file: %s\n", strerror(errno));
    727   1.6       cgd 		return (FAIL);
    728   1.6       cgd 	}
    729   1.6       cgd 	rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
    730   1.6       cgd 	dp = dup(dirp->dd_fd);
    731   1.6       cgd 	for (i = itp->t_size; i > 0; i -= BUFSIZ) {
    732  1.52  christos 		size = MIN(i, BUFSIZ);
    733   1.6       cgd 		if (read(dp, buf, (int) size) == -1) {
    734   1.6       cgd 			fprintf(stderr,
    735  1.52  christos 			    "write error extracting inode %ju, name %s\n",
    736  1.52  christos 			    (uintmax_t)curfile.ino, curfile.name);
    737   1.6       cgd 			fprintf(stderr, "read: %s\n", strerror(errno));
    738  1.12   mycroft 			exit(1);
    739   1.6       cgd 		}
    740   1.6       cgd 		if (!Nflag && write(ofile, buf, (int) size) == -1) {
    741   1.6       cgd 			fprintf(stderr,
    742  1.52  christos 			    "write error extracting inode %ju, name %s\n",
    743  1.52  christos 			    (uintmax_t)curfile.ino, curfile.name);
    744   1.6       cgd 			fprintf(stderr, "write: %s\n", strerror(errno));
    745  1.12   mycroft 			exit(1);
    746   1.6       cgd 		}
    747   1.6       cgd 	}
    748   1.6       cgd 	(void) close(dp);
    749   1.6       cgd 	(void) close(ofile);
    750   1.6       cgd 	return (GOOD);
    751   1.6       cgd }
    752   1.6       cgd 
    753   1.6       cgd /*
    754   1.6       cgd  * Determine the type of an inode
    755   1.6       cgd  */
    756   1.6       cgd int
    757  1.43   xtraeme inodetype(ino_t ino)
    758   1.6       cgd {
    759   1.6       cgd 	struct inotab *itp;
    760   1.6       cgd 
    761   1.6       cgd 	itp = inotablookup(ino);
    762   1.6       cgd 	if (itp == NULL)
    763   1.6       cgd 		return (LEAF);
    764   1.6       cgd 	return (NODE);
    765   1.6       cgd }
    766   1.6       cgd 
    767   1.6       cgd /*
    768   1.6       cgd  * Allocate and initialize a directory inode entry.
    769   1.6       cgd  * If requested, save its pertinent mode, owner, and time info.
    770   1.6       cgd  */
    771   1.6       cgd static struct inotab *
    772  1.52  christos allocinotab(struct context *ctxp, long aseekpt)
    773   1.6       cgd {
    774  1.22     lukem 	struct inotab	*itp;
    775   1.6       cgd 	struct modeinfo node;
    776   1.6       cgd 
    777   1.6       cgd 	itp = calloc(1, sizeof(struct inotab));
    778   1.6       cgd 	if (itp == NULL)
    779  1.52  christos 		panic("no memory for directory table\n");
    780  1.38      fvdl 	itp->t_next = inotab[INOHASH(ctxp->ino)];
    781  1.38      fvdl 	inotab[INOHASH(ctxp->ino)] = itp;
    782  1.38      fvdl 	itp->t_ino = ctxp->ino;
    783  1.34     lukem 	itp->t_seekpt = aseekpt;
    784   1.6       cgd 	if (mf == NULL)
    785   1.7   mycroft 		return (itp);
    786  1.38      fvdl 	node.ino = ctxp->ino;
    787  1.38      fvdl 	node.mtimep[0].tv_sec = ctxp->atime_sec;
    788  1.51     enami 	node.mtimep[0].tv_nsec = ctxp->atime_nsec;
    789  1.38      fvdl 	node.mtimep[1].tv_sec = ctxp->mtime_sec;
    790  1.51     enami 	node.mtimep[1].tv_nsec = ctxp->mtime_nsec;
    791  1.38      fvdl 	node.ctimep[0].tv_sec = ctxp->atime_sec;
    792  1.51     enami 	node.ctimep[0].tv_nsec = ctxp->atime_nsec;
    793  1.38      fvdl 	node.ctimep[1].tv_sec = ctxp->birthtime_sec;
    794  1.51     enami 	node.ctimep[1].tv_nsec = ctxp->birthtime_nsec;
    795  1.52  christos 	node.extsize = ctxp->extsize;
    796  1.38      fvdl 	node.mode = ctxp->mode;
    797  1.38      fvdl 	node.flags = ctxp->file_flags;
    798  1.38      fvdl 	node.uid = ctxp->uid;
    799  1.38      fvdl 	node.gid = ctxp->gid;
    800  1.52  christos 	if (fwrite((char *)&node, sizeof(struct modeinfo), 1, mf) != 1)
    801  1.52  christos 		fail_dirtmp(modefile);
    802   1.7   mycroft 	return (itp);
    803   1.6       cgd }
    804   1.6       cgd 
    805   1.6       cgd /*
    806   1.6       cgd  * Look up an inode in the table of directories
    807   1.6       cgd  */
    808   1.6       cgd static struct inotab *
    809  1.43   xtraeme inotablookup(ino_t ino)
    810   1.6       cgd {
    811  1.22     lukem 	struct inotab *itp;
    812   1.6       cgd 
    813   1.6       cgd 	for (itp = inotab[INOHASH(ino)]; itp != NULL; itp = itp->t_next)
    814   1.6       cgd 		if (itp->t_ino == ino)
    815   1.7   mycroft 			return (itp);
    816   1.6       cgd 	return (NULL);
    817   1.6       cgd }
    818   1.6       cgd 
    819   1.6       cgd /*
    820   1.6       cgd  * Clean up and exit
    821   1.6       cgd  */
    822  1.12   mycroft void
    823  1.43   xtraeme cleanup(void)
    824   1.6       cgd {
    825   1.6       cgd 
    826   1.6       cgd 	closemt();
    827  1.52  christos 	if (modefile[0] != '#') {
    828  1.52  christos 		(void) truncate(modefile, 0);
    829   1.6       cgd 		(void) unlink(modefile);
    830  1.52  christos 	}
    831  1.52  christos 	if (dirfile[0] != '#') {
    832  1.52  christos 		(void) truncate(dirfile, 0);
    833   1.6       cgd 		(void) unlink(dirfile);
    834  1.52  christos 	}
    835  1.52  christos }
    836  1.52  christos 
    837  1.52  christos /*
    838  1.52  christos  * Print out information about the failure to save directory,
    839  1.52  christos  * extended attribute, and mode information.
    840  1.52  christos  */
    841  1.53  riastrad static void __dead
    842  1.52  christos fail_dirtmp(char *filename)
    843  1.52  christos {
    844  1.52  christos 	warn("%s: cannot write directory database", filename);
    845  1.52  christos 	if (errno == ENOSPC) {
    846  1.52  christos 		fprintf(stderr, "Try making space in %s, %s\n%s\n", tmpdir,
    847  1.52  christos 		    "or set environment variable TMPDIR",
    848  1.52  christos 		    "to an alternate location with more disk space.");
    849  1.52  christos 	}
    850  1.52  christos 	exit(1);
    851   1.6       cgd }
    852