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