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