1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 25 26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 29 30 /* Parts of this product may be derived from */ 31 /* Mortice Kern Systems Inc. and Berkeley 4.3 BSD systems. */ 32 /* licensed from Mortice Kern Systems Inc. and */ 33 /* the University of California. */ 34 35 /* 36 * Copyright 1985, 1990 by Mortice Kern Systems Inc. All rights reserved. 37 */ 38 39 #include <stdio.h> 40 #include <errno.h> 41 #include <pwd.h> 42 #include <grp.h> 43 #include <sys/types.h> 44 #include <sys/stat.h> 45 #include <sys/param.h> 46 #include <sys/acl.h> 47 #include <limits.h> 48 #include <unistd.h> 49 #include <stdlib.h> 50 #include <locale.h> 51 #include <string.h> 52 #include <strings.h> 53 #include <ctype.h> 54 #include <wait.h> 55 #include <fnmatch.h> 56 #include <langinfo.h> 57 #include <ftw.h> 58 #include <libgen.h> 59 #include "getresponse.h" 60 61 #define A_DAY (long)(60*60*24) /* a day full of seconds */ 62 #define A_MIN (long)(60) 63 #define BLKSIZ 512 64 #define round(x, s) (((x)+(s)-1)&~((s)-1)) 65 #ifndef FTW_SLN 66 #define FTW_SLN 7 67 #endif 68 #define LINEBUF_SIZE LINE_MAX /* input or output lines */ 69 #define REMOTE_FS "/etc/dfs/fstypes" 70 #define N_FSTYPES 20 71 #define SHELL_MAXARGS 253 /* see doexec() for description */ 72 73 /* 74 * This is the list of operations 75 * F_USER and F_GROUP are named to avoid conflict with USER and GROUP defined 76 * in sys/acl.h 77 */ 78 79 enum Command 80 { 81 PRINT, DEPTH, LOCAL, MOUNT, ATIME, MTIME, CTIME, NEWER, 82 NAME, F_USER, F_GROUP, INUM, SIZE, LINKS, PERM, EXEC, OK, CPIO, NCPIO, 83 TYPE, AND, OR, NOT, LPAREN, RPAREN, CSIZE, VARARGS, FOLLOW, 84 PRUNE, NOUSER, NOGRP, FSTYPE, LS, XATTR, ACL, MMIN, AMIN, CMIN 85 }; 86 87 enum Type 88 { 89 Unary, Id, Num, Str, Exec, Cpio, Op 90 }; 91 92 struct Args 93 { 94 char name[10]; 95 enum Command action; 96 enum Type type; 97 }; 98 99 /* 100 * Except for pathnames, these are the only legal arguments 101 */ 102 static struct Args commands[] = 103 { 104 "!", NOT, Op, 105 "(", LPAREN, Unary, 106 ")", RPAREN, Unary, 107 "-a", AND, Op, 108 "-amin", AMIN, Num, 109 "-atime", ATIME, Num, 110 "-cpio", CPIO, Cpio, 111 "-cmin", CMIN, Num, 112 "-ctime", CTIME, Num, 113 "-depth", DEPTH, Unary, 114 "-exec", EXEC, Exec, 115 "-follow", FOLLOW, Unary, 116 "-group", F_GROUP, Num, 117 "-inum", INUM, Num, 118 "-links", LINKS, Num, 119 "-local", LOCAL, Unary, 120 "-mount", MOUNT, Unary, 121 "-mmin", MMIN, Num, 122 "-mtime", MTIME, Num, 123 "-name", NAME, Str, 124 "-ncpio", NCPIO, Cpio, 125 "-newer", NEWER, Str, 126 "-o", OR, Op, 127 "-ok", OK, Exec, 128 "-perm", PERM, Num, 129 "-print", PRINT, Unary, 130 "-size", SIZE, Num, 131 "-type", TYPE, Num, 132 "-xdev", MOUNT, Unary, 133 "-user", F_USER, Num, 134 "-prune", PRUNE, Unary, 135 "-nouser", NOUSER, Unary, 136 "-nogroup", NOGRP, Unary, 137 "-fstype", FSTYPE, Str, 138 "-ls", LS, Unary, 139 "-xattr", XATTR, Unary, 140 "-acl", ACL, Unary, 141 NULL, 0, 0 142 }; 143 144 union Item 145 { 146 struct Node *np; 147 struct Arglist *vp; 148 time_t t; 149 char *cp; 150 char **ap; 151 long l; 152 int i; 153 long long ll; 154 }; 155 156 struct Node 157 { 158 struct Node *next; 159 enum Command action; 160 enum Type type; 161 union Item first; 162 union Item second; 163 }; 164 165 /* if no -print, -exec or -ok replace "expression" with "(expression) -print" */ 166 static struct Node PRINT_NODE = { 0, PRINT, 0, 0}; 167 static struct Node LPAREN_NODE = { 0, LPAREN, 0, 0}; 168 169 170 /* 171 * Prototype variable size arglist buffer 172 */ 173 174 struct Arglist 175 { 176 struct Arglist *next; 177 char *end; 178 char *nextstr; 179 char **firstvar; 180 char **nextvar; 181 char *arglist[1]; 182 }; 183 184 185 static int compile(); 186 static int execute(); 187 static int doexec(char *, char **, int *); 188 static struct Args *lookup(); 189 static int ok(); 190 static void usage(void) __NORETURN; 191 static struct Arglist *varargs(); 192 static int list(); 193 static char *getgroup(); 194 static FILE *cmdopen(); 195 static int cmdclose(); 196 static char *getshell(); 197 static void init_remote_fs(); 198 static char *getname(); 199 static int readmode(); 200 static mode_t getmode(); 201 static char *gettail(); 202 203 204 static int walkflags = FTW_CHDIR|FTW_PHYS|FTW_ANYERR|FTW_NOLOOP; 205 static struct Node *topnode; 206 static struct Node *freenode; /* next free node we may use later */ 207 static char *cpio[] = { "cpio", "-o", 0 }; 208 static char *ncpio[] = { "cpio", "-oc", 0 }; 209 static char *cpiol[] = { "cpio", "-oL", 0 }; 210 static char *ncpiol[] = { "cpio", "-ocL", 0 }; 211 static time_t now; 212 static FILE *output; 213 static char *dummyarg = (char *)-1; 214 static int lastval; 215 static int varsize; 216 static struct Arglist *lastlist; 217 static char *cmdname; 218 static char *remote_fstypes[N_FSTYPES+1]; 219 static int fstype_index = 0; 220 static int action_expression = 0; /* -print, -exec, or -ok */ 221 static int error = 0; 222 static int paren_cnt = 0; /* keeps track of parentheses */ 223 static int hflag = 0; 224 static int lflag = 0; 225 /* set when doexec()-invoked utility returns non-zero */ 226 static int exec_exitcode = 0; 227 extern char **environ; 228 229 int 230 main(int argc, char **argv) 231 { 232 char *cp; 233 int c; 234 int paths; 235 char *cwdpath; 236 237 (void) setlocale(LC_ALL, ""); 238 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 239 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 240 #endif 241 (void) textdomain(TEXT_DOMAIN); 242 243 cmdname = argv[0]; 244 if (time(&now) == (time_t)(-1)) { 245 (void) fprintf(stderr, gettext("%s: time() %s\n"), 246 cmdname, strerror(errno)); 247 exit(1); 248 } 249 while ((c = getopt(argc, argv, "HL")) != -1) { 250 switch (c) { 251 case 'H': 252 hflag = 1; 253 lflag = 0; 254 break; 255 case 'L': 256 hflag = 0; 257 lflag = 1; 258 break; 259 case '?': 260 usage(); 261 break; 262 } 263 } 264 265 argc -= optind; 266 argv += optind; 267 268 if (argc < 1) { 269 (void) fprintf(stderr, 270 gettext("%s: insufficient number of arguments\n"), cmdname); 271 usage(); 272 } 273 274 for (paths = 0; (cp = argv[paths]) != 0; ++paths) { 275 if (*cp == '-') 276 break; 277 else if ((*cp == '!' || *cp == '(') && *(cp+1) == 0) 278 break; 279 } 280 281 if (paths == 0) /* no path-list */ 282 usage(); 283 284 output = stdout; 285 286 /* lflag is the same as -follow */ 287 if (lflag) 288 walkflags &= ~FTW_PHYS; 289 290 /* allocate enough space for the compiler */ 291 topnode = malloc((argc + 1) * sizeof (struct Node)); 292 (void) memset(topnode, 0, (argc + 1) * sizeof (struct Node)); 293 294 if (compile(argv + paths, topnode, &action_expression) == 0) { 295 /* no expression, default to -print */ 296 (void) memcpy(topnode, &PRINT_NODE, sizeof (struct Node)); 297 } else if (!action_expression) { 298 /* 299 * if no action expression, insert an LPAREN node above topnode, 300 * with a PRINT node as its next node 301 */ 302 struct Node *savenode; 303 304 if (freenode == NULL) { 305 (void) fprintf(stderr, gettext("%s: can't append -print" 306 " implicitly; try explicit -print option\n"), 307 cmdname); 308 exit(1); 309 } 310 savenode = topnode; 311 topnode = freenode++; 312 (void) memcpy(topnode, &LPAREN_NODE, sizeof (struct Node)); 313 topnode->next = freenode; 314 topnode->first.np = savenode; 315 (void) memcpy(topnode->next, &PRINT_NODE, sizeof (struct Node)); 316 } 317 318 while (paths--) { 319 char *curpath; 320 struct stat sb; 321 322 curpath = *(argv++); 323 324 /* 325 * If -H is specified, it means we walk the first 326 * level (pathname on command line) logically, following 327 * symlinks, but lower levels are walked physically. 328 * We use our own secret interface to nftw() to change 329 * the from stat to lstat after the top level is walked. 330 */ 331 if (hflag) { 332 if (stat(curpath, &sb) < 0 && errno == ENOENT) 333 walkflags &= ~FTW_HOPTION; 334 else 335 walkflags |= FTW_HOPTION; 336 } 337 338 /* 339 * We need this check as nftw needs a CWD and we have no 340 * way of returning back from that code with a meaningful 341 * error related to this 342 */ 343 if ((cwdpath = getcwd(NULL, PATH_MAX)) == NULL) { 344 (void) fprintf(stderr, 345 gettext("%s : cannot get the current working " 346 "directory\n"), cmdname); 347 exit(1); 348 } else 349 free(cwdpath); 350 351 352 if (nftw(curpath, execute, 1000, walkflags)) { 353 (void) fprintf(stderr, 354 gettext("%s: cannot open %s: %s\n"), 355 cmdname, curpath, strerror(errno)); 356 error = 1; 357 } 358 359 } 360 361 /* execute any remaining variable length lists */ 362 while (lastlist) { 363 if (lastlist->end != lastlist->nextstr) { 364 *lastlist->nextvar = 0; 365 (void) doexec((char *)0, lastlist->arglist, 366 &exec_exitcode); 367 } 368 lastlist = lastlist->next; 369 } 370 if (output != stdout) 371 return (cmdclose(output)); 372 return ((exec_exitcode != 0) ? exec_exitcode : error); 373 } 374 375 /* 376 * compile the arguments 377 */ 378 379 static int 380 compile(argv, np, actionp) 381 char **argv; 382 struct Node *np; 383 int *actionp; 384 { 385 char *b; 386 char **av; 387 struct Node *oldnp = topnode; 388 struct Args *argp; 389 char **com; 390 int i; 391 enum Command wasop = PRINT; 392 393 if (init_yes() < 0) { 394 (void) fprintf(stderr, gettext(ERR_MSG_INIT_YES), 395 strerror(errno)); 396 exit(1); 397 } 398 399 for (av = argv; *av && (argp = lookup(*av)); av++) { 400 np->next = 0; 401 np->action = argp->action; 402 np->type = argp->type; 403 np->second.i = 0; 404 if (argp->type == Op) { 405 if (wasop == NOT || (wasop && np->action != NOT)) { 406 (void) fprintf(stderr, 407 gettext("%s: operand follows operand\n"), 408 cmdname); 409 exit(1); 410 } 411 if (np->action != NOT && oldnp == 0) 412 goto err; 413 wasop = argp->action; 414 } else { 415 wasop = PRINT; 416 if (argp->type != Unary) { 417 if (!(b = *++av)) { 418 (void) fprintf(stderr, 419 gettext("%s: incomplete statement\n"), 420 cmdname); 421 exit(1); 422 } 423 if (argp->type == Num) { 424 if ((argp->action != PERM) || 425 (*b != '+')) { 426 if (*b == '+' || *b == '-') { 427 np->second.i = *b; 428 b++; 429 } 430 } 431 } 432 } 433 } 434 switch (argp->action) { 435 case AND: 436 break; 437 case NOT: 438 break; 439 case OR: 440 np->first.np = topnode; 441 topnode = np; 442 oldnp->next = 0; 443 break; 444 445 case LPAREN: { 446 struct Node *save = topnode; 447 topnode = np+1; 448 paren_cnt++; 449 i = compile(++av, topnode, actionp); 450 np->first.np = topnode; 451 topnode = save; 452 av += i; 453 oldnp = np; 454 np += i + 1; 455 oldnp->next = np; 456 continue; 457 } 458 459 case RPAREN: 460 if (paren_cnt <= 0) { 461 (void) fprintf(stderr, 462 gettext("%s: unmatched ')'\n"), 463 cmdname); 464 exit(1); 465 } 466 paren_cnt--; 467 if (oldnp == 0) 468 goto err; 469 if (oldnp->type == Op) { 470 (void) fprintf(stderr, 471 gettext("%s: cannot immediately" 472 " follow an operand with ')'\n"), 473 cmdname); 474 exit(1); 475 } 476 oldnp->next = 0; 477 return (av-argv); 478 479 case FOLLOW: 480 walkflags &= ~FTW_PHYS; 481 break; 482 case MOUNT: 483 walkflags |= FTW_MOUNT; 484 break; 485 case DEPTH: 486 walkflags |= FTW_DEPTH; 487 break; 488 489 case LOCAL: 490 np->first.l = 0L; 491 np->first.ll = 0LL; 492 np->second.i = '+'; 493 /* 494 * Make it compatible to df -l for 495 * future enhancement. So, anything 496 * that is not remote, then it is 497 * local. 498 */ 499 init_remote_fs(); 500 break; 501 502 case SIZE: 503 if (b[strlen(b)-1] == 'c') 504 np->action = CSIZE; 505 /*FALLTHROUGH*/ 506 case INUM: 507 np->first.ll = atoll(b); 508 break; 509 510 case CMIN: 511 case CTIME: 512 case MMIN: 513 case MTIME: 514 case AMIN: 515 case ATIME: 516 case LINKS: 517 np->first.l = atol(b); 518 break; 519 520 case F_USER: 521 case F_GROUP: { 522 struct passwd *pw; 523 struct group *gr; 524 long value; 525 char *q; 526 527 value = -1; 528 if (argp->action == F_USER) { 529 if ((pw = getpwnam(b)) != 0) 530 value = (long)pw->pw_uid; 531 } else { 532 if ((gr = getgrnam(b)) != 0) 533 value = (long)gr->gr_gid; 534 } 535 if (value == -1) { 536 errno = 0; 537 value = strtol(b, &q, 10); 538 if (errno != 0 || q == b || *q != '\0') { 539 (void) fprintf(stderr, gettext( 540 "%s: cannot find %s name\n"), 541 cmdname, *av); 542 exit(1); 543 } 544 } 545 np->first.l = value; 546 break; 547 } 548 549 case EXEC: 550 case OK: 551 walkflags &= ~FTW_CHDIR; 552 np->first.ap = av; 553 (*actionp)++; 554 for (;;) { 555 if ((b = *av) == 0) { 556 (void) fprintf(stderr, 557 gettext("%s: incomplete statement\n"), 558 cmdname); 559 exit(1); 560 } 561 if (strcmp(b, ";") == 0) { 562 *av = 0; 563 break; 564 } else if (strcmp(b, "{}") == 0) 565 *av = dummyarg; 566 else if (strcmp(b, "+") == 0 && 567 av[-1] == dummyarg && 568 np->action == EXEC) { 569 av[-1] = 0; 570 np->first.vp = varargs(np->first.ap); 571 np->action = VARARGS; 572 break; 573 } 574 av++; 575 } 576 break; 577 578 case NAME: 579 np->first.cp = b; 580 break; 581 case PERM: 582 if (*b == '-') 583 ++b; 584 585 if (readmode(b) != NULL) { 586 (void) fprintf(stderr, gettext( 587 "find: -perm: Bad permission string\n")); 588 usage(); 589 } 590 np->first.l = (long)getmode((mode_t)0); 591 break; 592 case TYPE: 593 i = *b; 594 np->first.l = 595 i == 'd' ? S_IFDIR : 596 i == 'b' ? S_IFBLK : 597 i == 'c' ? S_IFCHR : 598 #ifdef S_IFIFO 599 i == 'p' ? S_IFIFO : 600 #endif 601 i == 'f' ? S_IFREG : 602 #ifdef S_IFLNK 603 i == 'l' ? S_IFLNK : 604 #endif 605 #ifdef S_IFSOCK 606 i == 's' ? S_IFSOCK : 607 #endif 608 #ifdef S_IFDOOR 609 i == 'D' ? S_IFDOOR : 610 #endif 611 0; 612 break; 613 614 case CPIO: 615 if (walkflags & FTW_PHYS) 616 com = cpio; 617 else 618 com = cpiol; 619 goto common; 620 621 case NCPIO: { 622 FILE *fd; 623 624 if (walkflags & FTW_PHYS) 625 com = ncpio; 626 else 627 com = ncpiol; 628 common: 629 /* set up cpio */ 630 if ((fd = fopen(b, "w")) == NULL) { 631 (void) fprintf(stderr, 632 gettext("%s: cannot create %s\n"), 633 cmdname, b); 634 exit(1); 635 } 636 637 np->first.l = (long)cmdopen("cpio", com, "w", fd); 638 (void) fclose(fd); 639 walkflags |= FTW_DEPTH; 640 np->action = CPIO; 641 } 642 /*FALLTHROUGH*/ 643 case PRINT: 644 (*actionp)++; 645 break; 646 647 case NEWER: { 648 struct stat statb; 649 if (stat(b, &statb) < 0) { 650 (void) fprintf(stderr, 651 gettext("%s: cannot access %s\n"), 652 cmdname, b); 653 exit(1); 654 } 655 np->first.l = statb.st_mtime; 656 np->second.i = '+'; 657 break; 658 } 659 660 case PRUNE: 661 case NOUSER: 662 case NOGRP: 663 break; 664 case FSTYPE: 665 np->first.cp = b; 666 break; 667 case LS: 668 (*actionp)++; 669 break; 670 case XATTR: 671 break; 672 case ACL: 673 break; 674 } 675 676 oldnp = np++; 677 oldnp->next = np; 678 } 679 680 if ((*av) || (wasop)) 681 goto err; 682 683 if (paren_cnt != 0) { 684 (void) fprintf(stderr, gettext("%s: unmatched '('\n"), 685 cmdname); 686 exit(1); 687 } 688 689 /* just before returning, save next free node from the list */ 690 freenode = oldnp->next; 691 oldnp->next = 0; 692 return (av-argv); 693 err: 694 if (*av) 695 (void) fprintf(stderr, 696 gettext("%s: bad option %s\n"), cmdname, *av); 697 else 698 (void) fprintf(stderr, gettext("%s: bad option\n"), cmdname); 699 usage(); 700 /*NOTREACHED*/ 701 } 702 703 /* 704 * print out a usage message 705 */ 706 707 static void 708 usage(void) 709 { 710 (void) fprintf(stderr, 711 gettext("%s: [-H | -L] path-list predicate-list\n"), cmdname); 712 exit(1); 713 } 714 715 /* 716 * This is the function that gets executed at each node 717 */ 718 719 static int 720 execute(name, statb, type, state) 721 char *name; 722 struct stat *statb; 723 int type; 724 struct FTW *state; 725 { 726 struct Node *np = topnode; 727 int val; 728 time_t t; 729 long l; 730 long long ll; 731 int not = 1; 732 char *filename; 733 734 if (type == FTW_NS) { 735 (void) fprintf(stderr, gettext("%s: stat() error %s: %s\n"), 736 cmdname, name, strerror(errno)); 737 error = 1; 738 return (0); 739 } else if (type == FTW_DNR) { 740 (void) fprintf(stderr, gettext("%s: cannot read dir %s: %s\n"), 741 cmdname, name, strerror(errno)); 742 error = 1; 743 return (0); 744 } else if (type == FTW_SLN && lflag == 0) { 745 (void) fprintf(stderr, 746 gettext("%s: cannot follow symbolic link %s: %s\n"), 747 cmdname, name, strerror(errno)); 748 error = 1; 749 return (0); 750 } else if (type == FTW_DL) { 751 (void) fprintf(stderr, gettext("%s: cycle detected for %s\n"), 752 cmdname, name); 753 error = 1; 754 return (0); 755 } 756 757 while (np) { 758 switch (np->action) { 759 case NOT: 760 not = !not; 761 np = np->next; 762 continue; 763 764 case AND: 765 np = np->next; 766 continue; 767 768 case OR: 769 if (np->first.np == np) { 770 /* 771 * handle naked OR (no term on left hand side) 772 */ 773 (void) fprintf(stderr, 774 gettext("%s: invalid -o construction\n"), 775 cmdname); 776 exit(2); 777 } 778 /* FALLTHROUGH */ 779 case LPAREN: { 780 struct Node *save = topnode; 781 topnode = np->first.np; 782 (void) execute(name, statb, type, state); 783 val = lastval; 784 topnode = save; 785 if (np->action == OR) { 786 if (val) 787 return (0); 788 val = 1; 789 } 790 break; 791 } 792 793 case LOCAL: { 794 int nremfs; 795 val = 1; 796 /* 797 * If file system type matches the remote 798 * file system type, then it is not local. 799 */ 800 for (nremfs = 0; nremfs < fstype_index; nremfs++) { 801 if (strcmp(remote_fstypes[nremfs], 802 statb->st_fstype) == 0) { 803 val = 0; 804 break; 805 } 806 } 807 break; 808 } 809 810 case TYPE: 811 l = (long)statb->st_mode&S_IFMT; 812 goto num; 813 814 case PERM: 815 l = (long)statb->st_mode&07777; 816 if (np->second.i == '-') 817 val = ((l&np->first.l) == np->first.l); 818 else 819 val = (l == np->first.l); 820 break; 821 822 case INUM: 823 ll = (long long)statb->st_ino; 824 goto llnum; 825 case NEWER: 826 l = statb->st_mtime; 827 goto num; 828 case ATIME: 829 t = statb->st_atime; 830 goto days; 831 case CTIME: 832 t = statb->st_ctime; 833 goto days; 834 case MTIME: 835 t = statb->st_mtime; 836 days: 837 l = (now-t)/A_DAY; 838 goto num; 839 case MMIN: 840 t = statb->st_mtime; 841 goto mins; 842 case AMIN: 843 t = statb->st_atime; 844 goto mins; 845 case CMIN: 846 t = statb->st_ctime; 847 goto mins; 848 mins: 849 l = (now-t)/A_MIN; 850 goto num; 851 case CSIZE: 852 ll = (long long)statb->st_size; 853 goto llnum; 854 case SIZE: 855 ll = (long long)round(statb->st_size, BLKSIZ)/BLKSIZ; 856 goto llnum; 857 case F_USER: 858 l = (long)statb->st_uid; 859 goto num; 860 case F_GROUP: 861 l = (long)statb->st_gid; 862 goto num; 863 case LINKS: 864 l = (long)statb->st_nlink; 865 goto num; 866 llnum: 867 if (np->second.i == '+') 868 val = (ll > np->first.ll); 869 else if (np->second.i == '-') 870 val = (ll < np->first.ll); 871 else 872 val = (ll == np->first.ll); 873 break; 874 num: 875 if (np->second.i == '+') 876 val = (l > np->first.l); 877 else if (np->second.i == '-') 878 val = (l < np->first.l); 879 else 880 val = (l == np->first.l); 881 break; 882 case OK: 883 val = ok(name, np->first.ap); 884 break; 885 case EXEC: 886 val = doexec(name, np->first.ap, NULL); 887 break; 888 889 case VARARGS: { 890 struct Arglist *ap = np->first.vp; 891 char *cp; 892 cp = ap->nextstr - (strlen(name)+1); 893 if (cp >= (char *)(ap->nextvar+3)) { 894 /* there is room just copy the name */ 895 val = 1; 896 (void) strcpy(cp, name); 897 *ap->nextvar++ = cp; 898 ap->nextstr = cp; 899 } else { 900 /* no more room, exec command */ 901 *ap->nextvar++ = name; 902 *ap->nextvar = 0; 903 val = 1; 904 (void) doexec((char *)0, ap->arglist, 905 &exec_exitcode); 906 ap->nextstr = ap->end; 907 ap->nextvar = ap->firstvar; 908 } 909 break; 910 } 911 912 case DEPTH: 913 case MOUNT: 914 case FOLLOW: 915 val = 1; 916 break; 917 918 case NAME: { 919 char *name1; 920 921 /* 922 * basename(3c) may modify name, so 923 * we need to pass another string 924 */ 925 if ((name1 = strdup(name)) == NULL) { 926 (void) fprintf(stderr, 927 gettext("%s: cannot strdup() %s: %s\n"), 928 cmdname, name, strerror(errno)); 929 exit(2); 930 } 931 /* 932 * XPG4 find should not treat a leading '.' in a 933 * filename specially for pattern matching. 934 * /usr/bin/find will not pattern match a leading 935 * '.' in a filename, unless '.' is explicitly 936 * specified. 937 */ 938 #ifdef XPG4 939 val = !fnmatch(np->first.cp, 940 basename(name1), 0); 941 #else 942 val = !fnmatch(np->first.cp, 943 basename(name1), FNM_PERIOD); 944 #endif 945 free(name1); 946 break; 947 } 948 949 case PRUNE: 950 if (type == FTW_D) 951 state->quit = FTW_PRUNE; 952 val = 1; 953 break; 954 case NOUSER: 955 val = ((getpwuid(statb->st_uid)) == 0); 956 break; 957 case NOGRP: 958 val = ((getgrgid(statb->st_gid)) == 0); 959 break; 960 case FSTYPE: 961 val = (strcmp(np->first.cp, statb->st_fstype) == 0); 962 break; 963 case CPIO: 964 output = (FILE *)np->first.l; 965 (void) fprintf(output, "%s\n", name); 966 val = 1; 967 break; 968 case PRINT: 969 (void) fprintf(stdout, "%s\n", name); 970 val = 1; 971 break; 972 case LS: 973 (void) list(name, statb); 974 val = 1; 975 break; 976 case XATTR: 977 filename = (walkflags & FTW_CHDIR) ? 978 gettail(name) : name; 979 val = (pathconf(filename, _PC_XATTR_EXISTS) == 1); 980 break; 981 case ACL: 982 /* 983 * Need to get the tail of the file name, since we have 984 * already chdir()ed into the directory (performed in 985 * nftw()) of the file 986 */ 987 filename = (walkflags & FTW_CHDIR) ? 988 gettail(name) : name; 989 val = acl_trivial(filename); 990 break; 991 } 992 /* 993 * evaluate 'val' and 'not' (exclusive-or) 994 * if no inversion (not == 1), return only when val == 0 995 * (primary not true). Otherwise, invert the primary 996 * and return when the primary is true. 997 * 'Lastval' saves the last result (fail or pass) when 998 * returning back to the calling routine. 999 */ 1000 if (val^not) { 1001 lastval = 0; 1002 return (0); 1003 } 1004 lastval = 1; 1005 not = 1; 1006 np = np->next; 1007 } 1008 return (0); 1009 } 1010 1011 /* 1012 * code for the -ok option 1013 */ 1014 1015 static int 1016 ok(name, argv) 1017 char *name; 1018 char *argv[]; 1019 { 1020 int c; 1021 int i = 0; 1022 char resp[LINE_MAX + 1]; 1023 1024 (void) fflush(stdout); /* to flush possible `-print' */ 1025 1026 if ((*argv != dummyarg) && (strcmp(*argv, name))) 1027 (void) fprintf(stderr, "< %s ... %s >? ", *argv, name); 1028 else 1029 (void) fprintf(stderr, "< {} ... %s >? ", name); 1030 1031 (void) fflush(stderr); 1032 1033 while ((c = getchar()) != '\n') { 1034 if (c == EOF) 1035 exit(2); 1036 if (i < LINE_MAX) 1037 resp[i++] = c; 1038 } 1039 resp[i] = '\0'; 1040 1041 if (yes_check(resp)) 1042 return (doexec(name, argv, NULL)); 1043 else 1044 return (0); 1045 } 1046 1047 /* 1048 * execute argv with {} replaced by name 1049 * 1050 * Per XPG6, find must exit non-zero if an invocation through 1051 * -exec, punctuated by a plus sign, exits non-zero, so set 1052 * exitcode if we see a non-zero exit. 1053 * exitcode should be NULL when -exec or -ok is not punctuated 1054 * by a plus sign. 1055 */ 1056 1057 static int 1058 doexec(char *name, char *argv[], int *exitcode) 1059 { 1060 char *cp; 1061 char **av = argv; 1062 char *newargs[1 + SHELL_MAXARGS + 1]; 1063 int dummyseen = 0; 1064 int i, j, status, rc, r = 0; 1065 int exit_status = 0; 1066 pid_t pid, pid1; 1067 1068 (void) fflush(stdout); /* to flush possible `-print' */ 1069 if (name) { 1070 while (cp = *av++) { 1071 if (cp == dummyarg) { 1072 dummyseen = 1; 1073 av[-1] = name; 1074 } 1075 1076 } 1077 } 1078 if (argv[0] == NULL) /* null command line */ 1079 return (r); 1080 1081 if ((pid = fork()) == -1) { 1082 /* fork failed */ 1083 if (exitcode != NULL) 1084 *exitcode = 1; 1085 return (0); 1086 } 1087 if (pid != 0) { 1088 /* parent */ 1089 do { 1090 /* wait for child to exit */ 1091 if ((rc = wait(&r)) == -1 && errno != EINTR) { 1092 (void) fprintf(stderr, 1093 gettext("wait failed %s"), strerror(errno)); 1094 1095 if (exitcode != NULL) 1096 *exitcode = 1; 1097 return (0); 1098 } 1099 } while (rc != pid); 1100 } else { 1101 /* child */ 1102 (void) execvp(argv[0], argv); 1103 if (errno != E2BIG) 1104 exit(1); 1105 1106 /* 1107 * We are in a situation where argv[0] points to a 1108 * script without the interpreter line, e.g. #!/bin/sh. 1109 * execvp() will execute either /usr/bin/sh or 1110 * /usr/xpg4/bin/sh against the script, and you will be 1111 * limited to SHELL_MAXARGS arguments. If you try to 1112 * pass more than SHELL_MAXARGS arguments, execvp() 1113 * fails with E2BIG. 1114 * See usr/src/lib/libc/port/gen/execvp.c. 1115 * 1116 * In this situation, process the argument list by 1117 * packets of SHELL_MAXARGS arguments with respect of 1118 * the following rules: 1119 * 1. the invocations have to complete before find exits 1120 * 2. only one invocation can be running at a time 1121 */ 1122 1123 i = 1; 1124 newargs[0] = argv[0]; 1125 1126 while (argv[i]) { 1127 j = 1; 1128 while (j <= SHELL_MAXARGS && argv[i]) { 1129 newargs[j++] = argv[i++]; 1130 } 1131 newargs[j] = NULL; 1132 1133 if ((pid1 = fork()) == -1) { 1134 /* fork failed */ 1135 exit(1); 1136 } 1137 if (pid1 == 0) { 1138 /* child */ 1139 (void) execvp(newargs[0], newargs); 1140 exit(1); 1141 } 1142 1143 status = 0; 1144 1145 do { 1146 /* wait for the child to exit */ 1147 if ((rc = wait(&status)) == -1 && 1148 errno != EINTR) { 1149 (void) fprintf(stderr, 1150 gettext("wait failed %s"), 1151 strerror(errno)); 1152 exit(1); 1153 } 1154 } while (rc != pid1); 1155 1156 if (status) 1157 exit_status = 1; 1158 } 1159 /* all the invocations have completed */ 1160 exit(exit_status); 1161 } 1162 1163 if (name && dummyseen) { 1164 for (av = argv; cp = *av++; ) { 1165 if (cp == name) 1166 av[-1] = dummyarg; 1167 } 1168 } 1169 1170 if (r && exitcode != NULL) 1171 *exitcode = 3; /* use to indicate error in cmd invocation */ 1172 1173 return (!r); 1174 } 1175 1176 1177 /* 1178 * Table lookup routine 1179 */ 1180 static struct Args * 1181 lookup(word) 1182 char *word; 1183 { 1184 struct Args *argp = commands; 1185 int second; 1186 if (word == 0 || *word == 0) 1187 return (0); 1188 second = word[1]; 1189 while (*argp->name) { 1190 if (second == argp->name[1] && strcmp(word, argp->name) == 0) 1191 return (argp); 1192 argp++; 1193 } 1194 return (0); 1195 } 1196 1197 1198 /* 1199 * Get space for variable length argument list 1200 */ 1201 1202 static struct Arglist * 1203 varargs(com) 1204 char **com; 1205 { 1206 struct Arglist *ap; 1207 int n; 1208 char **ep; 1209 if (varsize == 0) { 1210 n = 2*sizeof (char **); 1211 for (ep = environ; *ep; ep++) 1212 n += (strlen(*ep)+sizeof (ep) + 1); 1213 varsize = sizeof (struct Arglist)+ARG_MAX-PATH_MAX-n-1; 1214 } 1215 ap = (struct Arglist *)malloc(varsize+1); 1216 ap->end = (char *)ap + varsize; 1217 ap->nextstr = ap->end; 1218 ap->nextvar = ap->arglist; 1219 while (*ap->nextvar++ = *com++); 1220 ap->nextvar--; 1221 ap->firstvar = ap->nextvar; 1222 ap->next = lastlist; 1223 lastlist = ap; 1224 return (ap); 1225 } 1226 1227 /* 1228 * filter command support 1229 * fork and exec cmd(argv) according to mode: 1230 * 1231 * "r" with fp as stdin of cmd (default stdin), cmd stdout returned 1232 * "w" with fp as stdout of cmd (default stdout), cmd stdin returned 1233 */ 1234 1235 #define CMDERR ((1<<8)-1) /* command error exit code */ 1236 #define MAXCMDS 8 /* max # simultaneous cmdopen()'s */ 1237 1238 static struct /* info for each cmdopen() */ 1239 { 1240 FILE *fp; /* returned by cmdopen() */ 1241 pid_t pid; /* pid used by cmdopen() */ 1242 } cmdproc[MAXCMDS]; 1243 1244 static FILE * 1245 cmdopen(cmd, argv, mode, fp) 1246 char *cmd; 1247 char **argv; 1248 char *mode; 1249 FILE *fp; 1250 { 1251 int proc; 1252 int cmdfd; 1253 int usrfd; 1254 int pio[2]; 1255 1256 switch (*mode) { 1257 case 'r': 1258 cmdfd = 1; 1259 usrfd = 0; 1260 break; 1261 case 'w': 1262 cmdfd = 0; 1263 usrfd = 1; 1264 break; 1265 default: 1266 return (0); 1267 } 1268 1269 for (proc = 0; proc < MAXCMDS; proc++) 1270 if (!cmdproc[proc].fp) 1271 break; 1272 if (proc >= MAXCMDS) 1273 return (0); 1274 1275 if (pipe(pio)) 1276 return (0); 1277 1278 switch (cmdproc[proc].pid = fork()) { 1279 case -1: 1280 return (0); 1281 case 0: 1282 if (fp && fileno(fp) != usrfd) { 1283 (void) close(usrfd); 1284 if (dup2(fileno(fp), usrfd) != usrfd) 1285 _exit(CMDERR); 1286 (void) close(fileno(fp)); 1287 } 1288 (void) close(cmdfd); 1289 if (dup2(pio[cmdfd], cmdfd) != cmdfd) 1290 _exit(CMDERR); 1291 (void) close(pio[cmdfd]); 1292 (void) close(pio[usrfd]); 1293 (void) execvp(cmd, argv); 1294 if (errno == ENOEXEC) { 1295 char **p; 1296 char **v; 1297 1298 /* 1299 * assume cmd is a shell script 1300 */ 1301 1302 p = argv; 1303 while (*p++); 1304 if (v = (char **)malloc((p - argv + 1) * 1305 sizeof (char **))) { 1306 p = v; 1307 *p++ = cmd; 1308 if (*argv) argv++; 1309 while (*p++ = *argv++); 1310 (void) execv(getshell(), v); 1311 } 1312 } 1313 _exit(CMDERR); 1314 /*NOTREACHED*/ 1315 default: 1316 (void) close(pio[cmdfd]); 1317 return (cmdproc[proc].fp = fdopen(pio[usrfd], mode)); 1318 } 1319 } 1320 1321 /* 1322 * close a stream opened by cmdopen() 1323 * -1 returned if cmdopen() had a problem 1324 * otherwise exit() status of command is returned 1325 */ 1326 1327 static int 1328 cmdclose(fp) 1329 FILE *fp; 1330 { 1331 int i; 1332 pid_t p, pid; 1333 int status; 1334 1335 for (i = 0; i < MAXCMDS; i++) 1336 if (fp == cmdproc[i].fp) break; 1337 if (i >= MAXCMDS) 1338 return (-1); 1339 (void) fclose(fp); 1340 cmdproc[i].fp = 0; 1341 pid = cmdproc[i].pid; 1342 while ((p = wait(&status)) != pid && p != (pid_t)-1); 1343 if (p == pid) { 1344 status = (status >> 8) & CMDERR; 1345 if (status == CMDERR) 1346 status = -1; 1347 } 1348 else 1349 status = -1; 1350 return (status); 1351 } 1352 1353 /* 1354 * return pointer to the full path name of the shell 1355 * 1356 * SHELL is read from the environment and must start with / 1357 * 1358 * if set-uid or set-gid then the executable and its containing 1359 * directory must not be writable by the real user 1360 * 1361 * /usr/bin/sh is returned by default 1362 */ 1363 1364 char * 1365 getshell() 1366 { 1367 char *s; 1368 char *sh; 1369 uid_t u; 1370 int j; 1371 1372 if (((sh = getenv("SHELL")) != 0) && *sh == '/') { 1373 if (u = getuid()) { 1374 if ((u != geteuid() || getgid() != getegid()) && 1375 access(sh, 2) == 0) 1376 goto defshell; 1377 s = strrchr(sh, '/'); 1378 *s = 0; 1379 j = access(sh, 2); 1380 *s = '/'; 1381 if (!j) goto defshell; 1382 } 1383 return (sh); 1384 } 1385 defshell: 1386 return ("/usr/bin/sh"); 1387 } 1388 1389 /* 1390 * the following functions implement the added "-ls" option 1391 */ 1392 1393 #include <utmpx.h> 1394 #include <sys/mkdev.h> 1395 1396 struct utmpx utmpx; 1397 #define NMAX (sizeof (utmpx.ut_name)) 1398 #define SCPYN(a, b) (void) strncpy(a, b, NMAX) 1399 1400 #define NUID 64 1401 #define NGID 64 1402 1403 static struct ncache { 1404 int id; 1405 char name[NMAX+1]; 1406 } nc[NUID], gc[NGID]; 1407 1408 /* 1409 * This function assumes that the password file is hashed 1410 * (or some such) to allow fast access based on a name key. 1411 */ 1412 static char * 1413 getname(uid_t uid) 1414 { 1415 struct passwd *pw; 1416 int cp; 1417 1418 #if (((NUID) & ((NUID) - 1)) != 0) 1419 cp = uid % (NUID); 1420 #else 1421 cp = uid & ((NUID) - 1); 1422 #endif 1423 if (nc[cp].id == uid && nc[cp].name[0]) 1424 return (nc[cp].name); 1425 pw = getpwuid(uid); 1426 if (!pw) 1427 return (0); 1428 nc[cp].id = uid; 1429 SCPYN(nc[cp].name, pw->pw_name); 1430 return (nc[cp].name); 1431 } 1432 1433 /* 1434 * This function assumes that the group file is hashed 1435 * (or some such) to allow fast access based on a name key. 1436 */ 1437 static char * 1438 getgroup(gid_t gid) 1439 { 1440 struct group *gr; 1441 int cp; 1442 1443 #if (((NGID) & ((NGID) - 1)) != 0) 1444 cp = gid % (NGID); 1445 #else 1446 cp = gid & ((NGID) - 1); 1447 #endif 1448 if (gc[cp].id == gid && gc[cp].name[0]) 1449 return (gc[cp].name); 1450 gr = getgrgid(gid); 1451 if (!gr) 1452 return (0); 1453 gc[cp].id = gid; 1454 SCPYN(gc[cp].name, gr->gr_name); 1455 return (gc[cp].name); 1456 } 1457 1458 #define permoffset(who) ((who) * 3) 1459 #define permission(who, type) ((type) >> permoffset(who)) 1460 #define kbytes(bytes) (((bytes) + 1023) / 1024) 1461 1462 static int 1463 list(file, stp) 1464 char *file; 1465 struct stat *stp; 1466 { 1467 char pmode[32], uname[32], gname[32], fsize[32], ftime[32]; 1468 int trivial; 1469 1470 /* 1471 * Each line below contains the relevant permission (column 1) and character 1472 * shown when the corresponding execute bit is either clear (column 2) 1473 * or set (column 3) 1474 * These permissions are as shown by ls(1b) 1475 */ 1476 static long special[] = { S_ISUID, 'S', 's', 1477 S_ISGID, 'S', 's', 1478 S_ISVTX, 'T', 't' }; 1479 1480 static time_t sixmonthsago = -1; 1481 #ifdef S_IFLNK 1482 char flink[MAXPATHLEN + 1]; 1483 #endif 1484 int who; 1485 char *cp; 1486 char *tailname; 1487 time_t now; 1488 long long ksize; 1489 1490 if (file == NULL || stp == NULL) 1491 return (-1); 1492 1493 (void) time(&now); 1494 if (sixmonthsago == -1) 1495 sixmonthsago = now - 6L*30L*24L*60L*60L; 1496 1497 switch (stp->st_mode & S_IFMT) { 1498 #ifdef S_IFDIR 1499 case S_IFDIR: /* directory */ 1500 pmode[0] = 'd'; 1501 break; 1502 #endif 1503 #ifdef S_IFCHR 1504 case S_IFCHR: /* character special */ 1505 pmode[0] = 'c'; 1506 break; 1507 #endif 1508 #ifdef S_IFBLK 1509 case S_IFBLK: /* block special */ 1510 pmode[0] = 'b'; 1511 break; 1512 #endif 1513 #ifdef S_IFIFO 1514 case S_IFIFO: /* fifo special */ 1515 pmode[0] = 'p'; 1516 break; 1517 #endif 1518 #ifdef S_IFLNK 1519 case S_IFLNK: /* symbolic link */ 1520 pmode[0] = 'l'; 1521 break; 1522 #endif 1523 #ifdef S_IFSOCK 1524 case S_IFSOCK: /* socket */ 1525 pmode[0] = 's'; 1526 break; 1527 #endif 1528 #ifdef S_IFDOOR 1529 case S_IFDOOR: /* door */ 1530 pmode[0] = 'D'; 1531 break; 1532 #endif 1533 #ifdef S_IFREG 1534 case S_IFREG: /* regular */ 1535 pmode[0] = '-'; 1536 break; 1537 #endif 1538 default: 1539 pmode[0] = '?'; 1540 break; 1541 } 1542 1543 for (who = 0; who < 3; who++) { 1544 int is_exec = stp->st_mode & permission(who, S_IEXEC)? 1 : 0; 1545 1546 if (stp->st_mode & permission(who, S_IREAD)) 1547 pmode[permoffset(who) + 1] = 'r'; 1548 else 1549 pmode[permoffset(who) + 1] = '-'; 1550 1551 if (stp->st_mode & permission(who, S_IWRITE)) 1552 pmode[permoffset(who) + 2] = 'w'; 1553 else 1554 pmode[permoffset(who) + 2] = '-'; 1555 1556 if (stp->st_mode & special[who * 3]) 1557 pmode[permoffset(who) + 3] = 1558 special[who * 3 + 1 + is_exec]; 1559 else if (is_exec) 1560 pmode[permoffset(who) + 3] = 'x'; 1561 else 1562 pmode[permoffset(who) + 3] = '-'; 1563 } 1564 1565 /* 1566 * Need to get the tail of the file name, since we have 1567 * already chdir()ed into the directory of the file 1568 */ 1569 1570 tailname = gettail(file); 1571 1572 trivial = acl_trivial(tailname); 1573 if (trivial == -1) 1574 trivial = 0; 1575 1576 if (trivial == 1) 1577 pmode[permoffset(who) + 1] = '+'; 1578 else 1579 pmode[permoffset(who) + 1] = ' '; 1580 1581 pmode[permoffset(who) + 2] = '\0'; 1582 1583 /* 1584 * Prepare uname and gname. Always add a space afterwards 1585 * to keep columns from running together. 1586 */ 1587 cp = getname(stp->st_uid); 1588 if (cp != NULL) 1589 (void) sprintf(uname, "%-8s ", cp); 1590 else 1591 (void) sprintf(uname, "%-8u ", stp->st_uid); 1592 1593 cp = getgroup(stp->st_gid); 1594 if (cp != NULL) 1595 (void) sprintf(gname, "%-8s ", cp); 1596 else 1597 (void) sprintf(gname, "%-8u ", stp->st_gid); 1598 1599 if (pmode[0] == 'b' || pmode[0] == 'c') 1600 (void) sprintf(fsize, "%3ld,%4ld", 1601 major(stp->st_rdev), minor(stp->st_rdev)); 1602 else { 1603 (void) sprintf(fsize, (stp->st_size < 100000000) ? 1604 "%8lld" : "%lld", stp->st_size); 1605 #ifdef S_IFLNK 1606 if (pmode[0] == 'l') { 1607 1608 1609 who = readlink(tailname, flink, sizeof (flink) - 1); 1610 1611 if (who >= 0) 1612 flink[who] = '\0'; 1613 else 1614 flink[0] = '\0'; 1615 } 1616 #endif 1617 } 1618 1619 cp = ctime(&stp->st_mtime); 1620 if (stp->st_mtime < sixmonthsago || stp->st_mtime > now) 1621 (void) sprintf(ftime, "%-7.7s %-4.4s", cp + 4, cp + 20); 1622 else 1623 (void) sprintf(ftime, "%-12.12s", cp + 4); 1624 1625 (void) printf((stp->st_ino < 100000) ? "%5llu " : 1626 "%llu ", stp->st_ino); /* inode # */ 1627 #ifdef S_IFSOCK 1628 ksize = (long long) kbytes(ldbtob(stp->st_blocks)); /* kbytes */ 1629 #else 1630 ksize = (long long) kbytes(stp->st_size); /* kbytes */ 1631 #endif 1632 (void) printf((ksize < 10000) ? "%4lld " : "%lld ", ksize); 1633 (void) printf("%s %2ld %s%s%s %s %s%s%s\n", 1634 pmode, /* protection */ 1635 stp->st_nlink, /* # of links */ 1636 uname, /* owner */ 1637 gname, /* group */ 1638 fsize, /* # of bytes */ 1639 ftime, /* modify time */ 1640 file, /* name */ 1641 #ifdef S_IFLNK 1642 (pmode[0] == 'l') ? " -> " : "", 1643 (pmode[0] == 'l') ? flink : "" /* symlink */ 1644 #else 1645 "", 1646 "" 1647 #endif 1648 ); 1649 1650 return (0); 1651 } 1652 1653 static char * 1654 new_string(char *s) 1655 { 1656 char *p = strdup(s); 1657 1658 if (p) 1659 return (p); 1660 (void) fprintf(stderr, gettext("%s: out of memory\n"), cmdname); 1661 exit(1); 1662 /*NOTREACHED*/ 1663 } 1664 1665 /* 1666 * Read remote file system types from REMOTE_FS into the 1667 * remote_fstypes array. 1668 */ 1669 static void 1670 init_remote_fs() 1671 { 1672 FILE *fp; 1673 char line_buf[LINEBUF_SIZE]; 1674 1675 if ((fp = fopen(REMOTE_FS, "r")) == NULL) { 1676 (void) fprintf(stderr, 1677 gettext("%s: Warning: can't open %s, ignored\n"), 1678 REMOTE_FS, cmdname); 1679 /* Use default string name for NFS */ 1680 remote_fstypes[fstype_index++] = "nfs"; 1681 return; 1682 } 1683 1684 while (fgets(line_buf, sizeof (line_buf), fp) != NULL) { 1685 char buf[LINEBUF_SIZE]; 1686 1687 /* LINTED - unbounded string specifier */ 1688 (void) sscanf(line_buf, "%s", buf); 1689 remote_fstypes[fstype_index++] = new_string(buf); 1690 1691 if (fstype_index == N_FSTYPES) 1692 break; 1693 } 1694 (void) fclose(fp); 1695 } 1696 1697 #define NPERM 30 /* Largest machine */ 1698 1699 /* 1700 * The PERM struct is the machine that builds permissions. The p_special 1701 * field contains what permissions need to be checked at run-time in 1702 * getmode(). This is one of 'X', 'u', 'g', or 'o'. It contains '\0' to 1703 * indicate normal processing. 1704 */ 1705 typedef struct PERMST { 1706 ushort_t p_who; /* Range of permission (e.g. ugo) */ 1707 ushort_t p_perm; /* Bits to turn on, off, assign */ 1708 uchar_t p_op; /* Operation: + - = */ 1709 uchar_t p_special; /* Special handling? */ 1710 } PERMST; 1711 1712 #ifndef S_ISVTX 1713 #define S_ISVTX 0 /* Not .1 */ 1714 #endif 1715 1716 /* Mask values */ 1717 #define P_A (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO) /* allbits */ 1718 #define P_U (S_ISUID|S_ISVTX|S_IRWXU) /* user */ 1719 #define P_G (S_ISGID|S_ISVTX|S_IRWXG) /* group */ 1720 #define P_O (S_ISVTX|S_IRWXO) /* other */ 1721 1722 static int iswho(int c); 1723 static int isop(int c); 1724 static int isperm(PERMST *pp, int c); 1725 1726 static PERMST machine[NPERM]; /* Permission construction machine */ 1727 static PERMST *endp; /* Last used PERM structure */ 1728 1729 static uint_t nowho; /* No who for this mode (DOS kludge) */ 1730 1731 /* 1732 * Read an ASCII string containing the symbolic/octal mode and 1733 * compile an automaton that recognizes it. The return value 1734 * is NULL if everything is OK, otherwise it is -1. 1735 */ 1736 static int 1737 readmode(ascmode) 1738 const char *ascmode; 1739 { 1740 const char *amode = ascmode; 1741 PERMST *pp; 1742 int seen_X; 1743 1744 nowho = 0; 1745 seen_X = 0; 1746 pp = &machine[0]; 1747 if (*amode >= '0' && *amode <= '7') { 1748 int mode; 1749 1750 mode = 0; 1751 while (*amode >= '0' && *amode <= '7') 1752 mode = (mode<<3) + *amode++ - '0'; 1753 if (*amode != '\0') 1754 return (-1); 1755 #if S_ISUID != 04000 || S_ISGID != 02000 || \ 1756 S_IRUSR != 0400 || S_IWUSR != 0200 || S_IXUSR != 0100 || \ 1757 S_IRGRP != 0040 || S_IWGRP != 0020 || S_IXGRP != 0010 || \ 1758 S_IROTH != 0004 || S_IWOTH != 0002 || S_IXOTH != 0001 1759 /* 1760 * There is no requirement of the octal mode bits being 1761 * the same as the S_ macros. 1762 */ 1763 { 1764 mode_t mapping[] = { 1765 S_IXOTH, S_IWOTH, S_IROTH, 1766 S_IXGRP, S_IWGRP, S_IRGRP, 1767 S_IXUSR, S_IWUSR, S_IRUSR, 1768 S_ISGID, S_ISUID, 1769 0 1770 }; 1771 int i, newmode = 0; 1772 1773 for (i = 0; mapping[i] != 0; i++) 1774 if (mode & (1<<i)) 1775 newmode |= mapping[i]; 1776 mode = newmode; 1777 } 1778 #endif 1779 pp->p_who = P_A; 1780 pp->p_perm = mode; 1781 pp->p_op = '='; 1782 } else for (;;) { 1783 int t; 1784 int who = 0; 1785 1786 while ((t = iswho(*amode)) != 0) { 1787 ++amode; 1788 who |= t; 1789 } 1790 if (who == 0) { 1791 mode_t currmask; 1792 (void) umask(currmask = umask((mode_t)0)); 1793 1794 /* 1795 * If no who specified, must use contents of 1796 * umask to determine which bits to flip. This 1797 * is POSIX/V7/BSD behaviour, but not SVID. 1798 */ 1799 who = (~currmask)&P_A; 1800 ++nowho; 1801 } else 1802 nowho = 0; 1803 samewho: 1804 if (!isop(pp->p_op = *amode++)) 1805 return (-1); 1806 pp->p_perm = 0; 1807 pp->p_special = 0; 1808 while ((t = isperm(pp, *amode)) != 0) { 1809 if (pp->p_special == 'X') { 1810 seen_X = 1; 1811 1812 if (pp->p_perm != 0) { 1813 ushort_t op; 1814 1815 /* 1816 * Remember the 'who' for the previous 1817 * transformation. 1818 */ 1819 pp->p_who = who; 1820 pp->p_special = 0; 1821 1822 op = pp->p_op; 1823 1824 /* Keep 'X' separate */ 1825 ++pp; 1826 pp->p_special = 'X'; 1827 pp->p_op = op; 1828 } 1829 } else if (seen_X) { 1830 ushort_t op; 1831 1832 /* Remember the 'who' for the X */ 1833 pp->p_who = who; 1834 1835 op = pp->p_op; 1836 1837 /* Keep 'X' separate */ 1838 ++pp; 1839 pp->p_perm = 0; 1840 pp->p_special = 0; 1841 pp->p_op = op; 1842 } 1843 ++amode; 1844 pp->p_perm |= t; 1845 } 1846 1847 /* 1848 * These returned 0, but were actually parsed, so 1849 * don't look at them again. 1850 */ 1851 switch (pp->p_special) { 1852 case 'u': 1853 case 'g': 1854 case 'o': 1855 ++amode; 1856 break; 1857 } 1858 pp->p_who = who; 1859 switch (*amode) { 1860 case '\0': 1861 break; 1862 1863 case ',': 1864 ++amode; 1865 ++pp; 1866 continue; 1867 1868 default: 1869 ++pp; 1870 goto samewho; 1871 } 1872 break; 1873 } 1874 endp = pp; 1875 return (NULL); 1876 } 1877 1878 /* 1879 * Given a character from the mode, return the associated 1880 * value as who (user designation) mask or 0 if this isn't valid. 1881 */ 1882 static int 1883 iswho(c) 1884 int c; 1885 { 1886 switch (c) { 1887 case 'a': 1888 return (P_A); 1889 1890 case 'u': 1891 return (P_U); 1892 1893 case 'g': 1894 return (P_G); 1895 1896 case 'o': 1897 return (P_O); 1898 1899 default: 1900 return (0); 1901 } 1902 /* NOTREACHED */ 1903 } 1904 1905 /* 1906 * Return non-zero if this is a valid op code 1907 * in a symbolic mode. 1908 */ 1909 static int 1910 isop(c) 1911 int c; 1912 { 1913 switch (c) { 1914 case '+': 1915 case '-': 1916 case '=': 1917 return (1); 1918 1919 default: 1920 return (0); 1921 } 1922 /* NOTREACHED */ 1923 } 1924 1925 /* 1926 * Return the permission bits implied by this character or 0 1927 * if it isn't valid. Also returns 0 when the pseudo-permissions 'u', 'g', or 1928 * 'o' are used, and sets pp->p_special to the one used. 1929 */ 1930 static int 1931 isperm(pp, c) 1932 PERMST *pp; 1933 int c; 1934 { 1935 switch (c) { 1936 case 'u': 1937 case 'g': 1938 case 'o': 1939 pp->p_special = c; 1940 return (0); 1941 1942 case 'r': 1943 return (S_IRUSR|S_IRGRP|S_IROTH); 1944 1945 case 'w': 1946 return (S_IWUSR|S_IWGRP|S_IWOTH); 1947 1948 case 'x': 1949 return (S_IXUSR|S_IXGRP|S_IXOTH); 1950 1951 #if S_ISVTX != 0 1952 case 't': 1953 return (S_ISVTX); 1954 #endif 1955 1956 case 'X': 1957 pp->p_special = 'X'; 1958 return (S_IXUSR|S_IXGRP|S_IXOTH); 1959 1960 #if S_ISVTX != 0 1961 case 'a': 1962 return (S_ISVTX); 1963 #endif 1964 1965 case 'h': 1966 return (S_ISUID); 1967 1968 /* 1969 * This change makes: 1970 * chmod +s file 1971 * set the system bit on dos but means that 1972 * chmod u+s file 1973 * chmod g+s file 1974 * chmod a+s file 1975 * are all like UNIX. 1976 */ 1977 case 's': 1978 return (nowho ? S_ISGID : S_ISGID|S_ISUID); 1979 1980 default: 1981 return (0); 1982 } 1983 /* NOTREACHED */ 1984 } 1985 1986 /* 1987 * Execute the automaton that is created by readmode() 1988 * to generate the final mode that will be used. This 1989 * code is passed a starting mode that is usually the original 1990 * mode of the file being changed (or 0). Note that this mode must contain 1991 * the file-type bits as well, so that S_ISDIR will succeed on directories. 1992 */ 1993 static mode_t 1994 getmode(mode_t startmode) 1995 { 1996 PERMST *pp; 1997 mode_t temp; 1998 mode_t perm; 1999 2000 for (pp = &machine[0]; pp <= endp; ++pp) { 2001 perm = (mode_t)0; 2002 /* 2003 * For the special modes 'u', 'g' and 'o', the named portion 2004 * of the mode refers to after the previous clause has been 2005 * processed, while the 'X' mode refers to the contents of the 2006 * mode before any clauses have been processed. 2007 * 2008 * References: P1003.2/D11.2, Section 4.7.7, 2009 * lines 2568-2570, 2578-2583 2010 */ 2011 switch (pp->p_special) { 2012 case 'u': 2013 temp = startmode & S_IRWXU; 2014 if (temp & (S_IRUSR|S_IRGRP|S_IROTH)) 2015 perm |= ((S_IRUSR|S_IRGRP|S_IROTH) & 2016 pp->p_who); 2017 if (temp & (S_IWUSR|S_IWGRP|S_IWOTH)) 2018 perm |= ((S_IWUSR|S_IWGRP|S_IWOTH) & pp->p_who); 2019 if (temp & (S_IXUSR|S_IXGRP|S_IXOTH)) 2020 perm |= ((S_IXUSR|S_IXGRP|S_IXOTH) & pp->p_who); 2021 break; 2022 2023 case 'g': 2024 temp = startmode & S_IRWXG; 2025 if (temp & (S_IRUSR|S_IRGRP|S_IROTH)) 2026 perm |= ((S_IRUSR|S_IRGRP|S_IROTH) & pp->p_who); 2027 if (temp & (S_IWUSR|S_IWGRP|S_IWOTH)) 2028 perm |= ((S_IWUSR|S_IWGRP|S_IWOTH) & pp->p_who); 2029 if (temp & (S_IXUSR|S_IXGRP|S_IXOTH)) 2030 perm |= ((S_IXUSR|S_IXGRP|S_IXOTH) & pp->p_who); 2031 break; 2032 2033 case 'o': 2034 temp = startmode & S_IRWXO; 2035 if (temp & (S_IRUSR|S_IRGRP|S_IROTH)) 2036 perm |= ((S_IRUSR|S_IRGRP|S_IROTH) & pp->p_who); 2037 if (temp & (S_IWUSR|S_IWGRP|S_IWOTH)) 2038 perm |= ((S_IWUSR|S_IWGRP|S_IWOTH) & pp->p_who); 2039 if (temp & (S_IXUSR|S_IXGRP|S_IXOTH)) 2040 perm |= ((S_IXUSR|S_IXGRP|S_IXOTH) & pp->p_who); 2041 break; 2042 2043 case 'X': 2044 perm = pp->p_perm; 2045 break; 2046 2047 default: 2048 perm = pp->p_perm; 2049 break; 2050 } 2051 switch (pp->p_op) { 2052 case '-': 2053 startmode &= ~(perm & pp->p_who); 2054 break; 2055 2056 case '=': 2057 startmode &= ~pp->p_who; 2058 /* FALLTHROUGH */ 2059 case '+': 2060 startmode |= (perm & pp->p_who); 2061 break; 2062 } 2063 } 2064 return (startmode); 2065 } 2066 2067 /* 2068 * Returns the last component of a path name, unless it is 2069 * an absolute path, in which case it returns the whole path 2070 */ 2071 static char 2072 *gettail(char *fname) 2073 { 2074 char *base = fname; 2075 2076 if (*fname != '/') { 2077 if ((base = strrchr(fname, '/')) != NULL) 2078 base++; 2079 else 2080 base = fname; 2081 } 2082 return (base); 2083 } 2084