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