1 /* 2 * Copyright 2004 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 <ctype.h> 19 #include <errno.h> 20 #include <syslog.h> 21 #include <limits.h> 22 /* LINTED: this file really is necessary */ 23 #include <euc.h> 24 #include <widec.h> 25 26 /* 27 * Insure that all the components of a pathname exist. Note that 28 * lookupname() and addentry() both expect complex names as 29 * input arguments, so a double NULL needs to be added to each name. 30 */ 31 void 32 pathcheck(name) 33 char *name; 34 { 35 char *cp, save; 36 struct entry *ep; 37 char *start; 38 39 start = strchr(name, '/'); 40 if (start == 0) 41 return; 42 for (cp = start; *cp != '\0'; cp++) { 43 if (*cp != '/') 44 continue; 45 *cp = '\0'; 46 save = *(cp+1); 47 *(cp+1) = '\0'; 48 ep = lookupname(name); 49 if (ep == NIL) { 50 ep = addentry(name, psearch(name), NODE); 51 newnode(ep); 52 } 53 /* LINTED: result fits in a short */ 54 ep->e_flags |= NEW|KEEP; 55 *cp = '/'; 56 *(cp+1) = save; 57 } 58 } 59 60 /* 61 * Change a name to a unique temporary name. 62 */ 63 void 64 mktempname(ep) 65 struct entry *ep; 66 { 67 char *newname; 68 69 if (ep->e_flags & TMPNAME) 70 badentry(ep, gettext("mktempname: called with TMPNAME")); 71 /* LINTED: result fits in a short */ 72 ep->e_flags |= TMPNAME; 73 newname = savename(gentempname(ep)); 74 renameit(myname(ep), newname); 75 freename(ep->e_name); 76 ep->e_name = newname; 77 /* LINTED: savename guarantees strlen will fit */ 78 ep->e_namlen = strlen(ep->e_name); 79 } 80 81 /* 82 * Generate a temporary name for an entry. 83 */ 84 char * 85 gentempname(ep) 86 struct entry *ep; 87 { 88 static char name[MAXPATHLEN]; 89 struct entry *np; 90 long i = 0; 91 92 for (np = lookupino(ep->e_ino); np != NIL && np != ep; np = np->e_links) 93 i++; 94 if (np == NIL) 95 badentry(ep, gettext("not on ino list")); 96 (void) snprintf(name, sizeof (name), "%s%ld%lu", TMPHDR, i, ep->e_ino); 97 return (name); 98 } 99 100 /* 101 * Rename a file or directory. 102 */ 103 void 104 renameit(fp, tp) 105 char *fp; 106 char *tp; 107 { 108 int fromfd, tofd; 109 char *from, *to; 110 char tobuf[MAXPATHLEN]; 111 char *pathend; 112 113 resolve(fp, &fromfd, &from); 114 /* 115 * The to pointer argument is assumed to be either a fully 116 * specified path (starting with "./") or a simple temporary 117 * file name (starting with TMPHDR). If passed a simple temp 118 * file name, we need to set up the descriptors explicitly. 119 */ 120 if (strncmp(tp, TMPHDR, sizeof (TMPHDR) - 1) == 0) { 121 tofd = fromfd; 122 if ((pathend = strrchr(from, '/')) != NULL) { 123 strncpy(tobuf, from, pathend - from + 1); 124 tobuf[pathend - from + 1] = NULL; 125 strlcat(tobuf, tp, sizeof (tobuf)); 126 to = tobuf; 127 } else { 128 to = tp; 129 } 130 } else 131 resolve(tp, &tofd, &to); 132 if (renameat(fromfd, from, tofd, to) < 0) { 133 int saverr = errno; 134 (void) fprintf(stderr, 135 gettext("Warning: cannot rename %s to %s: %s\n"), 136 from, to, strerror(saverr)); 137 (void) fflush(stderr); 138 } else { 139 vprintf(stdout, gettext("rename %s to %s\n"), from, to); 140 } 141 if (fromfd != AT_FDCWD) (void) close(fromfd); 142 if (tofd != AT_FDCWD) (void) close(tofd); 143 } 144 145 /* 146 * Create a new node (directory). Note that, because we have no 147 * mkdirat() function, fchdir() must be used set up the appropriate 148 * name space context prior to the call to mkdir() if we are 149 * operating in attribute space. 150 */ 151 void 152 newnode(np) 153 struct entry *np; 154 { 155 char *cp; 156 int dfd; 157 158 if (np->e_type != NODE) 159 badentry(np, gettext("newnode: not a node")); 160 resolve(myname(np), &dfd, &cp); 161 if (dfd != AT_FDCWD) { 162 if (fchdir(dfd) < 0) { 163 int saverr = errno; 164 (void) fprintf(stderr, 165 gettext("Warning: cannot create %s: %s"), 166 cp, strerror(saverr)); 167 (void) fflush(stderr); 168 (void) close(dfd); 169 return; 170 } 171 } 172 if (mkdir(cp, 0777) < 0) { 173 int saverr = errno; 174 /* LINTED: result fits in a short */ 175 np->e_flags |= EXISTED; 176 (void) fprintf(stderr, gettext("Warning: ")); 177 (void) fflush(stderr); 178 (void) fprintf(stderr, "%s: %s\n", cp, strerror(saverr)); 179 } else { 180 vprintf(stdout, gettext("Make node %s\n"), cp); 181 } 182 if (dfd != AT_FDCWD) { 183 fchdir(savepwd); 184 (void) close(dfd); 185 } 186 } 187 188 /* 189 * Remove an old node (directory). See comment above on newnode() 190 * for explanation of fchdir() use below. 191 */ 192 void 193 removenode(ep) 194 struct entry *ep; 195 { 196 char *cp; 197 int dfd; 198 199 if (ep->e_type != NODE) 200 badentry(ep, gettext("removenode: not a node")); 201 if (ep->e_entries != NIL) 202 badentry(ep, gettext("removenode: non-empty directory")); 203 /* LINTED: result fits in a short */ 204 ep->e_flags |= REMOVED; 205 /* LINTED: result fits in a short */ 206 ep->e_flags &= ~TMPNAME; 207 resolve(myname(ep), &dfd, &cp); 208 if (dfd != AT_FDCWD) { 209 if (fchdir(dfd) < 0) { 210 int saverr = errno; 211 (void) fprintf(stderr, 212 gettext("Warning: cannot remove %s: %s"), 213 cp, strerror(saverr)); 214 (void) fflush(stderr); 215 (void) close(dfd); 216 return; 217 } 218 } 219 if (rmdir(cp) < 0) { /* NOTE: could use unlinkat (..,REMOVEDIR) */ 220 int saverr = errno; 221 (void) fprintf(stderr, gettext("Warning: %s: %s\n"), 222 cp, strerror(saverr)); 223 (void) fflush(stderr); 224 } else { 225 vprintf(stdout, gettext("Remove node %s\n"), cp); 226 } 227 if (dfd != AT_FDCWD) { 228 (void) fchdir(savepwd); 229 (void) close(dfd); 230 } 231 } 232 233 /* 234 * Remove a leaf. 235 */ 236 void 237 removeleaf(ep) 238 struct entry *ep; 239 { 240 char *cp; 241 int dfd; 242 243 if (ep->e_type != LEAF) 244 badentry(ep, gettext("removeleaf: not a leaf")); 245 /* LINTED: result fits in a short */ 246 ep->e_flags |= REMOVED; 247 /* LINTED: result fits in a short */ 248 ep->e_flags &= ~TMPNAME; 249 resolve(myname(ep), &dfd, &cp); 250 if (unlinkat(dfd, cp, 0) < 0) { 251 int saverr = errno; 252 (void) fprintf(stderr, gettext("Warning: %s: %s\n"), 253 cp, strerror(saverr)); 254 (void) fflush(stderr); 255 } else { 256 vprintf(stdout, gettext("Remove leaf %s\n"), cp); 257 } 258 if (dfd != AT_FDCWD) 259 (void) close(dfd); 260 } 261 262 /* 263 * Create a link. 264 * This function assumes that the context has already been set 265 * for the link file to be created (i.e., we have "fchdir-ed" 266 * into attribute space already if this is an attribute link). 267 */ 268 lf_linkit(existing, new, type) 269 char *existing, *new; 270 int type; 271 { 272 char linkbuf[MAXPATHLEN]; 273 struct stat64 s1[1], s2[1]; 274 char *name; 275 int dfd, l, result; 276 277 resolve(existing, &dfd, &name); 278 if (dfd == -1) { 279 (void) fprintf(stderr, gettext( 280 "Warning: cannot restore %s link %s->%s\n"), 281 (type == SYMLINK ? "symbolic" : "hard"), new, existing); 282 result = FAIL; 283 goto out; 284 } 285 if (type == SYMLINK) { 286 if (symlink(name, new) < 0) { 287 /* No trailing \0 from readlink(2) */ 288 if (((l = readlink(new, linkbuf, sizeof (linkbuf))) 289 > 0) && 290 (l == strlen(name)) && 291 (strncmp(linkbuf, name, l) == 0)) { 292 vprintf(stdout, 293 gettext("Symbolic link %s->%s ok\n"), 294 new, name); 295 result = GOOD; 296 goto out; 297 } else { 298 int saverr = errno; 299 (void) fprintf(stderr, gettext( 300 "Warning: cannot create symbolic link %s->%s: %s"), 301 new, name, strerror(saverr)); 302 (void) fflush(stderr); 303 result = FAIL; 304 goto out; 305 } 306 } 307 } else if (type == HARDLINK) { 308 if (link(name, new) < 0) { 309 int saverr = errno; 310 if ((stat64(name, s1) == 0) && 311 (stat64(new, s2) == 0) && 312 (s1->st_dev == s2->st_dev) && 313 (s1->st_ino == s2->st_ino)) { 314 vprintf(stdout, 315 gettext("Hard link %s->%s ok\n"), 316 new, name); 317 result = GOOD; 318 goto out; 319 } else { 320 (void) fprintf(stderr, gettext( 321 "Warning: cannot create hard link %s->%s: %s\n"), 322 new, name, strerror(saverr)); 323 (void) fflush(stderr); 324 result = FAIL; 325 goto out; 326 } 327 } 328 } else { 329 panic(gettext("%s: unknown type %d\n"), "linkit", type); 330 result = FAIL; 331 goto out; 332 } 333 result = GOOD; 334 if (type == SYMLINK) 335 vprintf(stdout, gettext("Create symbolic link %s->%s\n"), 336 new, name); 337 else 338 vprintf(stdout, gettext("Create hard link %s->%s\n"), 339 new, name); 340 out: 341 if (dfd != AT_FDCWD) { 342 (void) close(dfd); 343 } 344 return (result); 345 } 346 347 /* 348 * Find lowest-numbered inode (above "start") that needs to be extracted. 349 * Caller knows that a return value of maxino means there's nothing left. 350 */ 351 ino_t 352 lowerbnd(start) 353 ino_t start; 354 { 355 struct entry *ep; 356 357 for (; start < maxino; start++) { 358 ep = lookupino(start); 359 if (ep == NIL || ep->e_type == NODE) 360 continue; 361 if (ep->e_flags & (NEW|EXTRACT)) 362 return (start); 363 } 364 return (start); 365 } 366 367 /* 368 * Find highest-numbered inode (below "start") that needs to be extracted. 369 */ 370 ino_t 371 upperbnd(start) 372 ino_t start; 373 { 374 struct entry *ep; 375 376 for (; start > ROOTINO; start--) { 377 ep = lookupino(start); 378 if (ep == NIL || ep->e_type == NODE) 379 continue; 380 if (ep->e_flags & (NEW|EXTRACT)) 381 return (start); 382 } 383 return (start); 384 } 385 386 /* 387 * report on a badly formed entry 388 */ 389 void 390 badentry(ep, msg) 391 struct entry *ep; 392 char *msg; 393 { 394 395 (void) fprintf(stderr, gettext("bad entry: %s\n"), msg); 396 (void) fprintf(stderr, gettext("name: %s\n"), myname(ep)); 397 (void) fprintf(stderr, gettext("parent name %s\n"), 398 myname(ep->e_parent)); 399 if (ep->e_sibling != NIL) 400 (void) fprintf(stderr, gettext("sibling name: %s\n"), 401 myname(ep->e_sibling)); 402 if (ep->e_entries != NIL) 403 (void) fprintf(stderr, gettext("next entry name: %s\n"), 404 myname(ep->e_entries)); 405 if (ep->e_links != NIL) 406 (void) fprintf(stderr, gettext("next link name: %s\n"), 407 myname(ep->e_links)); 408 if (ep->e_xattrs != NIL) 409 (void) fprintf(stderr, gettext("attribute root name: %s\n"), 410 myname(ep->e_xattrs)); 411 if (ep->e_next != NIL) 412 (void) fprintf(stderr, gettext("next hashchain name: %s\n"), 413 myname(ep->e_next)); 414 (void) fprintf(stderr, gettext("entry type: %s\n"), 415 ep->e_type == NODE ? gettext("NODE") : gettext("LEAF")); 416 (void) fprintf(stderr, gettext("inode number: %lu\n"), ep->e_ino); 417 panic(gettext("flags: %s\n"), flagvalues(ep)); 418 /* Our callers are expected to handle our returning. */ 419 } 420 421 /* 422 * Construct a string indicating the active flag bits of an entry. 423 */ 424 char * 425 flagvalues(ep) 426 struct entry *ep; 427 { 428 static char flagbuf[BUFSIZ]; 429 430 (void) strlcpy(flagbuf, gettext("|NIL"), sizeof (flagbuf)); 431 flagbuf[0] = '\0'; 432 if (ep->e_flags & REMOVED) 433 (void) strlcat(flagbuf, gettext("|REMOVED"), sizeof (flagbuf)); 434 if (ep->e_flags & TMPNAME) 435 (void) strlcat(flagbuf, gettext("|TMPNAME"), sizeof (flagbuf)); 436 if (ep->e_flags & EXTRACT) 437 (void) strlcat(flagbuf, gettext("|EXTRACT"), sizeof (flagbuf)); 438 if (ep->e_flags & NEW) 439 (void) strlcat(flagbuf, gettext("|NEW"), sizeof (flagbuf)); 440 if (ep->e_flags & KEEP) 441 (void) strlcat(flagbuf, gettext("|KEEP"), sizeof (flagbuf)); 442 if (ep->e_flags & EXISTED) 443 (void) strlcat(flagbuf, gettext("|EXISTED"), sizeof (flagbuf)); 444 if (ep->e_flags & XATTR) 445 (void) strlcat(flagbuf, gettext("|XATTR"), sizeof (flagbuf)); 446 if (ep->e_flags & XATTRROOT) 447 (void) strlcat(flagbuf, gettext("|XATTRROOT"), 448 sizeof (flagbuf)); 449 return (&flagbuf[1]); 450 } 451 452 /* 453 * Check to see if a name is on a dump tape. 454 */ 455 ino_t 456 dirlookup(name) 457 char *name; 458 { 459 ino_t ino; 460 461 ino = psearch(name); 462 if (ino == 0 || BIT(ino, dumpmap) == 0) 463 (void) fprintf(stderr, gettext("%s is not on volume\n"), name); 464 return (ino); 465 } 466 467 /* 468 * Elicit a reply. 469 */ 470 reply(question) 471 char *question; 472 { 473 char *yesorno = gettext("yn"); /* must be two characters, "yes" first */ 474 int c; 475 476 do { 477 (void) fprintf(stderr, "%s? [%s] ", question, yesorno); 478 (void) fflush(stderr); 479 c = getc(terminal); 480 while (c != '\n' && getc(terminal) != '\n') { 481 if (ferror(terminal)) { 482 (void) fprintf(stderr, gettext( 483 "Error reading response\n")); 484 (void) fflush(stderr); 485 return (FAIL); 486 } 487 if (feof(terminal)) 488 return (FAIL); 489 } 490 if (isupper(c)) 491 c = tolower(c); 492 } while (c != yesorno[0] && c != yesorno[1]); 493 if (c == yesorno[0]) 494 return (GOOD); 495 return (FAIL); 496 } 497 498 /* 499 * handle unexpected inconsistencies 500 */ 501 /* 502 * Note that a panic w/ EOF on the tty means all panics will return... 503 */ 504 #ifdef __STDC__ 505 #include <stdarg.h> 506 507 /* VARARGS1 */ 508 void 509 panic(const char *msg, ...) 510 { 511 va_list args; 512 513 va_start(args, msg); 514 (void) vfprintf(stderr, msg, args); 515 va_end(args); 516 if (reply(gettext("abort")) == GOOD) { 517 if (reply(gettext("dump core")) == GOOD) 518 abort(); 519 done(1); 520 } 521 } 522 #else 523 #include <varargs.h> 524 525 /* VARARGS1 */ 526 void 527 panic(va_alist) 528 va_dcl 529 { 530 va_list args; 531 char *msg; 532 533 va_start(args); 534 msg = va_arg(args, char *); 535 (void) vfprintf(stderr, msg, args); 536 va_end(args); 537 if (reply(gettext("abort")) == GOOD) { 538 if (reply(gettext("dump core")) == GOOD) 539 abort(); 540 done(1); 541 } 542 #endif 543 544 /* 545 * Locale-specific version of ctime 546 */ 547 char * 548 lctime(tp) 549 time_t *tp; 550 { 551 static char buf[256]; 552 struct tm *tm; 553 554 tm = localtime(tp); 555 (void) strftime(buf, sizeof (buf), "%c\n", tm); 556 return (buf); 557 } 558 559 static int 560 statcmp(const struct stat *left, const struct stat *right) 561 { 562 int result = 1; 563 564 if ((left->st_dev == right->st_dev) && 565 (left->st_ino == right->st_ino) && 566 (left->st_mode == right->st_mode) && 567 (left->st_nlink == right->st_nlink) && 568 (left->st_uid == right->st_uid) && 569 (left->st_gid == right->st_gid) && 570 (left->st_rdev == right->st_rdev) && 571 (left->st_ctim.tv_sec == right->st_ctim.tv_sec) && 572 (left->st_ctim.tv_nsec == right->st_ctim.tv_nsec) && 573 (left->st_mtim.tv_sec == right->st_mtim.tv_sec) && 574 (left->st_mtim.tv_nsec == right->st_mtim.tv_nsec) && 575 (left->st_blksize == right->st_blksize) && 576 (left->st_blocks == right->st_blocks)) { 577 result = 0; 578 } 579 580 return (result); 581 } 582 583 /* 584 * Safely open a file. 585 */ 586 int 587 safe_open(int dfd, const char *filename, int mode, int perms) 588 { 589 static int init_syslog = 1; 590 int fd; 591 int working_mode; 592 int saverr; 593 char *errtext; 594 struct stat pre_stat, pre_lstat; 595 struct stat post_stat, post_lstat; 596 597 if (init_syslog) { 598 openlog(progname, LOG_CONS, LOG_DAEMON); 599 init_syslog = 0; 600 } 601 602 /* 603 * Don't want to be spoofed into trashing something we 604 * shouldn't, thus the following rigamarole. If it doesn't 605 * exist, we create it and proceed. Otherwise, require that 606 * what's there be a real file with no extraneous links and 607 * owned by whoever ran us. 608 * 609 * The silliness with using both lstat() and fstat() is to avoid 610 * race-condition games with someone replacing the file with a 611 * symlink after we've opened it. If there was an flstat(), 612 * we wouldn't need the fstat(). 613 * 614 * The initial open with the hard-coded flags is ok even if we 615 * are intending to open only for reading. If it succeeds, 616 * then the file did not exist, and we'll synthesize an appropriate 617 * complaint below. Otherwise, it does exist, so we won't be 618 * truncating it with the open. 619 */ 620 if ((fd = openat(dfd, filename, 621 O_WRONLY|O_CREAT|O_TRUNC|O_EXCL|O_LARGEFILE, perms)) < 0) { 622 if (errno == EEXIST) { 623 if (fstatat(dfd, filename, &pre_lstat, 624 AT_SYMLINK_NOFOLLOW) < 0) { 625 saverr = errno; 626 (void) close(fd); 627 errno = saverr; 628 return (-1); 629 } 630 631 if (fstatat(dfd, filename, &pre_stat, 0) < 0) { 632 saverr = errno; 633 (void) close(fd); 634 errno = saverr; 635 return (-1); 636 } 637 638 working_mode = mode & (O_WRONLY|O_RDWR|O_RDONLY); 639 working_mode |= O_LARGEFILE; 640 641 if ((fd = openat(dfd, filename, working_mode)) < 0) { 642 if (errno == ENOENT) { 643 errtext = gettext( 644 "Unexpected condition detected: %s used to exist, but doesn't any longer\n"); 645 (void) fprintf(stderr, errtext, 646 filename); 647 syslog(LOG_WARNING, errtext, filename); 648 errno = ENOENT; 649 } 650 return (-1); 651 } 652 653 if (fstatat(fd, NULL, &post_lstat, 654 AT_SYMLINK_NOFOLLOW) < 0) { 655 saverr = errno; 656 (void) close(fd); 657 errno = saverr; 658 return (-1); 659 } 660 661 if (fstatat(fd, NULL, &post_stat, 0) < 0) { 662 saverr = errno; 663 (void) close(fd); 664 errno = saverr; 665 return (-1); 666 } 667 668 if (statcmp(&pre_lstat, &post_lstat) != 0) { 669 errtext = gettext( 670 "Unexpected condition detected: %s's lstat(2) information changed\n"); 671 (void) fprintf(stderr, errtext, filename); 672 syslog(LOG_WARNING, errtext, filename); 673 errno = EPERM; 674 return (-1); 675 } 676 677 if (statcmp(&pre_stat, &post_stat) != 0) { 678 errtext = gettext( 679 "Unexpected condition detected: %s's stat(2) information changed\n"); 680 (void) fprintf(stderr, errtext, filename); 681 syslog(LOG_WARNING, errtext, filename); 682 errno = EPERM; 683 return (-1); 684 } 685 686 /* 687 * If inode, device, or type are wrong, bail out. 688 */ 689 if ((!S_ISREG(post_lstat.st_mode) || 690 (post_stat.st_ino != post_lstat.st_ino) || 691 (post_stat.st_dev != post_lstat.st_dev))) { 692 errtext = gettext( 693 "Unexpected condition detected: %s is not a regular file\n"); 694 (void) fprintf(stderr, errtext, filename); 695 syslog(LOG_WARNING, errtext, filename); 696 (void) close(fd); 697 errno = EPERM; 698 return (-1); 699 } 700 701 /* 702 * Bad link count implies someone's linked our 703 * target to something else, which we probably 704 * shouldn't step on. 705 */ 706 if (post_lstat.st_nlink != 1) { 707 errtext = gettext( 708 "Unexpected condition detected: %s must have exactly one link\n"); 709 (void) fprintf(stderr, errtext, filename); 710 syslog(LOG_WARNING, errtext, filename); 711 (void) close(fd); 712 errno = EPERM; 713 return (-1); 714 } 715 /* 716 * Root might make a file, but non-root might 717 * need to open it. If the permissions let us 718 * get this far, then let it through. 719 */ 720 if (post_lstat.st_uid != getuid() && 721 post_lstat.st_uid != 0) { 722 errtext = gettext( 723 "Unsupported condition detected: %s must be owned by uid %ld or 0\n"); 724 (void) fprintf(stderr, errtext, filename, 725 (long)getuid()); 726 syslog(LOG_WARNING, errtext, filename, 727 (long)getuid()); 728 (void) close(fd); 729 errno = EPERM; 730 return (-1); 731 } 732 if (mode & (O_WRONLY|O_TRUNC)) { 733 if (ftruncate(fd, (off_t)0) < 0) { 734 (void) fprintf(stderr, 735 "ftruncate(%s): %s\n", 736 filename, strerror(errno)); 737 (void) close(fd); 738 return (-1); 739 } 740 } 741 } else { 742 /* 743 * Didn't exist, but couldn't open it. 744 */ 745 return (-1); 746 } 747 } else { 748 /* 749 * If truncating open succeeded for a read-only open, 750 * bail out, as we really shouldn't have succeeded. 751 */ 752 if (mode & O_RDONLY) { 753 /* Undo the O_CREAT */ 754 (void) unlinkat(dfd, filename, 0); 755 (void) fprintf(stderr, "open(%s): %s\n", 756 filename, strerror(ENOENT)); 757 (void) close(fd); 758 errno = ENOENT; 759 return (-1); 760 } 761 } 762 763 return (fd); 764 } 765 766 /* 767 * STDIO version of safe_open. Equivalent to fopen64(...). 768 */ 769 FILE * 770 safe_fopen(const char *filename, const char *smode, int perms) 771 { 772 int fd; 773 int bmode; 774 775 /* 776 * accepts only modes "r", "r+", and "w" 777 */ 778 if (smode[0] == 'r') { 779 if (smode[1] == '\0') { 780 bmode = O_RDONLY; 781 } else if ((smode[1] == '+') && (smode[2] == '\0')) { 782 bmode = O_RDWR; 783 } 784 } else if ((smode[0] == 'w') && (smode[1] == '\0')) { 785 bmode = O_WRONLY; 786 } else { 787 (void) fprintf(stderr, 788 gettext("internal error: safe_fopen: invalid mode `%s'\n"), 789 smode); 790 return (NULL); 791 } 792 793 fd = safe_open(AT_FDCWD, filename, bmode, perms); 794 795 /* 796 * caller is expected to report error. 797 */ 798 if (fd >= 0) 799 return (fdopen(fd, smode)); 800 801 return ((FILE *)NULL); 802 } 803 804 /* 805 * Read the contents of a directory. 806 */ 807 int 808 mkentry(name, ino, ap) 809 char *name; 810 ino_t ino; 811 struct arglist *ap; 812 { 813 struct afile *fp; 814 815 if (ap->base == NULL) { 816 ap->nent = 20; 817 ap->base = (struct afile *)calloc((unsigned)ap->nent, 818 sizeof (*(ap->base))); 819 if (ap->base == NULL) { 820 (void) fprintf(stderr, 821 gettext("%s: out of memory\n"), ap->cmd); 822 return (FAIL); 823 } 824 } 825 if (ap->head == NULL) 826 ap->head = ap->last = ap->base; 827 fp = ap->last; 828 fp->fnum = ino; 829 fp->fname = savename(name); 830 fp++; 831 if (fp == ap->head + ap->nent) { 832 ap->base = (struct afile *)realloc((char *)ap->base, 833 (size_t)(2 * ap->nent * (size_t)sizeof (*(ap->base)))); 834 if (ap->base == NULL) { 835 (void) fprintf(stderr, 836 gettext("%s: out of memory\n"), ap->cmd); 837 return (FAIL); 838 } 839 ap->head = ap->base; 840 fp = ap->head + ap->nent; 841 ap->nent *= 2; 842 } 843 ap->last = fp; 844 return (GOOD); 845 } 846 847 #ifdef __STDC__ 848 static int gmatch(wchar_t *, wchar_t *); 849 static int addg(struct direct *, char *, char *, struct arglist *); 850 #else 851 static int gmatch(); 852 static int addg(); 853 #endif 854 855 /* 856 * XXX This value is ASCII (but not language) dependent. In 857 * ASCII, it is the DEL character (unlikely to appear in paths). 858 * If you are compiling on an EBCDIC-based machine, re-define 859 * this (0x7f is '"') to be something like 0x7 (DEL). It's 860 * either this hack or re-write the expand() algorithm... 861 */ 862 #define DELIMCHAR ((char)0x7f) 863 864 /* 865 * Expand a file name. 866 * "as" is the pattern to expand. 867 * "rflg" non-zero indicates that we're recursing. 868 * "ap" is where to put the results of the expansion. 869 * 870 * Our caller guarantees that "as" is at least the string ".". 871 */ 872 int 873 expand(as, rflg, ap) 874 char *as; 875 int rflg; 876 struct arglist *ap; 877 { 878 int count, size; 879 char dir = 0; 880 char *rescan = 0; 881 RST_DIR *dirp; 882 char *s, *cs; 883 int sindex, rindexa, lindex; 884 struct direct *dp; 885 char slash; 886 char *rs; 887 char c; 888 wchar_t w_fname[PATH_MAX+1]; 889 wchar_t w_pname[PATH_MAX+1]; 890 891 /* 892 * check for meta chars 893 */ 894 s = cs = as; 895 slash = 0; 896 while (*cs != '*' && *cs != '?' && *cs != '[') { 897 if (*cs++ == 0) { 898 if (rflg && slash) 899 break; 900 else 901 return (0); 902 } else if (*cs == '/') { 903 slash++; 904 } 905 } 906 for (;;) { 907 if (cs == s) { 908 s = ""; 909 break; 910 } else if (*--cs == '/') { 911 *cs = 0; 912 if (s == cs) 913 s = "/"; 914 break; 915 } 916 } 917 if ((dirp = rst_opendir(s)) != NULL) 918 dir++; 919 count = 0; 920 if (*cs == 0) 921 *cs++ = DELIMCHAR; 922 if (dir) { 923 /* 924 * check for rescan 925 */ 926 rs = cs; 927 do { 928 if (*rs == '/') { 929 rescan = rs; 930 *rs = 0; 931 } 932 } while (*rs++); 933 /* LINTED: result fits into an int */ 934 sindex = (int)(ap->last - ap->head); 935 (void) mbstowcs(w_pname, cs, PATH_MAX); 936 w_pname[PATH_MAX - 1] = 0; 937 while ((dp = rst_readdir(dirp)) != NULL && dp->d_ino != 0) { 938 if (!dflag && BIT(dp->d_ino, dumpmap) == 0) 939 continue; 940 if ((*dp->d_name == '.' && *cs != '.')) 941 continue; 942 (void) mbstowcs(w_fname, dp->d_name, PATH_MAX); 943 w_fname[PATH_MAX - 1] = 0; 944 if (gmatch(w_fname, w_pname)) { 945 if (addg(dp, s, rescan, ap) < 0) { 946 rst_closedir(dirp); 947 return (-1); 948 } 949 count++; 950 } 951 } 952 if (rescan) { 953 rindexa = sindex; 954 /* LINTED: result fits into an int */ 955 lindex = (int)(ap->last - ap->head); 956 if (count) { 957 count = 0; 958 while (rindexa < lindex) { 959 size = expand(ap->head[rindexa].fname, 960 1, ap); 961 if (size < 0) { 962 rst_closedir(dirp); 963 return (size); 964 } 965 count += size; 966 rindexa++; 967 } 968 } 969 /* LINTED: lint is confused about pointer size/type */ 970 bcopy((void *)(&ap->head[lindex]), 971 (void *)(&ap->head[sindex]), 972 (size_t)((ap->last - &ap->head[rindexa])) * 973 sizeof (*ap->head)); 974 ap->last -= lindex - sindex; 975 *rescan = '/'; 976 } 977 rst_closedir(dirp); 978 } 979 s = as; 980 while ((c = *s) != '\0') 981 *s++ = (c != DELIMCHAR ? c : '/'); 982 983 return (count); 984 } 985 986 /* 987 * Check for a name match 988 */ 989 static int 990 gmatch(s, p) 991 wchar_t *s; /* string to test */ 992 wchar_t *p; /* pattern to match against */ 993 { 994 long scc; /* source character to text */ 995 wchar_t c; /* pattern character to match */ 996 char ok; /* [x-y] range match status */ 997 long lc; /* left character of [x-y] range */ 998 999 scc = *s++; 1000 switch (c = *p++) { 1001 1002 case '[': 1003 ok = 0; 1004 lc = -1; 1005 while (c = *p++) { 1006 if (c == ']') { 1007 return (ok ? gmatch(s, p) : 0); 1008 } else if (c == '-') { 1009 wchar_t rc = *p++; 1010 /* 1011 * Check both ends must belong to 1012 * the same codeset. 1013 */ 1014 if (wcsetno(lc) != wcsetno(rc)) { 1015 /* 1016 * If not, ignore the '-' 1017 * operator and [x-y] is 1018 * treated as if it were 1019 * [xy]. 1020 */ 1021 if (scc == lc) 1022 ok++; 1023 if (scc == (lc = rc)) 1024 ok++; 1025 } else if (lc <= scc && scc <= rc) 1026 ok++; 1027 } else { 1028 lc = c; 1029 if (scc == lc) 1030 ok++; 1031 } 1032 } 1033 /* No closing bracket => failure */ 1034 return (0); 1035 1036 default: 1037 if (c != scc) 1038 return (0); 1039 /*FALLTHROUGH*/ 1040 1041 case '?': 1042 return (scc ? gmatch(s, p) : 0); 1043 1044 case '*': 1045 if (*p == 0) 1046 return (1); 1047 s--; 1048 while (*s) { 1049 if (gmatch(s++, p)) 1050 return (1); 1051 } 1052 return (0); 1053 1054 case 0: 1055 return (scc == 0); 1056 } 1057 } 1058 1059 /* 1060 * Construct a matched name. 1061 */ 1062 static int 1063 addg(dp, as1, as3, ap) 1064 struct direct *dp; /* The directory containing the name */ 1065 char *as1; /* The current directory */ 1066 char *as3; /* The file name in dp */ 1067 struct arglist *ap; /* Where to append the new name */ 1068 { 1069 char *s1, *s2, *limit; 1070 int c; 1071 char buf[MAXPATHLEN + 1]; 1072 1073 s2 = buf; 1074 limit = buf + sizeof (buf) - 1; 1075 s1 = as1; 1076 while ((c = *s1++) != '\0' && s2 < limit) { 1077 if (c == DELIMCHAR) { 1078 *s2++ = '/'; 1079 break; 1080 } 1081 /* LINTED narrowing cast */ 1082 *s2++ = (char)c; 1083 } 1084 s1 = dp->d_name; 1085 while ((*s2 = *s1++) != '\0' && s2 < limit) 1086 s2++; 1087 s1 = as3; 1088 if (s1 != NULL && s2 < limit) { 1089 *s2++ = '/'; 1090 1091 while ((*s2++ = *++s1) != '\0' && s2 < limit) { 1092 continue; 1093 /*LINTED [empty loop body]*/ 1094 } 1095 } 1096 *s2 = '\0'; 1097 if (mkentry(buf, dp->d_ino, ap) == FAIL) 1098 return (-1); 1099 return (0); 1100 } 1101 1102 1103 /* 1104 * Resolve a "complex" pathname (as generated by myname()) into 1105 * a file descriptor and a relative path. The file descriptor 1106 * will reference the hidden directory containing the attribute 1107 * named by the relative path. If the provided path is not 1108 * complex, the returned file descriptor will be AT_FDCWD and rpath 1109 * will equal path. 1110 * 1111 * This function is intended to be used to transform a complex 1112 * pathname into a pair of handles that can be used to actually 1113 * manipulate the named file. Since extended attributes have 1114 * an independant name space, a file descriptor for a directory 1115 * in the attribute name space is necessary to actually manipulate 1116 * the attribute file (via the path-relative xxxat() system calls 1117 * or a call to fchdir()). 1118 * 1119 * In the event of an error, the returned file descriptor will be 1120 * -1. It is expected that callers will either check for this 1121 * condition directly, or attempt to use the descriptor, fail, and 1122 * generate an appropriate context-specific error message. 1123 * 1124 * This function is pretty much a no-op for "simple" (non-attribute) 1125 * paths. 1126 */ 1127 void 1128 resolve(path, fd, rpath) 1129 char *path; 1130 int *fd; 1131 char **rpath; 1132 { 1133 int tfd; 1134 1135 *fd = tfd = AT_FDCWD; 1136 *rpath = path; 1137 path = *rpath + strlen(*rpath) +1; 1138 while (*path != '\0' && 1139 (*fd = openat64(tfd, *rpath, O_RDONLY)) > 0) { 1140 if (tfd != AT_FDCWD) (void) close(tfd); 1141 tfd = *fd; 1142 *rpath = path; 1143 path = *rpath + strlen(*rpath) +1; 1144 } 1145 if (*fd == AT_FDCWD) 1146 return; 1147 if (*fd < 0 || (*fd = openat64(tfd, ".", O_RDONLY|O_XATTR)) < 0) { 1148 int saverr = errno; 1149 (void) fprintf(stderr, 1150 gettext("Warning: cannot fully resolve %s: %s"), 1151 path, strerror(saverr)); 1152 (void) fflush(stderr); 1153 } 1154 if (tfd != AT_FDCWD) (void) close(tfd); 1155 } 1156 1157 /* 1158 * Copy a complex pathname to another string. Note that the 1159 * length returned by this function is the number of characters 1160 * up to (but not including) the final NULL. 1161 */ 1162 int 1163 complexcpy(s1, s2, max) 1164 char *s1; 1165 char *s2; 1166 int max; 1167 { 1168 int nullseen = 0; 1169 int len = 0; 1170 1171 while (len++ < max) { 1172 *s1++ = *s2; 1173 if (*s2++ == '\0') { 1174 if (nullseen) 1175 return (len-1); 1176 else 1177 nullseen = 1; 1178 } else { 1179 nullseen = 0; 1180 } 1181 } 1182 *s1 = '\0'; 1183 if (nullseen == 0) 1184 *--s1 = '\0'; 1185 fprintf(stderr, 1186 gettext("Warning: unterminated source string in complexcpy\n")); 1187 return (max-1); 1188 } 1189