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