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