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