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