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 return (0); 754 } else if (type == FTW_SLN && lflag == 0) { 755 (void) fprintf(stderr, 756 gettext("%s: cannot follow symbolic link %s: %s\n"), 757 cmdname, name, strerror(errno)); 758 error = 1; 759 return (0); 760 } else if (type == FTW_DL) { 761 (void) fprintf(stderr, gettext("%s: cycle detected for %s\n"), 762 cmdname, name); 763 error = 1; 764 return (0); 765 } 766 767 while (np) { 768 switch (np->action) { 769 case NOT: 770 not = !not; 771 np = np->next; 772 continue; 773 774 case AND: 775 np = np->next; 776 continue; 777 778 case OR: 779 if (np->first.np == np) { 780 /* 781 * handle naked OR (no term on left hand side) 782 */ 783 (void) fprintf(stderr, 784 gettext("%s: invalid -o construction\n"), 785 cmdname); 786 exit(2); 787 } 788 /* FALLTHROUGH */ 789 case LPAREN: { 790 struct Node *save = topnode; 791 topnode = np->first.np; 792 (void) execute(name, statb, type, state); 793 val = lastval; 794 topnode = save; 795 if (np->action == OR) { 796 if (val) 797 return (0); 798 val = 1; 799 } 800 break; 801 } 802 803 case LOCAL: { 804 int nremfs; 805 val = 1; 806 /* 807 * If file system type matches the remote 808 * file system type, then it is not local. 809 */ 810 for (nremfs = 0; nremfs < fstype_index; nremfs++) { 811 if (strcmp(remote_fstypes[nremfs], 812 statb->st_fstype) == 0) { 813 val = 0; 814 break; 815 } 816 } 817 break; 818 } 819 820 case TYPE: 821 l = (long)statb->st_mode&S_IFMT; 822 goto num; 823 824 case PERM: 825 l = (long)statb->st_mode&07777; 826 if (np->second.i == '-') 827 val = ((l&np->first.l) == np->first.l); 828 else 829 val = (l == np->first.l); 830 break; 831 832 case INUM: 833 ll = (long long)statb->st_ino; 834 goto llnum; 835 case NEWER: 836 l = statb->st_mtime; 837 goto num; 838 case ATIME: 839 t = statb->st_atime; 840 goto days; 841 case CTIME: 842 t = statb->st_ctime; 843 goto days; 844 case MTIME: 845 t = statb->st_mtime; 846 days: 847 l = (now-t)/A_DAY; 848 goto num; 849 case MMIN: 850 t = statb->st_mtime; 851 goto mins; 852 case AMIN: 853 t = statb->st_atime; 854 goto mins; 855 case CMIN: 856 t = statb->st_ctime; 857 goto mins; 858 mins: 859 l = (now-t)/A_MIN; 860 goto num; 861 case CSIZE: 862 ll = (long long)statb->st_size; 863 goto llnum; 864 case SIZE: 865 ll = (long long)round(statb->st_size, BLKSIZ)/BLKSIZ; 866 goto llnum; 867 case F_USER: 868 l = (long)statb->st_uid; 869 goto num; 870 case F_GROUP: 871 l = (long)statb->st_gid; 872 goto num; 873 case LINKS: 874 l = (long)statb->st_nlink; 875 goto num; 876 llnum: 877 if (np->second.i == '+') 878 val = (ll > np->first.ll); 879 else if (np->second.i == '-') 880 val = (ll < np->first.ll); 881 else 882 val = (ll == np->first.ll); 883 break; 884 num: 885 if (np->second.i == '+') 886 val = (l > np->first.l); 887 else if (np->second.i == '-') 888 val = (l < np->first.l); 889 else 890 val = (l == np->first.l); 891 break; 892 case OK: 893 val = ok(name, np->first.ap); 894 break; 895 case EXEC: 896 val = doexec(name, np->first.ap, NULL); 897 break; 898 899 case VARARGS: { 900 struct Arglist *ap = np->first.vp; 901 char *cp; 902 cp = ap->nextstr - (strlen(name)+1); 903 if (cp >= (char *)(ap->nextvar+3)) { 904 /* there is room just copy the name */ 905 val = 1; 906 (void) strcpy(cp, name); 907 *ap->nextvar++ = cp; 908 ap->nextstr = cp; 909 } else { 910 /* no more room, exec command */ 911 *ap->nextvar++ = name; 912 *ap->nextvar = 0; 913 val = 1; 914 (void) doexec((char *)0, ap->arglist, 915 &exec_exitcode); 916 ap->nextstr = ap->end; 917 ap->nextvar = ap->firstvar; 918 } 919 break; 920 } 921 922 case DEPTH: 923 case MOUNT: 924 case FOLLOW: 925 val = 1; 926 break; 927 928 case NAME: { 929 char *name1; 930 931 /* 932 * basename(3c) may modify name, so 933 * we need to pass another string 934 */ 935 if ((name1 = strdup(name)) == NULL) { 936 (void) fprintf(stderr, 937 gettext("%s: cannot strdup() %s: %s\n"), 938 cmdname, name, strerror(errno)); 939 exit(2); 940 } 941 /* 942 * XPG4 find should not treat a leading '.' in a 943 * filename specially for pattern matching. 944 * /usr/bin/find will not pattern match a leading 945 * '.' in a filename, unless '.' is explicitly 946 * specified. 947 */ 948 #ifdef XPG4 949 val = !fnmatch(np->first.cp, 950 basename(name1), 0); 951 #else 952 val = !fnmatch(np->first.cp, 953 basename(name1), FNM_PERIOD); 954 #endif 955 free(name1); 956 break; 957 } 958 959 case PRUNE: 960 if (type == FTW_D) 961 state->quit = FTW_PRUNE; 962 val = 1; 963 break; 964 case NOUSER: 965 val = ((getpwuid(statb->st_uid)) == 0); 966 break; 967 case NOGRP: 968 val = ((getgrgid(statb->st_gid)) == 0); 969 break; 970 case FSTYPE: 971 val = (strcmp(np->first.cp, statb->st_fstype) == 0); 972 break; 973 case CPIO: 974 output = (FILE *)np->first.l; 975 (void) fprintf(output, "%s\n", name); 976 val = 1; 977 break; 978 case PRINT: 979 (void) fprintf(stdout, "%s\n", name); 980 val = 1; 981 break; 982 case LS: 983 (void) list(name, statb); 984 val = 1; 985 break; 986 case XATTR: 987 filename = (walkflags & FTW_CHDIR) ? 988 gettail(name) : name; 989 val = (pathconf(filename, _PC_XATTR_EXISTS) == 1); 990 break; 991 case ACL: 992 /* 993 * Need to get the tail of the file name, since we have 994 * already chdir()ed into the directory (performed in 995 * nftw()) of the file 996 */ 997 filename = (walkflags & FTW_CHDIR) ? 998 gettail(name) : name; 999 val = acl_trivial(filename); 1000 break; 1001 } 1002 /* 1003 * evaluate 'val' and 'not' (exclusive-or) 1004 * if no inversion (not == 1), return only when val == 0 1005 * (primary not true). Otherwise, invert the primary 1006 * and return when the primary is true. 1007 * 'Lastval' saves the last result (fail or pass) when 1008 * returning back to the calling routine. 1009 */ 1010 if (val^not) { 1011 lastval = 0; 1012 return (0); 1013 } 1014 lastval = 1; 1015 not = 1; 1016 np = np->next; 1017 } 1018 return (0); 1019 } 1020 1021 /* 1022 * code for the -ok option 1023 */ 1024 1025 static int 1026 ok(name, argv) 1027 char *name; 1028 char *argv[]; 1029 { 1030 int c; 1031 int i = 0; 1032 char resp[LINE_MAX + 1]; 1033 1034 (void) fflush(stdout); /* to flush possible `-print' */ 1035 1036 if ((*argv != dummyarg) && (strcmp(*argv, name))) 1037 (void) fprintf(stderr, "< %s ... %s >? ", *argv, name); 1038 else 1039 (void) fprintf(stderr, "< {} ... %s >? ", name); 1040 1041 (void) fflush(stderr); 1042 1043 while ((c = getchar()) != '\n') { 1044 if (c == EOF) 1045 exit(2); 1046 if (i < LINE_MAX) 1047 resp[i++] = c; 1048 } 1049 resp[i] = '\0'; 1050 1051 if (yes_check(resp)) 1052 return (doexec(name, argv, NULL)); 1053 else 1054 return (0); 1055 } 1056 1057 /* 1058 * execute argv with {} replaced by name 1059 * 1060 * Per XPG6, find must exit non-zero if an invocation through 1061 * -exec, punctuated by a plus sign, exits non-zero, so set 1062 * exitcode if we see a non-zero exit. 1063 * exitcode should be NULL when -exec or -ok is not punctuated 1064 * by a plus sign. 1065 */ 1066 1067 static int 1068 doexec(char *name, char *argv[], int *exitcode) 1069 { 1070 char *cp; 1071 char **av = argv; 1072 char *newargs[1 + SHELL_MAXARGS + 1]; 1073 int dummyseen = 0; 1074 int i, j, status, rc, r = 0; 1075 int exit_status = 0; 1076 pid_t pid, pid1; 1077 1078 (void) fflush(stdout); /* to flush possible `-print' */ 1079 if (name) { 1080 while (cp = *av++) { 1081 if (cp == dummyarg) { 1082 dummyseen = 1; 1083 av[-1] = name; 1084 } 1085 1086 } 1087 } 1088 if (argv[0] == NULL) /* null command line */ 1089 return (r); 1090 1091 if ((pid = fork()) == -1) { 1092 /* fork failed */ 1093 if (exitcode != NULL) 1094 *exitcode = 1; 1095 return (0); 1096 } 1097 if (pid != 0) { 1098 /* parent */ 1099 do { 1100 /* wait for child to exit */ 1101 if ((rc = wait(&r)) == -1 && errno != EINTR) { 1102 (void) fprintf(stderr, 1103 gettext("wait failed %s"), strerror(errno)); 1104 1105 if (exitcode != NULL) 1106 *exitcode = 1; 1107 return (0); 1108 } 1109 } while (rc != pid); 1110 } else { 1111 /* child */ 1112 (void) execvp(argv[0], argv); 1113 if (errno != E2BIG) 1114 exit(1); 1115 1116 /* 1117 * We are in a situation where argv[0] points to a 1118 * script without the interpreter line, e.g. #!/bin/sh. 1119 * execvp() will execute either /usr/bin/sh or 1120 * /usr/xpg4/bin/sh against the script, and you will be 1121 * limited to SHELL_MAXARGS arguments. If you try to 1122 * pass more than SHELL_MAXARGS arguments, execvp() 1123 * fails with E2BIG. 1124 * See usr/src/lib/libc/port/gen/execvp.c. 1125 * 1126 * In this situation, process the argument list by 1127 * packets of SHELL_MAXARGS arguments with respect of 1128 * the following rules: 1129 * 1. the invocations have to complete before find exits 1130 * 2. only one invocation can be running at a time 1131 */ 1132 1133 i = 1; 1134 newargs[0] = argv[0]; 1135 1136 while (argv[i]) { 1137 j = 1; 1138 while (j <= SHELL_MAXARGS && argv[i]) { 1139 newargs[j++] = argv[i++]; 1140 } 1141 newargs[j] = NULL; 1142 1143 if ((pid1 = fork()) == -1) { 1144 /* fork failed */ 1145 exit(1); 1146 } 1147 if (pid1 == 0) { 1148 /* child */ 1149 (void) execvp(newargs[0], newargs); 1150 exit(1); 1151 } 1152 1153 status = 0; 1154 1155 do { 1156 /* wait for the child to exit */ 1157 if ((rc = wait(&status)) == -1 && 1158 errno != EINTR) { 1159 (void) fprintf(stderr, 1160 gettext("wait failed %s"), 1161 strerror(errno)); 1162 exit(1); 1163 } 1164 } while (rc != pid1); 1165 1166 if (status) 1167 exit_status = 1; 1168 } 1169 /* all the invocations have completed */ 1170 exit(exit_status); 1171 } 1172 1173 if (name && dummyseen) { 1174 for (av = argv; cp = *av++; ) { 1175 if (cp == name) 1176 av[-1] = dummyarg; 1177 } 1178 } 1179 1180 if (r && exitcode != NULL) 1181 *exitcode = 3; /* use to indicate error in cmd invocation */ 1182 1183 return (!r); 1184 } 1185 1186 1187 /* 1188 * Table lookup routine 1189 */ 1190 static struct Args * 1191 lookup(word) 1192 char *word; 1193 { 1194 struct Args *argp = commands; 1195 int second; 1196 if (word == 0 || *word == 0) 1197 return (0); 1198 second = word[1]; 1199 while (*argp->name) { 1200 if (second == argp->name[1] && strcmp(word, argp->name) == 0) 1201 return (argp); 1202 argp++; 1203 } 1204 return (0); 1205 } 1206 1207 1208 /* 1209 * Get space for variable length argument list 1210 */ 1211 1212 static struct Arglist * 1213 varargs(com) 1214 char **com; 1215 { 1216 struct Arglist *ap; 1217 int n; 1218 char **ep; 1219 if (varsize == 0) { 1220 n = 2*sizeof (char **); 1221 for (ep = environ; *ep; ep++) 1222 n += (strlen(*ep)+sizeof (ep) + 1); 1223 varsize = sizeof (struct Arglist)+ARG_MAX-PATH_MAX-n-1; 1224 } 1225 ap = (struct Arglist *)malloc(varsize+1); 1226 ap->end = (char *)ap + varsize; 1227 ap->nextstr = ap->end; 1228 ap->nextvar = ap->arglist; 1229 while (*ap->nextvar++ = *com++); 1230 ap->nextvar--; 1231 ap->firstvar = ap->nextvar; 1232 ap->next = lastlist; 1233 lastlist = ap; 1234 return (ap); 1235 } 1236 1237 /* 1238 * filter command support 1239 * fork and exec cmd(argv) according to mode: 1240 * 1241 * "r" with fp as stdin of cmd (default stdin), cmd stdout returned 1242 * "w" with fp as stdout of cmd (default stdout), cmd stdin returned 1243 */ 1244 1245 #define CMDERR ((1<<8)-1) /* command error exit code */ 1246 #define MAXCMDS 8 /* max # simultaneous cmdopen()'s */ 1247 1248 static struct /* info for each cmdopen() */ 1249 { 1250 FILE *fp; /* returned by cmdopen() */ 1251 pid_t pid; /* pid used by cmdopen() */ 1252 } cmdproc[MAXCMDS]; 1253 1254 static FILE * 1255 cmdopen(cmd, argv, mode, fp) 1256 char *cmd; 1257 char **argv; 1258 char *mode; 1259 FILE *fp; 1260 { 1261 int proc; 1262 int cmdfd; 1263 int usrfd; 1264 int pio[2]; 1265 1266 switch (*mode) { 1267 case 'r': 1268 cmdfd = 1; 1269 usrfd = 0; 1270 break; 1271 case 'w': 1272 cmdfd = 0; 1273 usrfd = 1; 1274 break; 1275 default: 1276 return (0); 1277 } 1278 1279 for (proc = 0; proc < MAXCMDS; proc++) 1280 if (!cmdproc[proc].fp) 1281 break; 1282 if (proc >= MAXCMDS) 1283 return (0); 1284 1285 if (pipe(pio)) 1286 return (0); 1287 1288 switch (cmdproc[proc].pid = fork()) { 1289 case -1: 1290 return (0); 1291 case 0: 1292 if (fp && fileno(fp) != usrfd) { 1293 (void) close(usrfd); 1294 if (dup2(fileno(fp), usrfd) != usrfd) 1295 _exit(CMDERR); 1296 (void) close(fileno(fp)); 1297 } 1298 (void) close(cmdfd); 1299 if (dup2(pio[cmdfd], cmdfd) != cmdfd) 1300 _exit(CMDERR); 1301 (void) close(pio[cmdfd]); 1302 (void) close(pio[usrfd]); 1303 (void) execvp(cmd, argv); 1304 if (errno == ENOEXEC) { 1305 char **p; 1306 char **v; 1307 1308 /* 1309 * assume cmd is a shell script 1310 */ 1311 1312 p = argv; 1313 while (*p++); 1314 if (v = (char **)malloc((p - argv + 1) * 1315 sizeof (char **))) { 1316 p = v; 1317 *p++ = cmd; 1318 if (*argv) argv++; 1319 while (*p++ = *argv++); 1320 (void) execv(getshell(), v); 1321 } 1322 } 1323 _exit(CMDERR); 1324 /*NOTREACHED*/ 1325 default: 1326 (void) close(pio[cmdfd]); 1327 return (cmdproc[proc].fp = fdopen(pio[usrfd], mode)); 1328 } 1329 } 1330 1331 /* 1332 * close a stream opened by cmdopen() 1333 * -1 returned if cmdopen() had a problem 1334 * otherwise exit() status of command is returned 1335 */ 1336 1337 static int 1338 cmdclose(fp) 1339 FILE *fp; 1340 { 1341 int i; 1342 pid_t p, pid; 1343 int status; 1344 1345 for (i = 0; i < MAXCMDS; i++) 1346 if (fp == cmdproc[i].fp) break; 1347 if (i >= MAXCMDS) 1348 return (-1); 1349 (void) fclose(fp); 1350 cmdproc[i].fp = 0; 1351 pid = cmdproc[i].pid; 1352 while ((p = wait(&status)) != pid && p != (pid_t)-1); 1353 if (p == pid) { 1354 status = (status >> 8) & CMDERR; 1355 if (status == CMDERR) 1356 status = -1; 1357 } 1358 else 1359 status = -1; 1360 return (status); 1361 } 1362 1363 /* 1364 * return pointer to the full path name of the shell 1365 * 1366 * SHELL is read from the environment and must start with / 1367 * 1368 * if set-uid or set-gid then the executable and its containing 1369 * directory must not be writable by the real user 1370 * 1371 * /usr/bin/sh is returned by default 1372 */ 1373 1374 char * 1375 getshell() 1376 { 1377 char *s; 1378 char *sh; 1379 uid_t u; 1380 int j; 1381 1382 if (((sh = getenv("SHELL")) != 0) && *sh == '/') { 1383 if (u = getuid()) { 1384 if ((u != geteuid() || getgid() != getegid()) && 1385 access(sh, 2) == 0) 1386 goto defshell; 1387 s = strrchr(sh, '/'); 1388 *s = 0; 1389 j = access(sh, 2); 1390 *s = '/'; 1391 if (!j) goto defshell; 1392 } 1393 return (sh); 1394 } 1395 defshell: 1396 return ("/usr/bin/sh"); 1397 } 1398 1399 /* 1400 * the following functions implement the added "-ls" option 1401 */ 1402 1403 #include <utmpx.h> 1404 #include <sys/mkdev.h> 1405 1406 struct utmpx utmpx; 1407 #define NMAX (sizeof (utmpx.ut_name)) 1408 #define SCPYN(a, b) (void) strncpy(a, b, NMAX) 1409 1410 #define NUID 64 1411 #define NGID 64 1412 1413 static struct ncache { 1414 int id; 1415 char name[NMAX+1]; 1416 } nc[NUID], gc[NGID]; 1417 1418 /* 1419 * This function assumes that the password file is hashed 1420 * (or some such) to allow fast access based on a name key. 1421 */ 1422 static char * 1423 getname(uid_t uid) 1424 { 1425 struct passwd *pw; 1426 int cp; 1427 1428 #if (((NUID) & ((NUID) - 1)) != 0) 1429 cp = uid % (NUID); 1430 #else 1431 cp = uid & ((NUID) - 1); 1432 #endif 1433 if (nc[cp].id == uid && nc[cp].name[0]) 1434 return (nc[cp].name); 1435 pw = getpwuid(uid); 1436 if (!pw) 1437 return (0); 1438 nc[cp].id = uid; 1439 SCPYN(nc[cp].name, pw->pw_name); 1440 return (nc[cp].name); 1441 } 1442 1443 /* 1444 * This function assumes that the group file is hashed 1445 * (or some such) to allow fast access based on a name key. 1446 */ 1447 static char * 1448 getgroup(gid_t gid) 1449 { 1450 struct group *gr; 1451 int cp; 1452 1453 #if (((NGID) & ((NGID) - 1)) != 0) 1454 cp = gid % (NGID); 1455 #else 1456 cp = gid & ((NGID) - 1); 1457 #endif 1458 if (gc[cp].id == gid && gc[cp].name[0]) 1459 return (gc[cp].name); 1460 gr = getgrgid(gid); 1461 if (!gr) 1462 return (0); 1463 gc[cp].id = gid; 1464 SCPYN(gc[cp].name, gr->gr_name); 1465 return (gc[cp].name); 1466 } 1467 1468 #define permoffset(who) ((who) * 3) 1469 #define permission(who, type) ((type) >> permoffset(who)) 1470 #define kbytes(bytes) (((bytes) + 1023) / 1024) 1471 1472 static int 1473 list(file, stp) 1474 char *file; 1475 struct stat *stp; 1476 { 1477 char pmode[32], uname[32], gname[32], fsize[32], ftime[32]; 1478 int trivial; 1479 1480 /* 1481 * Each line below contains the relevant permission (column 1) and character 1482 * shown when the corresponding execute bit is either clear (column 2) 1483 * or set (column 3) 1484 * These permissions are as shown by ls(1b) 1485 */ 1486 static long special[] = { S_ISUID, 'S', 's', 1487 S_ISGID, 'S', 's', 1488 S_ISVTX, 'T', 't' }; 1489 1490 static time_t sixmonthsago = -1; 1491 #ifdef S_IFLNK 1492 char flink[MAXPATHLEN + 1]; 1493 #endif 1494 int who; 1495 char *cp; 1496 char *tailname; 1497 time_t now; 1498 long long ksize; 1499 1500 if (file == NULL || stp == NULL) 1501 return (-1); 1502 1503 (void) time(&now); 1504 if (sixmonthsago == -1) 1505 sixmonthsago = now - 6L*30L*24L*60L*60L; 1506 1507 switch (stp->st_mode & S_IFMT) { 1508 #ifdef S_IFDIR 1509 case S_IFDIR: /* directory */ 1510 pmode[0] = 'd'; 1511 break; 1512 #endif 1513 #ifdef S_IFCHR 1514 case S_IFCHR: /* character special */ 1515 pmode[0] = 'c'; 1516 break; 1517 #endif 1518 #ifdef S_IFBLK 1519 case S_IFBLK: /* block special */ 1520 pmode[0] = 'b'; 1521 break; 1522 #endif 1523 #ifdef S_IFIFO 1524 case S_IFIFO: /* fifo special */ 1525 pmode[0] = 'p'; 1526 break; 1527 #endif 1528 #ifdef S_IFLNK 1529 case S_IFLNK: /* symbolic link */ 1530 pmode[0] = 'l'; 1531 break; 1532 #endif 1533 #ifdef S_IFSOCK 1534 case S_IFSOCK: /* socket */ 1535 pmode[0] = 's'; 1536 break; 1537 #endif 1538 #ifdef S_IFDOOR 1539 case S_IFDOOR: /* door */ 1540 pmode[0] = 'D'; 1541 break; 1542 #endif 1543 #ifdef S_IFREG 1544 case S_IFREG: /* regular */ 1545 pmode[0] = '-'; 1546 break; 1547 #endif 1548 default: 1549 pmode[0] = '?'; 1550 break; 1551 } 1552 1553 for (who = 0; who < 3; who++) { 1554 int is_exec = stp->st_mode & permission(who, S_IEXEC)? 1 : 0; 1555 1556 if (stp->st_mode & permission(who, S_IREAD)) 1557 pmode[permoffset(who) + 1] = 'r'; 1558 else 1559 pmode[permoffset(who) + 1] = '-'; 1560 1561 if (stp->st_mode & permission(who, S_IWRITE)) 1562 pmode[permoffset(who) + 2] = 'w'; 1563 else 1564 pmode[permoffset(who) + 2] = '-'; 1565 1566 if (stp->st_mode & special[who * 3]) 1567 pmode[permoffset(who) + 3] = 1568 special[who * 3 + 1 + is_exec]; 1569 else if (is_exec) 1570 pmode[permoffset(who) + 3] = 'x'; 1571 else 1572 pmode[permoffset(who) + 3] = '-'; 1573 } 1574 1575 /* 1576 * Need to get the tail of the file name, since we have 1577 * already chdir()ed into the directory of the file 1578 */ 1579 1580 tailname = gettail(file); 1581 1582 trivial = acl_trivial(tailname); 1583 if (trivial == -1) 1584 trivial = 0; 1585 1586 if (trivial == 1) 1587 pmode[permoffset(who) + 1] = '+'; 1588 else 1589 pmode[permoffset(who) + 1] = ' '; 1590 1591 pmode[permoffset(who) + 2] = '\0'; 1592 1593 /* 1594 * Prepare uname and gname. Always add a space afterwards 1595 * to keep columns from running together. 1596 */ 1597 cp = getname(stp->st_uid); 1598 if (cp != NULL) 1599 (void) sprintf(uname, "%-8s ", cp); 1600 else 1601 (void) sprintf(uname, "%-8u ", stp->st_uid); 1602 1603 cp = getgroup(stp->st_gid); 1604 if (cp != NULL) 1605 (void) sprintf(gname, "%-8s ", cp); 1606 else 1607 (void) sprintf(gname, "%-8u ", stp->st_gid); 1608 1609 if (pmode[0] == 'b' || pmode[0] == 'c') 1610 (void) sprintf(fsize, "%3ld,%4ld", 1611 major(stp->st_rdev), minor(stp->st_rdev)); 1612 else { 1613 (void) sprintf(fsize, (stp->st_size < 100000000) ? 1614 "%8lld" : "%lld", stp->st_size); 1615 #ifdef S_IFLNK 1616 if (pmode[0] == 'l') { 1617 1618 1619 who = readlink(tailname, flink, sizeof (flink) - 1); 1620 1621 if (who >= 0) 1622 flink[who] = '\0'; 1623 else 1624 flink[0] = '\0'; 1625 } 1626 #endif 1627 } 1628 1629 cp = ctime(&stp->st_mtime); 1630 if (stp->st_mtime < sixmonthsago || stp->st_mtime > now) 1631 (void) sprintf(ftime, "%-7.7s %-4.4s", cp + 4, cp + 20); 1632 else 1633 (void) sprintf(ftime, "%-12.12s", cp + 4); 1634 1635 (void) printf((stp->st_ino < 100000) ? "%5llu " : 1636 "%llu ", stp->st_ino); /* inode # */ 1637 #ifdef S_IFSOCK 1638 ksize = (long long) kbytes(ldbtob(stp->st_blocks)); /* kbytes */ 1639 #else 1640 ksize = (long long) kbytes(stp->st_size); /* kbytes */ 1641 #endif 1642 (void) printf((ksize < 10000) ? "%4lld " : "%lld ", ksize); 1643 (void) printf("%s %2ld %s%s%s %s %s%s%s\n", 1644 pmode, /* protection */ 1645 stp->st_nlink, /* # of links */ 1646 uname, /* owner */ 1647 gname, /* group */ 1648 fsize, /* # of bytes */ 1649 ftime, /* modify time */ 1650 file, /* name */ 1651 #ifdef S_IFLNK 1652 (pmode[0] == 'l') ? " -> " : "", 1653 (pmode[0] == 'l') ? flink : "" /* symlink */ 1654 #else 1655 "", 1656 "" 1657 #endif 1658 ); 1659 1660 return (0); 1661 } 1662 1663 static char * 1664 new_string(char *s) 1665 { 1666 char *p = strdup(s); 1667 1668 if (p) 1669 return (p); 1670 (void) fprintf(stderr, gettext("%s: out of memory\n"), cmdname); 1671 exit(1); 1672 /*NOTREACHED*/ 1673 } 1674 1675 /* 1676 * Read remote file system types from REMOTE_FS into the 1677 * remote_fstypes array. 1678 */ 1679 static void 1680 init_remote_fs() 1681 { 1682 FILE *fp; 1683 char line_buf[LINEBUF_SIZE]; 1684 1685 if ((fp = fopen(REMOTE_FS, "r")) == NULL) { 1686 (void) fprintf(stderr, 1687 gettext("%s: Warning: can't open %s, ignored\n"), 1688 REMOTE_FS, cmdname); 1689 /* Use default string name for NFS */ 1690 remote_fstypes[fstype_index++] = "nfs"; 1691 return; 1692 } 1693 1694 while (fgets(line_buf, sizeof (line_buf), fp) != NULL) { 1695 char buf[LINEBUF_SIZE]; 1696 1697 /* LINTED - unbounded string specifier */ 1698 (void) sscanf(line_buf, "%s", buf); 1699 remote_fstypes[fstype_index++] = new_string(buf); 1700 1701 if (fstype_index == N_FSTYPES) 1702 break; 1703 } 1704 (void) fclose(fp); 1705 } 1706 1707 #define NPERM 30 /* Largest machine */ 1708 1709 /* 1710 * The PERM struct is the machine that builds permissions. The p_special 1711 * field contains what permissions need to be checked at run-time in 1712 * getmode(). This is one of 'X', 'u', 'g', or 'o'. It contains '\0' to 1713 * indicate normal processing. 1714 */ 1715 typedef struct PERMST { 1716 ushort_t p_who; /* Range of permission (e.g. ugo) */ 1717 ushort_t p_perm; /* Bits to turn on, off, assign */ 1718 uchar_t p_op; /* Operation: + - = */ 1719 uchar_t p_special; /* Special handling? */ 1720 } PERMST; 1721 1722 #ifndef S_ISVTX 1723 #define S_ISVTX 0 /* Not .1 */ 1724 #endif 1725 1726 /* Mask values */ 1727 #define P_A (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO) /* allbits */ 1728 #define P_U (S_ISUID|S_ISVTX|S_IRWXU) /* user */ 1729 #define P_G (S_ISGID|S_ISVTX|S_IRWXG) /* group */ 1730 #define P_O (S_ISVTX|S_IRWXO) /* other */ 1731 1732 static int iswho(int c); 1733 static int isop(int c); 1734 static int isperm(PERMST *pp, int c); 1735 1736 static PERMST machine[NPERM]; /* Permission construction machine */ 1737 static PERMST *endp; /* Last used PERM structure */ 1738 1739 static uint_t nowho; /* No who for this mode (DOS kludge) */ 1740 1741 /* 1742 * Read an ASCII string containing the symbolic/octal mode and 1743 * compile an automaton that recognizes it. The return value 1744 * is NULL if everything is OK, otherwise it is -1. 1745 */ 1746 static int 1747 readmode(ascmode) 1748 const char *ascmode; 1749 { 1750 const char *amode = ascmode; 1751 PERMST *pp; 1752 int seen_X; 1753 1754 nowho = 0; 1755 seen_X = 0; 1756 pp = &machine[0]; 1757 if (*amode >= '0' && *amode <= '7') { 1758 int mode; 1759 1760 mode = 0; 1761 while (*amode >= '0' && *amode <= '7') 1762 mode = (mode<<3) + *amode++ - '0'; 1763 if (*amode != '\0') 1764 return (-1); 1765 #if S_ISUID != 04000 || S_ISGID != 02000 || \ 1766 S_IRUSR != 0400 || S_IWUSR != 0200 || S_IXUSR != 0100 || \ 1767 S_IRGRP != 0040 || S_IWGRP != 0020 || S_IXGRP != 0010 || \ 1768 S_IROTH != 0004 || S_IWOTH != 0002 || S_IXOTH != 0001 1769 /* 1770 * There is no requirement of the octal mode bits being 1771 * the same as the S_ macros. 1772 */ 1773 { 1774 mode_t mapping[] = { 1775 S_IXOTH, S_IWOTH, S_IROTH, 1776 S_IXGRP, S_IWGRP, S_IRGRP, 1777 S_IXUSR, S_IWUSR, S_IRUSR, 1778 S_ISGID, S_ISUID, 1779 0 1780 }; 1781 int i, newmode = 0; 1782 1783 for (i = 0; mapping[i] != 0; i++) 1784 if (mode & (1<<i)) 1785 newmode |= mapping[i]; 1786 mode = newmode; 1787 } 1788 #endif 1789 pp->p_who = P_A; 1790 pp->p_perm = mode; 1791 pp->p_op = '='; 1792 } else for (;;) { 1793 int t; 1794 int who = 0; 1795 1796 while ((t = iswho(*amode)) != 0) { 1797 ++amode; 1798 who |= t; 1799 } 1800 if (who == 0) { 1801 mode_t currmask; 1802 (void) umask(currmask = umask((mode_t)0)); 1803 1804 /* 1805 * If no who specified, must use contents of 1806 * umask to determine which bits to flip. This 1807 * is POSIX/V7/BSD behaviour, but not SVID. 1808 */ 1809 who = (~currmask)&P_A; 1810 ++nowho; 1811 } else 1812 nowho = 0; 1813 samewho: 1814 if (!isop(pp->p_op = *amode++)) 1815 return (-1); 1816 pp->p_perm = 0; 1817 pp->p_special = 0; 1818 while ((t = isperm(pp, *amode)) != 0) { 1819 if (pp->p_special == 'X') { 1820 seen_X = 1; 1821 1822 if (pp->p_perm != 0) { 1823 ushort_t op; 1824 1825 /* 1826 * Remember the 'who' for the previous 1827 * transformation. 1828 */ 1829 pp->p_who = who; 1830 pp->p_special = 0; 1831 1832 op = pp->p_op; 1833 1834 /* Keep 'X' separate */ 1835 ++pp; 1836 pp->p_special = 'X'; 1837 pp->p_op = op; 1838 } 1839 } else if (seen_X) { 1840 ushort_t op; 1841 1842 /* Remember the 'who' for the X */ 1843 pp->p_who = who; 1844 1845 op = pp->p_op; 1846 1847 /* Keep 'X' separate */ 1848 ++pp; 1849 pp->p_perm = 0; 1850 pp->p_special = 0; 1851 pp->p_op = op; 1852 } 1853 ++amode; 1854 pp->p_perm |= t; 1855 } 1856 1857 /* 1858 * These returned 0, but were actually parsed, so 1859 * don't look at them again. 1860 */ 1861 switch (pp->p_special) { 1862 case 'u': 1863 case 'g': 1864 case 'o': 1865 ++amode; 1866 break; 1867 } 1868 pp->p_who = who; 1869 switch (*amode) { 1870 case '\0': 1871 break; 1872 1873 case ',': 1874 ++amode; 1875 ++pp; 1876 continue; 1877 1878 default: 1879 ++pp; 1880 goto samewho; 1881 } 1882 break; 1883 } 1884 endp = pp; 1885 return (NULL); 1886 } 1887 1888 /* 1889 * Given a character from the mode, return the associated 1890 * value as who (user designation) mask or 0 if this isn't valid. 1891 */ 1892 static int 1893 iswho(c) 1894 int c; 1895 { 1896 switch (c) { 1897 case 'a': 1898 return (P_A); 1899 1900 case 'u': 1901 return (P_U); 1902 1903 case 'g': 1904 return (P_G); 1905 1906 case 'o': 1907 return (P_O); 1908 1909 default: 1910 return (0); 1911 } 1912 /* NOTREACHED */ 1913 } 1914 1915 /* 1916 * Return non-zero if this is a valid op code 1917 * in a symbolic mode. 1918 */ 1919 static int 1920 isop(c) 1921 int c; 1922 { 1923 switch (c) { 1924 case '+': 1925 case '-': 1926 case '=': 1927 return (1); 1928 1929 default: 1930 return (0); 1931 } 1932 /* NOTREACHED */ 1933 } 1934 1935 /* 1936 * Return the permission bits implied by this character or 0 1937 * if it isn't valid. Also returns 0 when the pseudo-permissions 'u', 'g', or 1938 * 'o' are used, and sets pp->p_special to the one used. 1939 */ 1940 static int 1941 isperm(pp, c) 1942 PERMST *pp; 1943 int c; 1944 { 1945 switch (c) { 1946 case 'u': 1947 case 'g': 1948 case 'o': 1949 pp->p_special = c; 1950 return (0); 1951 1952 case 'r': 1953 return (S_IRUSR|S_IRGRP|S_IROTH); 1954 1955 case 'w': 1956 return (S_IWUSR|S_IWGRP|S_IWOTH); 1957 1958 case 'x': 1959 return (S_IXUSR|S_IXGRP|S_IXOTH); 1960 1961 #if S_ISVTX != 0 1962 case 't': 1963 return (S_ISVTX); 1964 #endif 1965 1966 case 'X': 1967 pp->p_special = 'X'; 1968 return (S_IXUSR|S_IXGRP|S_IXOTH); 1969 1970 #if S_ISVTX != 0 1971 case 'a': 1972 return (S_ISVTX); 1973 #endif 1974 1975 case 'h': 1976 return (S_ISUID); 1977 1978 /* 1979 * This change makes: 1980 * chmod +s file 1981 * set the system bit on dos but means that 1982 * chmod u+s file 1983 * chmod g+s file 1984 * chmod a+s file 1985 * are all like UNIX. 1986 */ 1987 case 's': 1988 return (nowho ? S_ISGID : S_ISGID|S_ISUID); 1989 1990 default: 1991 return (0); 1992 } 1993 /* NOTREACHED */ 1994 } 1995 1996 /* 1997 * Execute the automaton that is created by readmode() 1998 * to generate the final mode that will be used. This 1999 * code is passed a starting mode that is usually the original 2000 * mode of the file being changed (or 0). Note that this mode must contain 2001 * the file-type bits as well, so that S_ISDIR will succeed on directories. 2002 */ 2003 static mode_t 2004 getmode(mode_t startmode) 2005 { 2006 PERMST *pp; 2007 mode_t temp; 2008 mode_t perm; 2009 2010 for (pp = &machine[0]; pp <= endp; ++pp) { 2011 perm = (mode_t)0; 2012 /* 2013 * For the special modes 'u', 'g' and 'o', the named portion 2014 * of the mode refers to after the previous clause has been 2015 * processed, while the 'X' mode refers to the contents of the 2016 * mode before any clauses have been processed. 2017 * 2018 * References: P1003.2/D11.2, Section 4.7.7, 2019 * lines 2568-2570, 2578-2583 2020 */ 2021 switch (pp->p_special) { 2022 case 'u': 2023 temp = startmode & S_IRWXU; 2024 if (temp & (S_IRUSR|S_IRGRP|S_IROTH)) 2025 perm |= ((S_IRUSR|S_IRGRP|S_IROTH) & 2026 pp->p_who); 2027 if (temp & (S_IWUSR|S_IWGRP|S_IWOTH)) 2028 perm |= ((S_IWUSR|S_IWGRP|S_IWOTH) & pp->p_who); 2029 if (temp & (S_IXUSR|S_IXGRP|S_IXOTH)) 2030 perm |= ((S_IXUSR|S_IXGRP|S_IXOTH) & pp->p_who); 2031 break; 2032 2033 case 'g': 2034 temp = startmode & S_IRWXG; 2035 if (temp & (S_IRUSR|S_IRGRP|S_IROTH)) 2036 perm |= ((S_IRUSR|S_IRGRP|S_IROTH) & pp->p_who); 2037 if (temp & (S_IWUSR|S_IWGRP|S_IWOTH)) 2038 perm |= ((S_IWUSR|S_IWGRP|S_IWOTH) & pp->p_who); 2039 if (temp & (S_IXUSR|S_IXGRP|S_IXOTH)) 2040 perm |= ((S_IXUSR|S_IXGRP|S_IXOTH) & pp->p_who); 2041 break; 2042 2043 case 'o': 2044 temp = startmode & S_IRWXO; 2045 if (temp & (S_IRUSR|S_IRGRP|S_IROTH)) 2046 perm |= ((S_IRUSR|S_IRGRP|S_IROTH) & pp->p_who); 2047 if (temp & (S_IWUSR|S_IWGRP|S_IWOTH)) 2048 perm |= ((S_IWUSR|S_IWGRP|S_IWOTH) & pp->p_who); 2049 if (temp & (S_IXUSR|S_IXGRP|S_IXOTH)) 2050 perm |= ((S_IXUSR|S_IXGRP|S_IXOTH) & pp->p_who); 2051 break; 2052 2053 case 'X': 2054 perm = pp->p_perm; 2055 break; 2056 2057 default: 2058 perm = pp->p_perm; 2059 break; 2060 } 2061 switch (pp->p_op) { 2062 case '-': 2063 startmode &= ~(perm & pp->p_who); 2064 break; 2065 2066 case '=': 2067 startmode &= ~pp->p_who; 2068 /* FALLTHROUGH */ 2069 case '+': 2070 startmode |= (perm & pp->p_who); 2071 break; 2072 } 2073 } 2074 return (startmode); 2075 } 2076 2077 /* 2078 * Returns the last component of a path name, unless it is 2079 * an absolute path, in which case it returns the whole path 2080 */ 2081 static char 2082 *gettail(char *fname) 2083 { 2084 char *base = fname; 2085 2086 if (*fname != '/') { 2087 if ((base = strrchr(fname, '/')) != NULL) 2088 base++; 2089 else 2090 base = fname; 2091 } 2092 return (base); 2093 } 2094