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