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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 30 /* All Rights Reserved */ 31 32 33 /* Parts of this product may be derived from */ 34 /* Mortice Kern Systems Inc. and Berkeley 4.3 BSD systems. */ 35 /* licensed from Mortice Kern Systems Inc. and */ 36 /* the University of California. */ 37 38 /* 39 * Copyright 1985, 1990 by Mortice Kern Systems Inc. All rights reserved. 40 */ 41 42 #include <stdio.h> 43 #include <errno.h> 44 #include <pwd.h> 45 #include <grp.h> 46 #include <sys/types.h> 47 #include <sys/stat.h> 48 #include <sys/param.h> 49 #include <sys/acl.h> 50 #include <limits.h> 51 #include <unistd.h> 52 #include <stdlib.h> 53 #include <locale.h> 54 #include <string.h> 55 #include <strings.h> 56 #include <libgen.h> 57 #include <ctype.h> 58 #include <wait.h> 59 #include <fnmatch.h> 60 #include <langinfo.h> 61 #include <ftw.h> 62 63 #define A_DAY (long)(60*60*24) /* a day full of seconds */ 64 #define A_MIN (long)(60) 65 #define BLKSIZ 512 66 #define round(x, s) (((x)+(s)-1)&~((s)-1)) 67 #ifndef FTW_SLN 68 #define FTW_SLN 7 69 #endif 70 #define LINEBUF_SIZE LINE_MAX /* input or output lines */ 71 #define REMOTE_FS "/etc/dfs/fstypes" 72 #define N_FSTYPES 20 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(); 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; 206 static struct Node *savetnode; 207 static struct Node *topnode; 208 static struct Node *freenode; /* next free node we may use later */ 209 static char *cpio[] = { "cpio", "-o", 0 }; 210 static char *ncpio[] = { "cpio", "-oc", 0 }; 211 static char *cpiol[] = { "cpio", "-oL", 0 }; 212 static char *ncpiol[] = { "cpio", "-ocL", 0 }; 213 static time_t now; 214 static FILE *output; 215 static char *dummyarg = (char *)-1; 216 static int lastval; 217 static int varsize; 218 static struct Arglist *lastlist; 219 static char *cmdname; 220 static char *remote_fstypes[N_FSTYPES+1]; 221 static int fstype_index = 0; 222 static int action_expression = 0; /* -print, -exec, or -ok */ 223 static int error = 0; 224 static int paren_cnt = 0; /* keeps track of parentheses */ 225 static int hflag = 0; 226 static int lflag = 0; 227 static int varexecrc = 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 savetnode = malloc((argc + 1) * sizeof (struct Node)); 294 (void) memset(topnode, 0, (argc + 1) * sizeof (struct Node)); 295 (void) memset(savetnode, 0, (argc + 1) * sizeof (struct Node)); 296 297 if (compile(argv + paths, topnode, &action_expression) == 0) { 298 /* no expression, default to -print */ 299 (void) memcpy(topnode, &PRINT_NODE, sizeof (struct Node)); 300 } else if (!action_expression) { 301 /* 302 * if no action expression, insert an LPAREN node above topnode, 303 * with a PRINT node as its next node 304 */ 305 struct Node *savenode; 306 307 if (freenode == NULL) { 308 (void) fprintf(stderr, gettext("%s: can't append -print" 309 " implicitly; try explicit -print option\n"), 310 cmdname); 311 exit(1); 312 } 313 savenode = topnode; 314 topnode = freenode++; 315 (void) memcpy(topnode, &LPAREN_NODE, sizeof (struct Node)); 316 topnode->next = freenode; 317 topnode->first.np = savenode; 318 (void) memcpy(topnode->next, &PRINT_NODE, sizeof (struct Node)); 319 } 320 (void) memcpy(savetnode, topnode, ((argc + 1) * sizeof (struct Node))); 321 322 while (paths--) { 323 char *curpath; 324 struct stat sb; 325 326 curpath = *(argv++); 327 328 /* 329 * If -H is specified, it means we walk the first 330 * level (pathname on command line) logically, following 331 * symlinks, but lower levels are walked physically. 332 * We use our own secret interface to nftw() to change 333 * the from stat to lstat after the top level is walked. 334 */ 335 if (hflag) { 336 if (stat(curpath, &sb) < 0 && errno == ENOENT) 337 walkflags &= ~FTW_HOPTION; 338 else 339 walkflags |= FTW_HOPTION; 340 } 341 342 /* 343 * We need this check as nftw needs a CWD and we have no 344 * way of returning back from that code with a meaningful 345 * error related to this 346 */ 347 if ((cwdpath = getcwd(NULL, PATH_MAX)) == NULL) { 348 (void) fprintf(stderr, 349 gettext("%s : cannot get the current working " 350 "directory\n"), cmdname); 351 exit(1); 352 } else 353 free(cwdpath); 354 355 356 if (nftw(curpath, execute, 1000, walkflags)) { 357 (void) fprintf(stderr, 358 gettext("%s: cannot open %s: %s\n"), 359 cmdname, curpath, strerror(errno)); 360 error = 1; 361 } 362 363 if (paths > 1) 364 (void) memcpy(topnode, savetnode, 365 ((argc + 1) * sizeof (struct Node))); 366 } 367 368 /* execute any remaining variable length lists */ 369 while (lastlist) { 370 if (lastlist->end != lastlist->nextstr) { 371 *lastlist->nextvar = 0; 372 (void) doexec((char *)0, lastlist->arglist); 373 } 374 lastlist = lastlist->next; 375 } 376 if (output != stdout) 377 return (cmdclose(output)); 378 return ((varexecrc != 0) ? varexecrc : error); 379 } 380 381 /* 382 * compile the arguments 383 */ 384 385 static int 386 compile(argv, np, actionp) 387 char **argv; 388 struct Node *np; 389 int *actionp; 390 { 391 char *b; 392 char **av; 393 struct Node *oldnp = topnode; 394 struct Args *argp; 395 char **com; 396 int i; 397 enum Command wasop = PRINT; 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 i = -1; 525 if (argp->action == F_USER) { 526 if ((pw = getpwnam(b)) != 0) 527 i = (int)pw->pw_uid; 528 } else { 529 if ((gr = getgrnam(b)) != 0) 530 i = (int)gr->gr_gid; 531 } 532 if (i == -1) { 533 if (fnmatch("[0-9][0-9][0-9]*", b, 0) && 534 fnmatch("[0-9][0-9]", b, 0) && 535 fnmatch("[0-9]", b, 0)) { 536 (void) fprintf(stderr, gettext( 537 "%s: cannot find %s name\n"), 538 cmdname, *av); 539 exit(1); 540 } 541 i = atoi(b); 542 } 543 np->first.l = i; 544 break; 545 } 546 547 case EXEC: 548 case OK: 549 walkflags &= ~FTW_CHDIR; 550 np->first.ap = av; 551 (*actionp)++; 552 for (;;) { 553 if ((b = *av) == 0) { 554 (void) fprintf(stderr, 555 gettext("%s: incomplete statement\n"), 556 cmdname); 557 exit(1); 558 } 559 if (strcmp(b, ";") == 0) { 560 *av = 0; 561 break; 562 } else if (strcmp(b, "{}") == 0) 563 *av = dummyarg; 564 else if (strcmp(b, "+") == 0 && 565 av[-1] == dummyarg && 566 np->action == EXEC) { 567 av[-1] = 0; 568 np->first.vp = varargs(np->first.ap); 569 np->action = VARARGS; 570 break; 571 } 572 av++; 573 } 574 break; 575 576 case NAME: 577 np->first.cp = b; 578 break; 579 case PERM: 580 if (*b == '-') 581 ++b; 582 583 if (readmode(b) != NULL) { 584 (void) fprintf(stderr, gettext( 585 "find: -perm: Bad permission string\n")); 586 usage(); 587 } 588 np->first.l = (long)getmode((mode_t)0); 589 break; 590 case TYPE: 591 i = *b; 592 np->first.l = 593 i == 'd' ? S_IFDIR : 594 i == 'b' ? S_IFBLK : 595 i == 'c' ? S_IFCHR : 596 #ifdef S_IFIFO 597 i == 'p' ? S_IFIFO : 598 #endif 599 i == 'f' ? S_IFREG : 600 #ifdef S_IFLNK 601 i == 'l' ? S_IFLNK : 602 #endif 603 #ifdef S_IFSOCK 604 i == 's' ? S_IFSOCK : 605 #endif 606 #ifdef S_IFDOOR 607 i == 'D' ? S_IFDOOR : 608 #endif 609 0; 610 break; 611 612 case CPIO: 613 if (walkflags & FTW_PHYS) 614 com = cpio; 615 else 616 com = cpiol; 617 goto common; 618 619 case NCPIO: { 620 FILE *fd; 621 622 if (walkflags & FTW_PHYS) 623 com = ncpio; 624 else 625 com = ncpiol; 626 common: 627 /* set up cpio */ 628 if ((fd = fopen(b, "w")) == NULL) { 629 (void) fprintf(stderr, 630 gettext("%s: cannot create %s\n"), 631 cmdname, b); 632 exit(1); 633 } 634 635 np->first.l = (long)cmdopen("cpio", com, "w", fd); 636 (void) fclose(fd); 637 walkflags |= FTW_DEPTH; 638 np->action = CPIO; 639 } 640 /*FALLTHROUGH*/ 641 case PRINT: 642 (*actionp)++; 643 break; 644 645 case NEWER: { 646 struct stat statb; 647 if (stat(b, &statb) < 0) { 648 (void) fprintf(stderr, 649 gettext("%s: cannot access %s\n"), 650 cmdname, b); 651 exit(1); 652 } 653 np->first.l = statb.st_mtime; 654 np->second.i = '+'; 655 break; 656 } 657 658 case PRUNE: 659 case NOUSER: 660 case NOGRP: 661 break; 662 case FSTYPE: 663 np->first.cp = b; 664 break; 665 case LS: 666 (*actionp)++; 667 break; 668 case XATTR: 669 break; 670 case ACL: 671 break; 672 } 673 674 oldnp = np++; 675 oldnp->next = np; 676 } 677 678 if ((*av) || (wasop)) 679 goto err; 680 681 if (paren_cnt != 0) { 682 (void) fprintf(stderr, gettext("%s: unmatched '('\n"), 683 cmdname); 684 exit(1); 685 } 686 687 /* just before returning, save next free node from the list */ 688 freenode = oldnp->next; 689 oldnp->next = 0; 690 return (av-argv); 691 err: 692 if (*av) 693 (void) fprintf(stderr, 694 gettext("%s: bad option %s\n"), cmdname, *av); 695 else 696 (void) fprintf(stderr, gettext("%s: bad option\n"), cmdname); 697 usage(); 698 /*NOTREACHED*/ 699 } 700 701 /* 702 * print out a usage message 703 */ 704 705 static void 706 usage(void) 707 { 708 (void) fprintf(stderr, 709 gettext("%s: [-H | -L] path-list predicate-list\n"), cmdname); 710 exit(1); 711 } 712 713 /* 714 * This is the function that gets executed at each node 715 */ 716 717 static int 718 execute(name, statb, type, state) 719 char *name; 720 struct stat *statb; 721 int type; 722 struct FTW *state; 723 { 724 struct Node *np = topnode; 725 int val; 726 time_t t; 727 long l; 728 long long ll; 729 int not = 1; 730 char *filename; 731 732 if (type == FTW_NS) { 733 (void) fprintf(stderr, gettext("%s: stat() error %s: %s\n"), 734 cmdname, name, strerror(errno)); 735 error = 1; 736 return (0); 737 } else if (type == FTW_DNR) { 738 (void) fprintf(stderr, gettext("%s: cannot read dir %s: %s\n"), 739 cmdname, name, strerror(errno)); 740 error = 1; 741 return (0); 742 } else if (type == FTW_SLN && lflag == 0) { 743 (void) fprintf(stderr, 744 gettext("%s: cannot follow symbolic link %s: %s\n"), 745 cmdname, name, strerror(errno)); 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); 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 varexecrc = doexec((char *)0, ap->arglist); 898 ap->nextstr = ap->end; 899 ap->nextvar = ap->firstvar; 900 } 901 break; 902 } 903 904 case DEPTH: 905 case MOUNT: 906 case FOLLOW: 907 val = 1; 908 break; 909 910 case NAME: { 911 /* 912 * XPG4 find should not treat a leading '.' in a 913 * filename specially for pattern matching. 914 * /usr/bin/find will not pattern match a leading 915 * '.' in a filename, unless '.' is explicitly 916 * specified. 917 */ 918 #ifdef XPG4 919 val = !fnmatch(np->first.cp, 920 name+state->base, 0); 921 #else 922 val = !fnmatch(np->first.cp, 923 name+state->base, FNM_PERIOD); 924 #endif 925 break; 926 } 927 928 case PRUNE: 929 if (type == FTW_D) 930 state->quit = FTW_PRUNE; 931 val = 1; 932 break; 933 case NOUSER: 934 val = ((getpwuid(statb->st_uid)) == 0); 935 break; 936 case NOGRP: 937 val = ((getgrgid(statb->st_gid)) == 0); 938 break; 939 case FSTYPE: 940 val = (strcmp(np->first.cp, statb->st_fstype) == 0); 941 break; 942 case CPIO: 943 output = (FILE *)np->first.l; 944 (void) fprintf(output, "%s\n", name); 945 val = 1; 946 break; 947 case PRINT: 948 (void) fprintf(stdout, "%s\n", name); 949 val = 1; 950 break; 951 case LS: 952 (void) list(name, statb); 953 val = 1; 954 break; 955 case XATTR: 956 filename = gettail(name); 957 val = (pathconf(filename, _PC_XATTR_EXISTS) == 1); 958 break; 959 case ACL: 960 /* 961 * Need to get the tail of the file name, since we have 962 * already chdir()ed into the directory (performed in 963 * nftw()) of the file 964 */ 965 filename = gettail(name); 966 val = acl_trivial(name); 967 break; 968 } 969 /* 970 * evaluate 'val' and 'not' (exclusive-or) 971 * if no inversion (not == 1), return only when val == 0 972 * (primary not true). Otherwise, invert the primary 973 * and return when the primary is true. 974 * 'Lastval' saves the last result (fail or pass) when 975 * returning back to the calling routine. 976 */ 977 if (val^not) { 978 lastval = 0; 979 return (0); 980 } 981 lastval = 1; 982 not = 1; 983 np = np->next; 984 } 985 return (0); 986 } 987 988 /* 989 * code for the -ok option 990 */ 991 992 static int 993 ok(name, argv) 994 char *name; 995 char *argv[]; 996 { 997 int c, yes = 0; 998 999 (void) fflush(stdout); /* to flush possible `-print' */ 1000 1001 if ((*argv != dummyarg) && (strcmp(*argv, name))) 1002 (void) fprintf(stderr, "< %s ... %s >? ", *argv, name); 1003 else 1004 (void) fprintf(stderr, "< {} ... %s >? ", name); 1005 1006 (void) fflush(stderr); 1007 if ((c = tolower(getchar())) == *nl_langinfo(YESSTR)) 1008 yes = 1; 1009 while (c != '\n') 1010 if (c == EOF) 1011 exit(2); 1012 else 1013 c = getchar(); 1014 return (yes? doexec(name, argv): 0); 1015 } 1016 1017 /* 1018 * execute argv with {} replaced by name 1019 */ 1020 1021 static int 1022 doexec(name, argv) 1023 char *name; 1024 char *argv[]; 1025 { 1026 char *cp; 1027 char **av = argv; 1028 int dummyseen = 0; 1029 int r = 0; 1030 pid_t pid; 1031 1032 (void) fflush(stdout); /* to flush possible `-print' */ 1033 if (name) { 1034 while (cp = *av++) { 1035 if (cp == dummyarg) { 1036 dummyseen = 1; 1037 av[-1] = name; 1038 } 1039 1040 } 1041 } 1042 if (argv[0] == NULL) /* null command line */ 1043 return (r); 1044 1045 if (pid = fork()) { 1046 while (wait(&r) != pid); 1047 } else /* child */ { 1048 (void) execvp(argv[0], argv); 1049 exit(1); 1050 } 1051 if (name && dummyseen) { 1052 for (av = argv; cp = *av++; ) { 1053 if (cp == name) 1054 av[-1] = dummyarg; 1055 } 1056 } 1057 1058 return (!r); 1059 } 1060 1061 1062 /* 1063 * Table lookup routine 1064 */ 1065 static struct Args * 1066 lookup(word) 1067 char *word; 1068 { 1069 struct Args *argp = commands; 1070 int second; 1071 if (word == 0 || *word == 0) 1072 return (0); 1073 second = word[1]; 1074 while (*argp->name) { 1075 if (second == argp->name[1] && strcmp(word, argp->name) == 0) 1076 return (argp); 1077 argp++; 1078 } 1079 return (0); 1080 } 1081 1082 1083 /* 1084 * Get space for variable length argument list 1085 */ 1086 1087 static struct Arglist * 1088 varargs(com) 1089 char **com; 1090 { 1091 struct Arglist *ap; 1092 int n; 1093 char **ep; 1094 if (varsize == 0) { 1095 n = 2*sizeof (char **); 1096 for (ep = environ; *ep; ep++) 1097 n += (strlen(*ep)+sizeof (ep) + 1); 1098 varsize = sizeof (struct Arglist)+ARG_MAX-PATH_MAX-n-1; 1099 } 1100 ap = (struct Arglist *)malloc(varsize+1); 1101 ap->end = (char *)ap + varsize; 1102 ap->nextstr = ap->end; 1103 ap->nextvar = ap->arglist; 1104 while (*ap->nextvar++ = *com++); 1105 ap->nextvar--; 1106 ap->firstvar = ap->nextvar; 1107 ap->next = lastlist; 1108 lastlist = ap; 1109 return (ap); 1110 } 1111 1112 /* 1113 * filter command support 1114 * fork and exec cmd(argv) according to mode: 1115 * 1116 * "r" with fp as stdin of cmd (default stdin), cmd stdout returned 1117 * "w" with fp as stdout of cmd (default stdout), cmd stdin returned 1118 */ 1119 1120 #define CMDERR ((1<<8)-1) /* command error exit code */ 1121 #define MAXCMDS 8 /* max # simultaneous cmdopen()'s */ 1122 1123 static struct /* info for each cmdopen() */ 1124 { 1125 FILE *fp; /* returned by cmdopen() */ 1126 pid_t pid; /* pid used by cmdopen() */ 1127 } cmdproc[MAXCMDS]; 1128 1129 static FILE * 1130 cmdopen(cmd, argv, mode, fp) 1131 char *cmd; 1132 char **argv; 1133 char *mode; 1134 FILE *fp; 1135 { 1136 int proc; 1137 int cmdfd; 1138 int usrfd; 1139 int pio[2]; 1140 1141 switch (*mode) { 1142 case 'r': 1143 cmdfd = 1; 1144 usrfd = 0; 1145 break; 1146 case 'w': 1147 cmdfd = 0; 1148 usrfd = 1; 1149 break; 1150 default: 1151 return (0); 1152 } 1153 1154 for (proc = 0; proc < MAXCMDS; proc++) 1155 if (!cmdproc[proc].fp) 1156 break; 1157 if (proc >= MAXCMDS) 1158 return (0); 1159 1160 if (pipe(pio)) 1161 return (0); 1162 1163 switch (cmdproc[proc].pid = fork()) { 1164 case -1: 1165 return (0); 1166 case 0: 1167 if (fp && fileno(fp) != usrfd) { 1168 (void) close(usrfd); 1169 if (dup2(fileno(fp), usrfd) != usrfd) 1170 _exit(CMDERR); 1171 (void) close(fileno(fp)); 1172 } 1173 (void) close(cmdfd); 1174 if (dup2(pio[cmdfd], cmdfd) != cmdfd) 1175 _exit(CMDERR); 1176 (void) close(pio[cmdfd]); 1177 (void) close(pio[usrfd]); 1178 (void) execvp(cmd, argv); 1179 if (errno == ENOEXEC) { 1180 char **p; 1181 char **v; 1182 1183 /* 1184 * assume cmd is a shell script 1185 */ 1186 1187 p = argv; 1188 while (*p++); 1189 if (v = (char **)malloc((p - argv + 1) * 1190 sizeof (char **))) { 1191 p = v; 1192 *p++ = cmd; 1193 if (*argv) argv++; 1194 while (*p++ = *argv++); 1195 (void) execv(getshell(), v); 1196 } 1197 } 1198 _exit(CMDERR); 1199 /*NOTREACHED*/ 1200 default: 1201 (void) close(pio[cmdfd]); 1202 return (cmdproc[proc].fp = fdopen(pio[usrfd], mode)); 1203 } 1204 } 1205 1206 /* 1207 * close a stream opened by cmdopen() 1208 * -1 returned if cmdopen() had a problem 1209 * otherwise exit() status of command is returned 1210 */ 1211 1212 static int 1213 cmdclose(fp) 1214 FILE *fp; 1215 { 1216 int i; 1217 pid_t p, pid; 1218 int status; 1219 1220 for (i = 0; i < MAXCMDS; i++) 1221 if (fp == cmdproc[i].fp) break; 1222 if (i >= MAXCMDS) 1223 return (-1); 1224 (void) fclose(fp); 1225 cmdproc[i].fp = 0; 1226 pid = cmdproc[i].pid; 1227 while ((p = wait(&status)) != pid && p != (pid_t)-1); 1228 if (p == pid) { 1229 status = (status >> 8) & CMDERR; 1230 if (status == CMDERR) 1231 status = -1; 1232 } 1233 else 1234 status = -1; 1235 return (status); 1236 } 1237 1238 /* 1239 * return pointer to the full path name of the shell 1240 * 1241 * SHELL is read from the environment and must start with / 1242 * 1243 * if set-uid or set-gid then the executable and its containing 1244 * directory must not be writable by the real user 1245 * 1246 * /usr/bin/sh is returned by default 1247 */ 1248 1249 char * 1250 getshell() 1251 { 1252 char *s; 1253 char *sh; 1254 uid_t u; 1255 int j; 1256 1257 if (((sh = getenv("SHELL")) != 0) && *sh == '/') { 1258 if (u = getuid()) { 1259 if ((u != geteuid() || getgid() != getegid()) && 1260 !access(sh, 2)) 1261 goto defshell; 1262 s = strrchr(sh, '/'); 1263 *s = 0; 1264 j = access(sh, 2); 1265 *s = '/'; 1266 if (!j) goto defshell; 1267 } 1268 return (sh); 1269 } 1270 defshell: 1271 return ("/usr/bin/sh"); 1272 } 1273 1274 /* 1275 * the following functions implement the added "-ls" option 1276 */ 1277 1278 #include <utmpx.h> 1279 #include <sys/mkdev.h> 1280 1281 struct utmpx utmpx; 1282 #define NMAX (sizeof (utmpx.ut_name)) 1283 #define SCPYN(a, b) (void) strncpy(a, b, NMAX) 1284 1285 #define NUID 64 1286 #define NGID 64 1287 1288 static struct ncache { 1289 int id; 1290 char name[NMAX+1]; 1291 } nc[NUID], gc[NGID]; 1292 1293 /* 1294 * This function assumes that the password file is hashed 1295 * (or some such) to allow fast access based on a name key. 1296 */ 1297 static char * 1298 getname(uid_t uid) 1299 { 1300 struct passwd *pw; 1301 int cp; 1302 1303 #if (((NUID) & ((NUID) - 1)) != 0) 1304 cp = uid % (NUID); 1305 #else 1306 cp = uid & ((NUID) - 1); 1307 #endif 1308 if (uid >= 0 && nc[cp].id == uid && nc[cp].name[0]) 1309 return (nc[cp].name); 1310 pw = getpwuid(uid); 1311 if (!pw) 1312 return (0); 1313 nc[cp].id = uid; 1314 SCPYN(nc[cp].name, pw->pw_name); 1315 return (nc[cp].name); 1316 } 1317 1318 /* 1319 * This function assumes that the group file is hashed 1320 * (or some such) to allow fast access based on a name key. 1321 */ 1322 static char * 1323 getgroup(gid_t gid) 1324 { 1325 struct group *gr; 1326 int cp; 1327 1328 #if (((NGID) & ((NGID) - 1)) != 0) 1329 cp = gid % (NGID); 1330 #else 1331 cp = gid & ((NGID) - 1); 1332 #endif 1333 if (gid >= 0 && gc[cp].id == gid && gc[cp].name[0]) 1334 return (gc[cp].name); 1335 gr = getgrgid(gid); 1336 if (!gr) 1337 return (0); 1338 gc[cp].id = gid; 1339 SCPYN(gc[cp].name, gr->gr_name); 1340 return (gc[cp].name); 1341 } 1342 1343 #define permoffset(who) ((who) * 3) 1344 #define permission(who, type) ((type) >> permoffset(who)) 1345 #define kbytes(bytes) (((bytes) + 1023) / 1024) 1346 1347 static int 1348 list(file, stp) 1349 char *file; 1350 struct stat *stp; 1351 { 1352 char pmode[32], uname[32], gname[32], fsize[32], ftime[32]; 1353 int trivial; 1354 1355 /* 1356 * Each line below contains the relevant permission (column 1) and character 1357 * shown when the corresponding execute bit is either clear (column 2) 1358 * or set (column 3) 1359 * These permissions are as shown by ls(1b) 1360 */ 1361 static long special[] = { S_ISUID, 'S', 's', 1362 S_ISGID, 'S', 's', 1363 S_ISVTX, 'T', 't' }; 1364 1365 static time_t sixmonthsago = -1; 1366 #ifdef S_IFLNK 1367 char flink[MAXPATHLEN + 1]; 1368 #endif 1369 int who; 1370 char *cp; 1371 char *tailname; 1372 time_t now; 1373 long long ksize; 1374 1375 if (file == NULL || stp == NULL) 1376 return (-1); 1377 1378 (void) time(&now); 1379 if (sixmonthsago == -1) 1380 sixmonthsago = now - 6L*30L*24L*60L*60L; 1381 1382 switch (stp->st_mode & S_IFMT) { 1383 #ifdef S_IFDIR 1384 case S_IFDIR: /* directory */ 1385 pmode[0] = 'd'; 1386 break; 1387 #endif 1388 #ifdef S_IFCHR 1389 case S_IFCHR: /* character special */ 1390 pmode[0] = 'c'; 1391 break; 1392 #endif 1393 #ifdef S_IFBLK 1394 case S_IFBLK: /* block special */ 1395 pmode[0] = 'b'; 1396 break; 1397 #endif 1398 #ifdef S_IFIFO 1399 case S_IFIFO: /* fifo special */ 1400 pmode[0] = 'p'; 1401 break; 1402 #endif 1403 #ifdef S_IFLNK 1404 case S_IFLNK: /* symbolic link */ 1405 pmode[0] = 'l'; 1406 break; 1407 #endif 1408 #ifdef S_IFSOCK 1409 case S_IFSOCK: /* socket */ 1410 pmode[0] = 's'; 1411 break; 1412 #endif 1413 #ifdef S_IFDOOR 1414 case S_IFDOOR: /* door */ 1415 pmode[0] = 'D'; 1416 break; 1417 #endif 1418 #ifdef S_IFREG 1419 case S_IFREG: /* regular */ 1420 pmode[0] = '-'; 1421 break; 1422 #endif 1423 default: 1424 pmode[0] = '?'; 1425 break; 1426 } 1427 1428 for (who = 0; who < 3; who++) { 1429 int is_exec = stp->st_mode & permission(who, S_IEXEC)? 1 : 0; 1430 1431 if (stp->st_mode & permission(who, S_IREAD)) 1432 pmode[permoffset(who) + 1] = 'r'; 1433 else 1434 pmode[permoffset(who) + 1] = '-'; 1435 1436 if (stp->st_mode & permission(who, S_IWRITE)) 1437 pmode[permoffset(who) + 2] = 'w'; 1438 else 1439 pmode[permoffset(who) + 2] = '-'; 1440 1441 if (stp->st_mode & special[who * 3]) 1442 pmode[permoffset(who) + 3] = 1443 special[who * 3 + 1 + is_exec]; 1444 else if (is_exec) 1445 pmode[permoffset(who) + 3] = 'x'; 1446 else 1447 pmode[permoffset(who) + 3] = '-'; 1448 } 1449 1450 /* 1451 * Need to get the tail of the file name, since we have 1452 * already chdir()ed into the directory of the file 1453 */ 1454 1455 tailname = gettail(file); 1456 1457 trivial = acl_trivial(tailname); 1458 if (trivial == -1) 1459 trivial = 0; 1460 1461 if (trivial == 1) 1462 pmode[permoffset(who) + 1] = '+'; 1463 else 1464 pmode[permoffset(who) + 1] = ' '; 1465 1466 pmode[permoffset(who) + 2] = '\0'; 1467 1468 /* 1469 * Prepare uname and gname. Always add a space afterwards 1470 * to keep columns from running together. 1471 */ 1472 cp = getname(stp->st_uid); 1473 if (cp != NULL) 1474 (void) sprintf(uname, "%-8s ", cp); 1475 else 1476 (void) sprintf(uname, "%-8ld ", stp->st_uid); 1477 1478 cp = getgroup(stp->st_gid); 1479 if (cp != NULL) 1480 (void) sprintf(gname, "%-8s ", cp); 1481 else 1482 (void) sprintf(gname, "%-8ld ", stp->st_gid); 1483 1484 if (pmode[0] == 'b' || pmode[0] == 'c') 1485 (void) sprintf(fsize, "%3ld,%4ld", 1486 major(stp->st_rdev), minor(stp->st_rdev)); 1487 else { 1488 (void) sprintf(fsize, (stp->st_size < 100000000) ? 1489 "%8lld" : "%lld", stp->st_size); 1490 #ifdef S_IFLNK 1491 if (pmode[0] == 'l') { 1492 1493 1494 who = readlink(tailname, flink, sizeof (flink) - 1); 1495 1496 if (who >= 0) 1497 flink[who] = '\0'; 1498 else 1499 flink[0] = '\0'; 1500 } 1501 #endif 1502 } 1503 1504 cp = ctime(&stp->st_mtime); 1505 if (stp->st_mtime < sixmonthsago || stp->st_mtime > now) 1506 (void) sprintf(ftime, "%-7.7s %-4.4s", cp + 4, cp + 20); 1507 else 1508 (void) sprintf(ftime, "%-12.12s", cp + 4); 1509 1510 (void) printf((stp->st_ino < 100000) ? "%5llu " : 1511 "%llu ", stp->st_ino); /* inode # */ 1512 #ifdef S_IFSOCK 1513 ksize = (long long) kbytes(ldbtob(stp->st_blocks)); /* kbytes */ 1514 #else 1515 ksize = (long long) kbytes(stp->st_size); /* kbytes */ 1516 #endif 1517 (void) printf((ksize < 10000) ? "%4lld " : "%lld ", ksize); 1518 (void) printf("%s %2ld %s%s%s %s %s%s%s\n", 1519 pmode, /* protection */ 1520 stp->st_nlink, /* # of links */ 1521 uname, /* owner */ 1522 gname, /* group */ 1523 fsize, /* # of bytes */ 1524 ftime, /* modify time */ 1525 file, /* name */ 1526 #ifdef S_IFLNK 1527 (pmode[0] == 'l') ? " -> " : "", 1528 (pmode[0] == 'l') ? flink : "" /* symlink */ 1529 #else 1530 "", 1531 "" 1532 #endif 1533 ); 1534 1535 return (0); 1536 } 1537 1538 static char * 1539 new_string(char *s) 1540 { 1541 char *p = strdup(s); 1542 1543 if (p) 1544 return (p); 1545 (void) fprintf(stderr, gettext("%s: out of memory\n"), cmdname); 1546 exit(1); 1547 /*NOTREACHED*/ 1548 } 1549 1550 /* 1551 * Read remote file system types from REMOTE_FS into the 1552 * remote_fstypes array. 1553 */ 1554 static void 1555 init_remote_fs() 1556 { 1557 FILE *fp; 1558 char line_buf[LINEBUF_SIZE]; 1559 1560 if ((fp = fopen(REMOTE_FS, "r")) == NULL) { 1561 (void) fprintf(stderr, 1562 gettext("%s: Warning: can't open %s, ignored\n"), 1563 REMOTE_FS, cmdname); 1564 /* Use default string name for NFS */ 1565 remote_fstypes[fstype_index++] = "nfs"; 1566 return; 1567 } 1568 1569 while (fgets(line_buf, sizeof (line_buf), fp) != NULL) { 1570 char buf[LINEBUF_SIZE]; 1571 1572 /* LINTED - unbounded string specifier */ 1573 (void) sscanf(line_buf, "%s", buf); 1574 remote_fstypes[fstype_index++] = new_string(buf); 1575 1576 if (fstype_index == N_FSTYPES) 1577 break; 1578 } 1579 (void) fclose(fp); 1580 } 1581 1582 #define NPERM 30 /* Largest machine */ 1583 1584 /* 1585 * The PERM struct is the machine that builds permissions. The p_special 1586 * field contains what permissions need to be checked at run-time in 1587 * getmode(). This is one of 'X', 'u', 'g', or 'o'. It contains '\0' to 1588 * indicate normal processing. 1589 */ 1590 typedef struct PERMST { 1591 ushort_t p_who; /* Range of permission (e.g. ugo) */ 1592 ushort_t p_perm; /* Bits to turn on, off, assign */ 1593 uchar_t p_op; /* Operation: + - = */ 1594 uchar_t p_special; /* Special handling? */ 1595 } PERMST; 1596 1597 #ifndef S_ISVTX 1598 #define S_ISVTX 0 /* Not .1 */ 1599 #endif 1600 1601 /* Mask values */ 1602 #define P_A (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO) /* allbits */ 1603 #define P_U (S_ISUID|S_ISVTX|S_IRWXU) /* user */ 1604 #define P_G (S_ISGID|S_ISVTX|S_IRWXG) /* group */ 1605 #define P_O (S_ISVTX|S_IRWXO) /* other */ 1606 1607 static int iswho(int c); 1608 static int isop(int c); 1609 static int isperm(PERMST *pp, int c); 1610 1611 static PERMST machine[NPERM]; /* Permission construction machine */ 1612 static PERMST *endp; /* Last used PERM structure */ 1613 1614 static uint_t nowho; /* No who for this mode (DOS kludge) */ 1615 1616 /* 1617 * Read an ASCII string containing the symbolic/octal mode and 1618 * compile an automaton that recognizes it. The return value 1619 * is NULL if everything is OK, otherwise it is -1. 1620 */ 1621 static int 1622 readmode(ascmode) 1623 const char *ascmode; 1624 { 1625 const char *amode = ascmode; 1626 PERMST *pp; 1627 int seen_X; 1628 1629 nowho = 0; 1630 seen_X = 0; 1631 pp = &machine[0]; 1632 if (*amode >= '0' && *amode <= '7') { 1633 int mode; 1634 1635 mode = 0; 1636 while (*amode >= '0' && *amode <= '7') 1637 mode = (mode<<3) + *amode++ - '0'; 1638 if (*amode != '\0') 1639 return (-1); 1640 #if S_ISUID != 04000 || S_ISGID != 02000 || \ 1641 S_IRUSR != 0400 || S_IWUSR != 0200 || S_IXUSR != 0100 || \ 1642 S_IRGRP != 0040 || S_IWGRP != 0020 || S_IXGRP != 0010 || \ 1643 S_IROTH != 0004 || S_IWOTH != 0002 || S_IXOTH != 0001 1644 /* 1645 * There is no requirement of the octal mode bits being 1646 * the same as the S_ macros. 1647 */ 1648 { 1649 mode_t mapping[] = { 1650 S_IXOTH, S_IWOTH, S_IROTH, 1651 S_IXGRP, S_IWGRP, S_IRGRP, 1652 S_IXUSR, S_IWUSR, S_IRUSR, 1653 S_ISGID, S_ISUID, 1654 0 1655 }; 1656 int i, newmode = 0; 1657 1658 for (i = 0; mapping[i] != 0; i++) 1659 if (mode & (1<<i)) 1660 newmode |= mapping[i]; 1661 mode = newmode; 1662 } 1663 #endif 1664 pp->p_who = P_A; 1665 pp->p_perm = mode; 1666 pp->p_op = '='; 1667 } else for (;;) { 1668 int t; 1669 int who = 0; 1670 1671 while ((t = iswho(*amode)) != 0) { 1672 ++amode; 1673 who |= t; 1674 } 1675 if (who == 0) { 1676 mode_t currmask; 1677 (void) umask(currmask = umask((mode_t)0)); 1678 1679 /* 1680 * If no who specified, must use contents of 1681 * umask to determine which bits to flip. This 1682 * is POSIX/V7/BSD behaviour, but not SVID. 1683 */ 1684 who = (~currmask)&P_A; 1685 ++nowho; 1686 } else 1687 nowho = 0; 1688 samewho: 1689 if (!isop(pp->p_op = *amode++)) 1690 return (-1); 1691 pp->p_perm = 0; 1692 pp->p_special = 0; 1693 while ((t = isperm(pp, *amode)) != 0) { 1694 if (pp->p_special == 'X') { 1695 seen_X = 1; 1696 1697 if (pp->p_perm != 0) { 1698 ushort_t op; 1699 1700 /* 1701 * Remember the 'who' for the previous 1702 * transformation. 1703 */ 1704 pp->p_who = who; 1705 pp->p_special = 0; 1706 1707 op = pp->p_op; 1708 1709 /* Keep 'X' separate */ 1710 ++pp; 1711 pp->p_special = 'X'; 1712 pp->p_op = op; 1713 } 1714 } else if (seen_X) { 1715 ushort_t op; 1716 1717 /* Remember the 'who' for the X */ 1718 pp->p_who = who; 1719 1720 op = pp->p_op; 1721 1722 /* Keep 'X' separate */ 1723 ++pp; 1724 pp->p_perm = 0; 1725 pp->p_special = 0; 1726 pp->p_op = op; 1727 } 1728 ++amode; 1729 pp->p_perm |= t; 1730 } 1731 1732 /* 1733 * These returned 0, but were actually parsed, so 1734 * don't look at them again. 1735 */ 1736 switch (pp->p_special) { 1737 case 'u': 1738 case 'g': 1739 case 'o': 1740 ++amode; 1741 break; 1742 } 1743 pp->p_who = who; 1744 switch (*amode) { 1745 case '\0': 1746 break; 1747 1748 case ',': 1749 ++amode; 1750 ++pp; 1751 continue; 1752 1753 default: 1754 ++pp; 1755 goto samewho; 1756 } 1757 break; 1758 } 1759 endp = pp; 1760 return (NULL); 1761 } 1762 1763 /* 1764 * Given a character from the mode, return the associated 1765 * value as who (user designation) mask or 0 if this isn't valid. 1766 */ 1767 static int 1768 iswho(c) 1769 int c; 1770 { 1771 switch (c) { 1772 case 'a': 1773 return (P_A); 1774 1775 case 'u': 1776 return (P_U); 1777 1778 case 'g': 1779 return (P_G); 1780 1781 case 'o': 1782 return (P_O); 1783 1784 default: 1785 return (0); 1786 } 1787 /* NOTREACHED */ 1788 } 1789 1790 /* 1791 * Return non-zero if this is a valid op code 1792 * in a symbolic mode. 1793 */ 1794 static int 1795 isop(c) 1796 int c; 1797 { 1798 switch (c) { 1799 case '+': 1800 case '-': 1801 case '=': 1802 return (1); 1803 1804 default: 1805 return (0); 1806 } 1807 /* NOTREACHED */ 1808 } 1809 1810 /* 1811 * Return the permission bits implied by this character or 0 1812 * if it isn't valid. Also returns 0 when the pseudo-permissions 'u', 'g', or 1813 * 'o' are used, and sets pp->p_special to the one used. 1814 */ 1815 static int 1816 isperm(pp, c) 1817 PERMST *pp; 1818 int c; 1819 { 1820 switch (c) { 1821 case 'u': 1822 case 'g': 1823 case 'o': 1824 pp->p_special = c; 1825 return (0); 1826 1827 case 'r': 1828 return (S_IRUSR|S_IRGRP|S_IROTH); 1829 1830 case 'w': 1831 return (S_IWUSR|S_IWGRP|S_IWOTH); 1832 1833 case 'x': 1834 return (S_IXUSR|S_IXGRP|S_IXOTH); 1835 1836 #if S_ISVTX != 0 1837 case 't': 1838 return (S_ISVTX); 1839 #endif 1840 1841 case 'X': 1842 pp->p_special = 'X'; 1843 return (S_IXUSR|S_IXGRP|S_IXOTH); 1844 1845 #if S_ISVTX != 0 1846 case 'a': 1847 return (S_ISVTX); 1848 #endif 1849 1850 case 'h': 1851 return (S_ISUID); 1852 1853 /* 1854 * This change makes: 1855 * chmod +s file 1856 * set the system bit on dos but means that 1857 * chmod u+s file 1858 * chmod g+s file 1859 * chmod a+s file 1860 * are all like UNIX. 1861 */ 1862 case 's': 1863 return (nowho ? S_ISGID : S_ISGID|S_ISUID); 1864 1865 default: 1866 return (0); 1867 } 1868 /* NOTREACHED */ 1869 } 1870 1871 /* 1872 * Execute the automaton that is created by readmode() 1873 * to generate the final mode that will be used. This 1874 * code is passed a starting mode that is usually the original 1875 * mode of the file being changed (or 0). Note that this mode must contain 1876 * the file-type bits as well, so that S_ISDIR will succeed on directories. 1877 */ 1878 static mode_t 1879 getmode(mode_t startmode) 1880 { 1881 PERMST *pp; 1882 mode_t temp; 1883 mode_t perm; 1884 1885 for (pp = &machine[0]; pp <= endp; ++pp) { 1886 perm = (mode_t)0; 1887 /* 1888 * For the special modes 'u', 'g' and 'o', the named portion 1889 * of the mode refers to after the previous clause has been 1890 * processed, while the 'X' mode refers to the contents of the 1891 * mode before any clauses have been processed. 1892 * 1893 * References: P1003.2/D11.2, Section 4.7.7, 1894 * lines 2568-2570, 2578-2583 1895 */ 1896 switch (pp->p_special) { 1897 case 'u': 1898 temp = startmode & S_IRWXU; 1899 if (temp & (S_IRUSR|S_IRGRP|S_IROTH)) 1900 perm |= ((S_IRUSR|S_IRGRP|S_IROTH) & 1901 pp->p_who); 1902 if (temp & (S_IWUSR|S_IWGRP|S_IWOTH)) 1903 perm |= ((S_IWUSR|S_IWGRP|S_IWOTH) & pp->p_who); 1904 if (temp & (S_IXUSR|S_IXGRP|S_IXOTH)) 1905 perm |= ((S_IXUSR|S_IXGRP|S_IXOTH) & pp->p_who); 1906 break; 1907 1908 case 'g': 1909 temp = startmode & S_IRWXG; 1910 if (temp & (S_IRUSR|S_IRGRP|S_IROTH)) 1911 perm |= ((S_IRUSR|S_IRGRP|S_IROTH) & pp->p_who); 1912 if (temp & (S_IWUSR|S_IWGRP|S_IWOTH)) 1913 perm |= ((S_IWUSR|S_IWGRP|S_IWOTH) & pp->p_who); 1914 if (temp & (S_IXUSR|S_IXGRP|S_IXOTH)) 1915 perm |= ((S_IXUSR|S_IXGRP|S_IXOTH) & pp->p_who); 1916 break; 1917 1918 case 'o': 1919 temp = startmode & S_IRWXO; 1920 if (temp & (S_IRUSR|S_IRGRP|S_IROTH)) 1921 perm |= ((S_IRUSR|S_IRGRP|S_IROTH) & pp->p_who); 1922 if (temp & (S_IWUSR|S_IWGRP|S_IWOTH)) 1923 perm |= ((S_IWUSR|S_IWGRP|S_IWOTH) & pp->p_who); 1924 if (temp & (S_IXUSR|S_IXGRP|S_IXOTH)) 1925 perm |= ((S_IXUSR|S_IXGRP|S_IXOTH) & pp->p_who); 1926 break; 1927 1928 case 'X': 1929 perm = pp->p_perm; 1930 break; 1931 1932 default: 1933 perm = pp->p_perm; 1934 break; 1935 } 1936 switch (pp->p_op) { 1937 case '-': 1938 startmode &= ~(perm & pp->p_who); 1939 break; 1940 1941 case '=': 1942 startmode &= ~pp->p_who; 1943 /* FALLTHROUGH */ 1944 case '+': 1945 startmode |= (perm & pp->p_who); 1946 break; 1947 } 1948 } 1949 return (startmode); 1950 } 1951 1952 /* 1953 * Returns the last component of a path name, unless it is 1954 * an absolute path, in which case it returns the whole path 1955 */ 1956 static char 1957 *gettail(char *fname) 1958 { 1959 char *base = fname; 1960 1961 if (*fname != '/') { 1962 if ((base = strrchr(fname, '/')) != NULL) 1963 base++; 1964 else 1965 base = fname; 1966 } 1967 return (base); 1968 } 1969