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