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