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