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