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