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