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