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