1 /* 2 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 7 /* All Rights Reserved */ 8 9 /* 10 * Copyright (c) 1983 Regents of the University of California. 11 * All rights reserved. The Berkeley software License Agreement 12 * specifies the terms and conditions for redistribution. 13 */ 14 15 #include "restore.h" 16 #include <byteorder.h> 17 #include <stdlib.h> 18 #include <unistd.h> 19 #include <utime.h> 20 21 /* 22 * Symbol table of directories read from tape. 23 */ 24 #define HASHSIZE 1000 25 #define INOHASH(val) (val % HASHSIZE) 26 struct inotab { 27 struct inotab *t_next; 28 ino_t t_ino; 29 offset_t t_seekpt; 30 offset_t t_size; 31 struct inotab *t_xattr; 32 }; 33 static struct inotab *inotab[HASHSIZE]; 34 static struct inotab *xattrlist = NULL; 35 36 /* 37 * Information retained about directories. 38 */ 39 static struct modeinfo { 40 ino_t ino; 41 time_t timep[2]; 42 mode_t mode; 43 uid_t uid; 44 gid_t gid; 45 size_t metasize; 46 } node; 47 48 /* 49 * Global variables for this file. 50 */ 51 static off64_t g_seekpt; /* some people have a local seekpt */ 52 static FILE *df, *mf; 53 static char dirfile[MAXPATHLEN] = "#"; /* No file */ 54 static char modefile[MAXPATHLEN] = "#"; /* No file */ 55 56 static RST_DIR *dirp; 57 58 #define INIT_TEMPFILE(name, type) \ 59 if (name[0] == '#') { \ 60 if (tmpdir == (char *)NULL) /* can't happen; be paranoid */ \ 61 tmpdir = "/tmp"; \ 62 (void) snprintf(name, sizeof (name), \ 63 "%s/rst" type "%ld.XXXXXX", tmpdir, dumpdate); \ 64 (void) mktemp(name); \ 65 } 66 67 #define INIT_DIRFILE() INIT_TEMPFILE(dirfile, "dir") 68 #define INIT_MODEFILE() INIT_TEMPFILE(modefile, "mode") 69 70 /* 71 * Format of old style directories. 72 */ 73 #define ODIRSIZ 14 74 struct odirect { 75 ushort_t d_ino; 76 char d_name[ODIRSIZ]; 77 }; 78 79 static ino_t search(ino_t, char *); 80 static void putdir(char *, size_t); 81 static void putent(struct direct *); 82 static void skipmetadata(FILE *, size_t); 83 static void flushent(void); 84 static void dcvt(struct odirect *, struct direct *); 85 static RST_DIR *rst_initdirfile(char *); 86 static offset_t rst_telldir(RST_DIR *); 87 static void rst_seekdir(RST_DIR *, offset_t, offset_t); 88 static struct inotab *allocinotab(ino_t, struct dinode *, off64_t); 89 static void nodeflush(void); 90 static struct inotab *inotablookup(ino_t); 91 92 /* 93 * Extract directory contents, building up a directory structure 94 * on disk for extraction by name. 95 * If genmode is requested, save mode, owner, and times for all 96 * directories on the tape. 97 */ 98 void 99 extractdirs(int genmode) 100 { 101 int ts; 102 struct dinode *ip; 103 int saverr; 104 struct inotab *itp; 105 struct direct nulldir; 106 static char dotname[] = "."; /* dirlookup/psearch writes to its arg */ 107 108 vprintf(stdout, gettext("Extract directories from tape\n")); 109 INIT_DIRFILE(); 110 if ((df = safe_fopen(dirfile, "w", 0600)) == (FILE *)NULL) { 111 saverr = errno; 112 (void) fprintf(stderr, 113 gettext("%s: %s - cannot create directory temporary\n"), 114 progname, dirfile); 115 errno = saverr; 116 perror("fopen"); 117 done(1); 118 } 119 if (genmode != 0) { 120 INIT_MODEFILE(); 121 if ((mf = safe_fopen(modefile, "w", 0600)) == (FILE *)NULL) { 122 saverr = errno; 123 (void) fprintf(stderr, 124 gettext("%s: %s - cannot create modefile \n"), 125 progname, modefile); 126 errno = saverr; 127 perror("fopen"); 128 done(1); 129 } 130 } 131 nulldir.d_ino = 0; 132 nulldir.d_namlen = 1; 133 (void) strcpy(nulldir.d_name, "/"); 134 /* LINTED DIRSIZ will always fit into a ushort_t */ 135 nulldir.d_reclen = (ushort_t)DIRSIZ(&nulldir); 136 /* LINTED sign extension ok in assert */ 137 assert(DIRSIZ(&nulldir) == (ulong_t)nulldir.d_reclen); 138 for (;;) { 139 curfile.name = gettext("<directory file - name unknown>"); 140 curfile.action = USING; 141 ip = curfile.dip; 142 ts = curfile.ts; 143 if (ts != TS_END && ts != TS_INODE) { 144 getfile(null, null); 145 continue; 146 } 147 if (ts == TS_INODE && ip == NULL) { 148 (void) fprintf(stderr, gettext( 149 "%s: extractdirs: Failed internal consistency check, curfile.dip is NULL\n"), 150 progname); 151 done(1); 152 } 153 if ((ts == TS_INODE && (ip->di_mode & IFMT) != IFDIR && 154 (ip->di_mode & IFMT) != IFATTRDIR) || 155 (ts == TS_END)) { 156 (void) fflush(df); 157 /* XXX Legitimate error, bad complaint string */ 158 if (ferror(df)) 159 panic("%s: %s\n", dirfile, strerror(errno)); 160 (void) fclose(df); 161 rst_closedir(dirp); 162 dirp = rst_initdirfile(dirfile); 163 if (dirp == NULL) 164 perror("initdirfile"); 165 if (mf != NULL) { 166 (void) fflush(mf); 167 /* XXX Legitimate error, bad complaint string */ 168 if (ferror(mf)) 169 panic("%s: %s\n", 170 modefile, strerror(errno)); 171 (void) fclose(mf); 172 } 173 if (dirlookup(dotname) == 0) { 174 (void) fprintf(stderr, gettext( 175 "Root directory is not on tape\n")); 176 done(1); 177 } 178 return; 179 } 180 itp = allocinotab(curfile.ino, ip, g_seekpt); 181 getfile(putdir, null); 182 if (mf != NULL) 183 nodeflush(); 184 185 putent(&nulldir); 186 flushent(); 187 itp->t_size = g_seekpt - itp->t_seekpt; 188 } 189 } 190 191 /* 192 * skip over all the directories on the tape 193 */ 194 void 195 skipdirs() 196 { 197 while (curfile.dip != NULL && 198 ((curfile.dip->di_mode & IFMT) == IFDIR || 199 (curfile.dip->di_mode & IFMT) == IFATTRDIR)) { 200 skipfile(); 201 } 202 } 203 204 /* 205 * Recursively find names and inumbers of all files in subtree 206 * pname and pass them off to be processed. 207 */ 208 void 209 treescan(char *pname, ino_t ino, long (*todo)()) 210 { 211 struct inotab *itp; 212 struct direct *dp; 213 uint_t loclen; 214 offset_t bpt; 215 char locname[MAXCOMPLEXLEN]; 216 217 itp = inotablookup(ino); 218 if (itp == NULL) { 219 /* 220 * Pname is name of a simple file or an unchanged directory. 221 */ 222 (void) (*todo)(pname, ino, LEAF); 223 return; 224 } 225 /* 226 * Pname is a dumped directory name. 227 */ 228 if ((*todo)(pname, ino, NODE) == FAIL) 229 return; 230 /* 231 * begin search through the directory 232 * skipping over "." and ".." 233 */ 234 loclen = complexcpy(locname, pname, MAXCOMPLEXLEN); 235 locname[loclen-1] = '/'; 236 rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); 237 dp = rst_readdir(dirp); /* "." */ 238 239 if (dp != NULL && strcmp(dp->d_name, ".") == 0) 240 dp = rst_readdir(dirp); /* ".." */ 241 else 242 (void) fprintf(stderr, 243 gettext("Warning: `.' missing from directory %s\n"), 244 pname); 245 if (dp != NULL && strcmp(dp->d_name, "..") == 0) 246 dp = rst_readdir(dirp); /* first real entry */ 247 else 248 (void) fprintf(stderr, 249 gettext("Warning: `..' missing from directory %s\n"), 250 pname); 251 bpt = rst_telldir(dirp); 252 /* 253 * a zero inode signals end of directory 254 */ 255 while (dp != NULL && dp->d_ino != 0) { 256 locname[loclen] = '\0'; 257 if ((loclen + dp->d_namlen) >= (sizeof (locname) - 2)) { 258 (void) fprintf(stderr, 259 gettext( 260 "%s%s: ignoring name that exceeds %d char\n"), 261 locname, dp->d_name, MAXCOMPLEXLEN); 262 } else { 263 /* Always fits by if() condition */ 264 (void) strcpy(locname + loclen, dp->d_name); 265 /* put a double null on string for lookupname() */ 266 locname[loclen+dp->d_namlen+1] = '\0'; 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 if (dp == NULL) 274 (void) fprintf(stderr, 275 gettext("corrupted directory: %s.\n"), locname); 276 } 277 278 /* 279 * Scan the directory table looking for extended attribute trees. 280 * Recursively find names and inumbers in each tree and pass them 281 * off to be processed. If the always parameter is not set, only 282 * process the attribute tree if the attribute tree parent is to 283 * be extracted. 284 */ 285 void 286 attrscan(int always, long (*todo)()) 287 { 288 struct inotab *itp; 289 struct entry *ep, *parent; 290 struct direct *dp; 291 char name[MAXCOMPLEXLEN]; 292 int len; 293 294 for (itp = xattrlist; itp != NULL; itp = itp->t_xattr) { 295 rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); 296 if ((dp = rst_readdir(dirp)) != NULL && /* "." */ 297 (dp = rst_readdir(dirp)) != NULL && /* ".." */ 298 strcmp(dp->d_name, "..") == 0) { 299 if ((parent = lookupino(dp->d_ino)) != NULL) { 300 if (!always && 301 (parent->e_flags & (NEW|EXTRACT)) == 0) 302 continue; 303 len = complexcpy(name, myname(parent), 304 MAXCOMPLEXLEN - 3); 305 name[len] = '.'; 306 name[len+1] = '\0'; 307 name[len+2] = '\0'; 308 inattrspace = 1; 309 if ((ep = lookupino(itp->t_ino)) == NULL) { 310 ep = addentry(name, itp->t_ino, 311 NODE|ROOT); 312 } 313 ep->e_flags |= XATTRROOT; 314 treescan(name, itp->t_ino, todo); 315 inattrspace = 0; 316 } else { 317 (void) fprintf(stderr, 318 gettext("Warning: orphaned attribute " 319 "directory\n")); 320 } 321 } else { 322 (void) fprintf(stderr, gettext("Warning: `..' missing " 323 "from attribute directory\n")); 324 } 325 } 326 } 327 328 /* 329 * Search the directory tree rooted at inode ROOTINO 330 * for the path pointed at by n. Note that n must be 331 * modifiable, although it is returned in the same 332 * condition it was given to us in. 333 */ 334 ino_t 335 psearch(char *n) 336 { 337 char *cp, *cp1; 338 ino_t ino; 339 char c; 340 341 ino = ROOTINO; 342 if (*(cp = n) == '/') 343 cp++; 344 next: 345 cp1 = cp + 1; 346 while (*cp1 != '/' && *cp1) 347 cp1++; 348 c = *cp1; 349 *cp1 = 0; 350 ino = search(ino, cp); 351 if (ino == 0) { 352 *cp1 = c; 353 return (0); 354 } 355 *cp1 = c; 356 if (c == '/') { 357 cp = cp1+1; 358 goto next; 359 } 360 return (ino); 361 } 362 363 /* 364 * search the directory inode ino 365 * looking for entry cp 366 */ 367 static ino_t 368 search(ino_t inum, char *cp) 369 { 370 struct direct *dp; 371 struct inotab *itp; 372 uint_t len; 373 374 itp = inotablookup(inum); 375 if (itp == NULL) 376 return (0); 377 rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); 378 len = strlen(cp); 379 do { 380 dp = rst_readdir(dirp); 381 if (dp == NULL || dp->d_ino == 0) 382 return (0); 383 } while (dp->d_namlen != len || strncmp(dp->d_name, cp, len) != 0); 384 return (dp->d_ino); 385 } 386 387 /* 388 * Put the directory entries in the directory file 389 */ 390 static void 391 putdir(char *buf, size_t size) 392 { 393 struct direct cvtbuf; 394 struct odirect *odp; 395 struct odirect *eodp; 396 struct direct *dp; 397 size_t loc, i; 398 399 if (cvtflag) { 400 /*LINTED [buf is char[] in getfile, size % fs_fsize == 0]*/ 401 eodp = (struct odirect *)&buf[size]; 402 /*LINTED [buf is char[] in getfile]*/ 403 for (odp = (struct odirect *)buf; odp < eodp; odp++) 404 if (odp->d_ino != 0) { 405 dcvt(odp, &cvtbuf); 406 putent(&cvtbuf); 407 } 408 } else { 409 loc = 0; 410 while (loc < size) { 411 /*LINTED [buf is char[] in getfile, loc % 4 == 0]*/ 412 dp = (struct direct *)(buf + loc); 413 normdirect(byteorder, dp); 414 i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1)); 415 if (dp->d_reclen == 0 || (long)dp->d_reclen > i) { 416 loc += i; 417 continue; 418 } 419 loc += dp->d_reclen; 420 if (dp->d_ino != 0) { 421 putent(dp); 422 } 423 } 424 } 425 } 426 427 /* 428 * These variables are "local" to the following two functions. 429 */ 430 static char dirbuf[DIRBLKSIZ]; 431 static int32_t dirloc = 0; 432 static int32_t prev = 0; 433 434 /* 435 * add a new directory entry to a file. 436 */ 437 static void 438 putent(struct direct *dp) 439 { 440 /* LINTED DIRSIZ will always fit in a ushort_t */ 441 dp->d_reclen = (ushort_t)DIRSIZ(dp); 442 /* LINTED sign extension ok in assert */ 443 assert(DIRSIZ(dp) == (ulong_t)dp->d_reclen); 444 if (dirloc + (long)dp->d_reclen > DIRBLKSIZ) { 445 /*LINTED [prev += dp->d_reclen, prev % 4 == 0]*/ 446 ((struct direct *)(dirbuf + prev))->d_reclen = 447 DIRBLKSIZ - prev; 448 (void) fwrite(dirbuf, 1, DIRBLKSIZ, df); 449 if (ferror(df)) 450 panic("%s: %s\n", dirfile, strerror(errno)); 451 dirloc = 0; 452 } 453 bcopy((char *)dp, dirbuf + dirloc, (size_t)dp->d_reclen); 454 prev = dirloc; 455 dirloc += dp->d_reclen; 456 } 457 458 /* 459 * flush out a directory that is finished. 460 */ 461 static void 462 flushent(void) 463 { 464 465 /* LINTED prev += dp->d_reclen, prev % 4 == 0 */ 466 ((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev; 467 (void) fwrite(dirbuf, (size_t)dirloc, 1, df); 468 if (ferror(df)) 469 panic("%s: %s\n", dirfile, strerror(errno)); 470 g_seekpt = ftello64(df); 471 dirloc = 0; 472 } 473 474 static void 475 dcvt(struct odirect *odp, struct direct *ndp) 476 { 477 478 (void) bzero((char *)ndp, sizeof (*ndp)); 479 ndp->d_ino = odp->d_ino; 480 /* Note that odp->d_name may not be null-terminated */ 481 /* LINTED assertion always true */ 482 assert(sizeof (ndp->d_name) > sizeof (odp->d_name)); 483 (void) strncpy(ndp->d_name, odp->d_name, sizeof (odp->d_name)); 484 ndp->d_name[sizeof (odp->d_name)] = '\0'; 485 /* LINTED: strlen will fit into d_namlen */ 486 ndp->d_namlen = strlen(ndp->d_name); 487 488 /* LINTED sign extension ok in assert */ 489 assert(DIRSIZ(ndp) == (ulong_t)ndp->d_reclen); 490 /* LINTED DIRSIZ always fits in ushort_t */ 491 ndp->d_reclen = (ushort_t)DIRSIZ(ndp); 492 } 493 494 /* 495 * Initialize the directory file 496 */ 497 static RST_DIR * 498 rst_initdirfile(char *name) 499 { 500 RST_DIR *dp; 501 int fd; 502 503 if ((fd = open(name, O_RDONLY | O_LARGEFILE)) == -1) 504 return ((RST_DIR *)0); 505 if ((dp = (RST_DIR *)malloc(sizeof (*dp))) == NULL) { 506 (void) close(fd); 507 return ((RST_DIR *)0); 508 } 509 dp->dd_fd = fd; 510 dp->dd_loc = 0; 511 dp->dd_refcnt = 1; 512 return (dp); 513 } 514 515 /* 516 * Simulate the opening of a directory 517 */ 518 RST_DIR * 519 rst_opendir(char *name) 520 { 521 struct inotab *itp; 522 ino_t ino; 523 524 if ((ino = dirlookup(name)) > 0 && 525 (itp = inotablookup(ino)) != NULL) { 526 rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); 527 dirp->dd_refcnt++; 528 return (dirp); 529 } 530 return ((RST_DIR *)0); 531 } 532 533 /* 534 * Releases the hidden state created by rst_opendir(). 535 * Specifically, the dirp it provided to the caller is malloc'd. 536 */ 537 void 538 rst_closedir(RST_DIR *cdirp) 539 { 540 if ((cdirp != NULL) && (--(cdirp->dd_refcnt) < 1)) 541 free(cdirp); 542 } 543 544 /* 545 * return a pointer into a directory 546 */ 547 static offset_t 548 rst_telldir(RST_DIR *tdirp) 549 { 550 offset_t pos = llseek(tdirp->dd_fd, (offset_t)0, SEEK_CUR); 551 552 if (pos == (offset_t)-1) { 553 perror("Could not determine position in directory file"); 554 done(1); 555 } 556 557 return ((pos - tdirp->dd_size) + tdirp->dd_loc); 558 } 559 560 /* 561 * Seek to an entry in a directory. 562 * Only values returned by ``rst_telldir'' should be passed to rst_seekdir. 563 * This routine handles many directories in a single file. 564 * It takes the base of the directory in the file, plus 565 * the desired seek offset into it. 566 */ 567 static void 568 rst_seekdir(RST_DIR *sdirp, offset_t loc, offset_t base) 569 { 570 571 if (loc == rst_telldir(sdirp)) 572 return; 573 loc -= base; 574 if (loc < 0) 575 (void) fprintf(stderr, 576 gettext("bad seek pointer to rst_seekdir %d\n"), loc); 577 (void) llseek(sdirp->dd_fd, base + (loc & ~(DIRBLKSIZ - 1)), 0); 578 sdirp->dd_loc = loc & (DIRBLKSIZ - 1); 579 if (sdirp->dd_loc != 0) 580 sdirp->dd_size = read(sdirp->dd_fd, sdirp->dd_buf, DIRBLKSIZ); 581 } 582 583 /* 584 * get next entry in a directory. 585 */ 586 struct direct * 587 rst_readdir(RST_DIR *rdirp) 588 { 589 struct direct *dp; 590 591 for (;;) { 592 if (rdirp->dd_loc == 0) { 593 rdirp->dd_size = read(rdirp->dd_fd, rdirp->dd_buf, 594 DIRBLKSIZ); 595 if (rdirp->dd_size <= 0) { 596 dprintf(stderr, 597 gettext("error reading directory\n")); 598 return ((struct direct *)0); 599 } 600 } 601 if (rdirp->dd_loc >= rdirp->dd_size) { 602 rdirp->dd_loc = 0; 603 continue; 604 } 605 /*LINTED [rvalue will be aligned on int boundary]*/ 606 dp = (struct direct *)(rdirp->dd_buf + rdirp->dd_loc); 607 if (dp->d_reclen == 0 || 608 (long)dp->d_reclen > (DIRBLKSIZ + 1 - rdirp->dd_loc)) { 609 dprintf(stderr, 610 gettext("corrupted directory: bad reclen %d\n"), 611 dp->d_reclen); 612 return ((struct direct *)0); 613 } 614 rdirp->dd_loc += dp->d_reclen; 615 if (dp->d_ino == 0 && strcmp(dp->d_name, "/") != 0) 616 continue; 617 if ((ino_t)(dp->d_ino) >= maxino) { 618 dprintf(stderr, 619 gettext("corrupted directory: bad inum %lu\n"), 620 dp->d_ino); 621 continue; 622 } 623 return (dp); 624 } 625 } 626 627 /* 628 * Set the mode, owner, and times for all new or changed directories 629 */ 630 void 631 setdirmodes(void) 632 { 633 FILE *smf; 634 struct entry *ep; 635 char *cp, *metadata = NULL; 636 size_t metasize = 0; 637 int override = -1; 638 int saverr; 639 static int complained_chown = 0; 640 static int complained_chmod = 0; 641 int dfd; 642 643 vprintf(stdout, gettext("Set directory mode, owner, and times.\n")); 644 /* XXX if modefile[0] == '#', shouldn't we just bail here? */ 645 /* XXX why isn't it set already? */ 646 INIT_MODEFILE(); 647 smf = fopen64(modefile, "r"); 648 if (smf == NULL) { 649 perror("fopen"); 650 (void) fprintf(stderr, 651 gettext("cannot open mode file %s\n"), modefile); 652 (void) fprintf(stderr, 653 gettext("directory mode, owner, and times not set\n")); 654 return; 655 } 656 clearerr(smf); 657 for (;;) { 658 (void) fread((char *)&node, 1, sizeof (node), smf); 659 if (feof(smf)) 660 break; 661 ep = lookupino(node.ino); 662 if (command == 'i' || command == 'x') { 663 if (ep == NIL) { 664 skipmetadata(smf, node.metasize); 665 continue; 666 } 667 if (ep->e_flags & EXISTED) { 668 if (override < 0) { 669 if (reply(gettext( 670 "Directories already exist, set modes anyway")) 671 == FAIL) 672 override = 0; 673 else 674 override = 1; 675 } 676 if (override == 0) { 677 /* LINTED: result fits into short */ 678 ep->e_flags &= ~NEW; 679 skipmetadata(smf, node.metasize); 680 continue; 681 } 682 } 683 if (node.ino == ROOTINO && 684 reply(gettext("set owner/mode for '.'")) == FAIL) { 685 skipmetadata(smf, node.metasize); 686 continue; 687 } 688 } 689 if (ep == NIL) { 690 panic(gettext("cannot find directory inode %d\n"), 691 node.ino); 692 skipmetadata(smf, node.metasize); 693 continue; 694 } 695 cp = myname(ep); 696 resolve(myname(ep), &dfd, &cp); 697 if (dfd != AT_FDCWD) { 698 if (fchdir(dfd) < 0) { 699 saverr = errno; 700 (void) fprintf(stderr, 701 gettext("Can not set attribute context: %s\n"), 702 strerror(saverr)); 703 (void) close(dfd); 704 continue; 705 } 706 } 707 if (chmod(cp, node.mode) < 0 && !complained_chmod) { 708 saverr = errno; 709 (void) fprintf(stderr, 710 gettext("Can not set directory permissions: %s\n"), 711 strerror(saverr)); 712 complained_chmod = 1; 713 } 714 if (node.metasize != 0) { 715 if (node.metasize > metasize) 716 metadata = realloc(metadata, 717 metasize = node.metasize); 718 if (metadata == NULL) { 719 (void) fprintf(stderr, 720 gettext("Cannot malloc metadata\n")); 721 done(1); 722 } 723 (void) fread(metadata, 1, node.metasize, smf); 724 metaproc(cp, metadata, node.metasize); 725 } 726 727 /* 728 * BUG 4302943 729 * Since the ACLs must be set before fixing the ownership, 730 * chown should be called only after metaproc 731 */ 732 if (chown(cp, node.uid, node.gid) < 0 && !complained_chown) { 733 saverr = errno; 734 (void) fprintf(stderr, 735 gettext("Can not set directory ownership: %s\n"), 736 strerror(saverr)); 737 complained_chown = 1; 738 } 739 utime(cp, (struct utimbuf *)node.timep); 740 /* LINTED: result fits into short */ 741 ep->e_flags &= ~NEW; 742 if (dfd != AT_FDCWD) { 743 fchdir(savepwd); 744 (void) close(dfd); 745 } 746 } 747 if (ferror(smf)) 748 panic(gettext("error setting directory modes\n")); 749 if (metadata != NULL) 750 (void) free(metadata); 751 (void) fclose(smf); 752 } 753 754 void 755 skipmetadata(FILE *f, size_t size) 756 { 757 /* XXX should we bail if this doesn't work? */ 758 /* LINTED unsigned -> signed conversion ok here */ 759 (void) fseeko(f, (off_t)size, SEEK_CUR); 760 } 761 762 /* 763 * Generate a literal copy of a directory. 764 */ 765 int 766 genliteraldir(char *name, ino_t ino) 767 { 768 struct inotab *itp; 769 int ofile, dp; 770 off64_t i; 771 size_t size; 772 char buf[BUFSIZ]; 773 774 itp = inotablookup(ino); 775 if (itp == NULL) { 776 (void) fprintf(stderr, 777 gettext("Cannot find directory inode %d named %s\n"), 778 ino, name); 779 return (FAIL); 780 } 781 if ((ofile = creat(name, 0666)) < 0) { 782 (void) fprintf(stderr, "%s: ", name); 783 (void) fflush(stderr); 784 perror(gettext("cannot create file")); 785 return (FAIL); 786 } 787 rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); 788 dp = dup(dirp->dd_fd); 789 if (dp < 0) { 790 perror(gettext("dup(2) failed")); 791 (void) close(ofile); 792 (void) unlink(name); 793 return (FAIL); 794 } 795 for (i = itp->t_size; i != 0; i -= size) { 796 /* LINTED cast is safe due to comparison */ 797 size = i < BUFSIZ ? (size_t)i : BUFSIZ; 798 /* XXX instead of done(), clean up and return FAIL? */ 799 if (read(dp, buf, size) == -1) { 800 (void) fprintf(stderr, gettext( 801 "read error extracting inode %d, name %s\n"), 802 curfile.ino, curfile.name); 803 perror("read"); 804 done(1); 805 } 806 if (write(ofile, buf, size) == -1) { 807 (void) fprintf(stderr, gettext( 808 "write error extracting inode %d, name %s\n"), 809 curfile.ino, curfile.name); 810 perror("write"); 811 done(1); 812 } 813 } 814 (void) close(dp); 815 (void) close(ofile); 816 return (GOOD); 817 } 818 819 /* 820 * Determine the type of an inode 821 */ 822 int 823 inodetype(ino_t ino) 824 { 825 struct inotab *itp; 826 827 itp = inotablookup(ino); 828 if (itp == NULL) 829 return (LEAF); 830 return (NODE); 831 } 832 833 /* 834 * Allocate and initialize a directory inode entry. 835 * If requested, save its pertinent mode, owner, and time info. 836 */ 837 static struct inotab * 838 allocinotab(ino_t ino, struct dinode *dip, off64_t seekpt) 839 { 840 struct inotab *itp; 841 842 itp = (struct inotab *)calloc(1, sizeof (*itp)); 843 if (itp == 0) { 844 (void) fprintf(stderr, 845 gettext("no memory for directory table\n")); 846 done(1); 847 } 848 itp->t_next = inotab[INOHASH(ino)]; 849 inotab[INOHASH(ino)] = itp; 850 itp->t_ino = ino; 851 itp->t_seekpt = seekpt; 852 if ((dip->di_mode & IFMT) == IFATTRDIR) { 853 itp->t_xattr = xattrlist; 854 xattrlist = itp; 855 } 856 if (mf == NULL) 857 return (itp); 858 node.ino = ino; 859 node.timep[0] = dip->di_atime; 860 node.timep[1] = dip->di_mtime; 861 node.mode = dip->di_mode; 862 node.uid = 863 dip->di_suid == UID_LONG ? dip->di_uid : (uid_t)dip->di_suid; 864 node.gid = 865 dip->di_sgid == GID_LONG ? dip->di_gid : (gid_t)dip->di_sgid; 866 return (itp); 867 } 868 869 void 870 nodeflush() 871 { 872 char *metadata; 873 874 if (mf == NULL) { 875 (void) fprintf(stderr, gettext( 876 "Inconsistency detected: modefile pointer is NULL\n")); 877 done(1); 878 } 879 metaget(&metadata, &(node.metasize)); 880 (void) fwrite((char *)&node, 1, sizeof (node), mf); 881 if (node.metasize != 0) 882 (void) fwrite(metadata, 1, node.metasize, mf); 883 if (ferror(mf)) 884 panic("%s: %s\n", modefile, strerror(errno)); 885 } 886 887 /* 888 * Look up an inode in the table of directories 889 */ 890 static struct inotab * 891 inotablookup(ino_t ino) 892 { 893 struct inotab *itp; 894 895 for (itp = inotab[INOHASH(ino)]; itp != NULL; itp = itp->t_next) 896 if (itp->t_ino == ino) 897 return (itp); 898 return ((struct inotab *)0); 899 } 900 901 /* 902 * Clean up and exit 903 */ 904 void 905 done(int exitcode) 906 { 907 closemt(ALLOW_OFFLINE); /* don't force offline on exit */ 908 if (modefile[0] != '#') 909 (void) unlink(modefile); 910 if (dirfile[0] != '#') 911 (void) unlink(dirfile); 912 exit(exitcode); 913 } 914