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