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