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