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