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