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