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