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