(root)/
findutils-4.9.0/
lib/
buildcmd.c
       1  /* buildcmd.c -- build command lines from a list of arguments.
       2     Copyright (C) 1990-2022 Free Software Foundation, Inc.
       3  
       4     This program is free software: you can redistribute it and/or modify
       5     it under the terms of the GNU General Public License as published by
       6     the Free Software Foundation, either version 3 of the License, or
       7     (at your option) any later version.
       8  
       9     This program is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12     GNU General Public License for more details.
      13  
      14     You should have received a copy of the GNU General Public License
      15     along with this program.  If not, see <https://www.gnu.org/licenses/>.
      16  */
      17  /* config.h must be included first. */
      18  #include <config.h>
      19  
      20  /* system headers. */
      21  #include <assert.h>
      22  #include <errno.h>
      23  #include <limits.h>
      24  #include <stdbool.h>
      25  #include <stdlib.h>
      26  #include <string.h>
      27  #ifndef _POSIX_SOURCE
      28  # include <sys/param.h>
      29  #endif
      30  #include <unistd.h>
      31  #include <wchar.h>
      32  #include <xalloc.h>
      33  
      34  /* gnulib headers. */
      35  #include "xstrtol.h"
      36  
      37  /* find headers. */
      38  #include "system.h"
      39  #include "buildcmd.h"
      40  #include "die.h"
      41  
      42  
      43  /* COMPAT:  SYSV version defaults size (and has a max value of) to 470.
      44     We try to make it as large as possible.  See bc_get_arg_max() below. */
      45  #if defined NCARGS && !defined ARG_MAX
      46  /* We include sys/param.h in order to detect this case. */
      47  # error "You have an unusual system.  Once you remove this error message from buildcmd.c, it should work, but please make sure that DejaGnu is installed on your system and that 'make check' passes before using the findutils programs.  Please mail bug-findutils@gnu.org to tell us about your system."
      48  # define ARG_MAX NCARGS
      49  #endif
      50  
      51  
      52  static const char *special_terminating_arg = "do_not_care";
      53  
      54  
      55  
      56  /* Add a terminator to the argument list. */
      57  static void
      58  bc_args_complete (struct buildcmd_control *ctl,
      59  		  struct buildcmd_state *state)
      60  {
      61    bc_push_arg (ctl, state, special_terminating_arg, 0, NULL, 0, 0);
      62  }
      63  
      64  
      65  /* Replace all instances of `replace_pat' in ARG with `linebuf',
      66     and add the resulting string to the list of arguments for the command
      67     to execute.
      68     ARGLEN is the length of ARG, not including the null.
      69     LBLEN is the length of LINEBUF, not including the null.
      70     PFXLEN is the length of PREFIX.  Substitution is not performed on
      71     the prefix.   The prefix is used if the argument contains replace_pat.
      72  
      73     COMPAT: insertions on the SYSV version are limited to 255 chars per line,
      74     and a max of 5 occurrences of replace_pat in the initial-arguments.
      75     Those restrictions do not exist here.  */
      76  
      77  void
      78  bc_do_insert (struct buildcmd_control *ctl,
      79                struct buildcmd_state *state,
      80                char *arg, size_t arglen,
      81                const char *prefix, size_t pfxlen,
      82                const char *linebuf, size_t lblen,
      83                int initial_args)
      84  {
      85    /* Temporary copy of each arg with the replace pattern replaced by the
      86       real arg.  */
      87    static char *insertbuf;
      88    char *p;
      89    size_t bytes_left = ctl->arg_max - 1;    /* Bytes left on the command line.  */
      90  
      91    /* XXX: on systems lacking an upper limit for exec args, ctl->arg_max
      92     *      may have been set to LONG_MAX (see bc_get_arg_max()).  Hence
      93     *      this xmalloc call may be a bad idea, especially since we are
      94     *      adding 1 to it...
      95     */
      96    if (!insertbuf)
      97      insertbuf = xmalloc (ctl->arg_max + 1);
      98    p = insertbuf;
      99  
     100    do
     101      {
     102        size_t len;               /* Length in ARG before `replace_pat'.  */
     103        char *s = mbsstr (arg, ctl->replace_pat);
     104        if (s)
     105          {
     106            len = s - arg;
     107          }
     108        else
     109          {
     110            len = arglen;
     111          }
     112  
     113        if (bytes_left <= len)
     114          break;
     115        else
     116  	bytes_left -= len;
     117  
     118        strncpy (p, arg, len);
     119        p += len;
     120        arg += len;
     121        arglen -= len;
     122  
     123        if (s)
     124          {
     125  	  if (bytes_left <= (lblen + pfxlen))
     126  	    break;
     127  	  else
     128  	    bytes_left -= (lblen + pfxlen);
     129  
     130  	  if (prefix)
     131  	    {
     132  	      strcpy (p, prefix);
     133  	      p += pfxlen;
     134  	    }
     135            strcpy (p, linebuf);
     136            p += lblen;
     137  
     138            arg += ctl->rplen;
     139            arglen -= ctl->rplen;
     140          }
     141      }
     142    while (*arg);
     143    if (*arg)
     144      die (EXIT_FAILURE, 0, _("command too long"));
     145    *p++ = '\0';
     146  
     147    bc_push_arg (ctl, state,
     148  	       insertbuf, p - insertbuf,
     149                 NULL, 0,
     150                 initial_args);
     151  }
     152  
     153  
     154  /* Update our best guess as to how many arguments we should pass to the next
     155   * invocation of the command.
     156   */
     157  static size_t
     158  update_limit (struct buildcmd_control *ctl,
     159  	      struct buildcmd_state *state,
     160  	      bool success,
     161  	      size_t limit)
     162  {
     163    if (success)
     164      {
     165        if (limit > state->largest_successful_arg_count)
     166  	state->largest_successful_arg_count = limit;
     167      }
     168    else
     169      {
     170        if (limit < state->smallest_failed_arg_count
     171  	  || (0 == state->smallest_failed_arg_count))
     172  	state->smallest_failed_arg_count = limit;
     173      }
     174  
     175    if (0 == (state->largest_successful_arg_count)
     176        || (state->smallest_failed_arg_count <= state->largest_successful_arg_count))
     177      {
     178        /* No success yet, or running on a system which has
     179  	 limits on total argv length, but not arg count. */
     180        if (success)
     181  	{
     182  	  if (limit < SIZE_MAX)
     183  	    ++limit;
     184  	}
     185        else
     186  	{
     187  	  limit /= 2;
     188  	}
     189      }
     190    else  /* We can use bisection. */
     191      {
     192        const size_t shift = (state->smallest_failed_arg_count
     193  			  - state->largest_successful_arg_count) / 2;
     194        if (success)
     195  	{
     196  	  if (shift)
     197  	    limit += shift;
     198  	  else
     199  	    ++limit;
     200  	}
     201        else
     202  	{
     203  	  if (shift)
     204  	    limit -= shift;
     205  	  else
     206  	    --limit;
     207  	}
     208      }
     209  
     210    /* Make sure the returned value is such that progress is
     211     * actually possible.
     212     */
     213    if (ctl->initial_argc && (limit <= ctl->initial_argc + 1u))
     214      limit = ctl->initial_argc + 1u;
     215    if (0 == limit)
     216      limit = 1u;
     217  
     218    return limit;
     219  }
     220  
     221  
     222  /* Copy some of the program arguments into an argv list.   Copy all the
     223   * initial arguments, plus up to LIMIT additional arguments.
     224   */
     225  static size_t
     226  copy_args (struct buildcmd_control *ctl,
     227  	   struct buildcmd_state *state,
     228  	   char** working_args, size_t limit, size_t done)
     229  {
     230    size_t dst_pos = 0;
     231    size_t src_pos = 0;
     232  
     233    while (src_pos < ctl->initial_argc)
     234      {
     235        working_args[dst_pos++] = state->cmd_argv[src_pos++];
     236      }
     237    src_pos += done;
     238    while (src_pos < state->cmd_argc && dst_pos < limit)
     239      {
     240        working_args[dst_pos++] = state->cmd_argv[src_pos++];
     241      }
     242    assert (dst_pos >= ctl->initial_argc);
     243    working_args[dst_pos] = NULL;
     244    return dst_pos;
     245  }
     246  
     247  
     248  
     249  
     250  /* Execute the program with the currently-built list of arguments. */
     251  void
     252  bc_do_exec (struct buildcmd_control *ctl,
     253  	    struct buildcmd_state *state)
     254  {
     255      char** working_args;
     256      size_t limit, done;
     257  
     258      /* Terminate the args. */
     259      bc_args_complete (ctl, state);
     260      /* Verify that the argument list is terminated. */
     261      assert (state->cmd_argc > 0);
     262      assert (state->cmd_argv[state->cmd_argc-1] == NULL);
     263  
     264      working_args = xmalloc ((1+state->cmd_argc) * sizeof (char*));
     265      done = 0;
     266      limit = state->cmd_argc;
     267  
     268      do
     269        {
     270  	const size_t dst_pos = copy_args (ctl, state, working_args,
     271  					  limit, done);
     272  	if (ctl->exec_callback (ctl, state->usercontext, dst_pos, working_args))
     273  	  {
     274  	    limit = update_limit (ctl, state, true, limit);
     275  	    done += (dst_pos - ctl->initial_argc);
     276  	  }
     277  	else  /* got E2BIG, adjust arguments */
     278  	  {
     279  	    if (limit <= ctl->initial_argc + 1)
     280  	      {
     281  		/* No room to reduce the length of the argument list.
     282  		   Issue an error message and give up. */
     283  		die (EXIT_FAILURE, 0,
     284  		     _("can't call exec() due to argument size restrictions"));
     285  	      }
     286  	    else
     287  	      {
     288  		/* Try fewer arguments. */
     289  		limit = update_limit (ctl, state, false, limit);
     290  	      }
     291  	  }
     292        }
     293      while ((done + 1) < (state->cmd_argc - ctl->initial_argc));
     294      /* (state->cmd_argc - ctl->initial_argc) includes the terminating NULL,
     295       * which is why we add 1 to done in the test above. */
     296  
     297      free (working_args);
     298      bc_clear_args (ctl, state);
     299  }
     300  
     301  
     302  /* Return nonzero if there would not be enough room for an additional
     303   * argument.  We check the total number of arguments only, not the space
     304   * occupied by those arguments.
     305   *
     306   * If we return zero, there still may not be enough room for the next
     307   * argument, depending on its length.
     308   */
     309  static int
     310  bc_argc_limit_reached (int initial_args,
     311  		       const struct buildcmd_control *ctl,
     312  		       struct buildcmd_state *state)
     313  {
     314    /* Check to see if we about to exceed a limit set by xargs' -n option */
     315    if (!initial_args && ctl->args_per_exec &&
     316        ( (state->cmd_argc - ctl->initial_argc) == ctl->args_per_exec))
     317      return 1;
     318  
     319    /* We deliberately use an equality test here rather than >= in order
     320     * to force a software failure if the code is modified in such a way
     321     * that it fails to call this function for every new argument.
     322     */
     323    return state->cmd_argc == ctl->max_arg_count;
     324  }
     325  
     326  
     327  /* Add ARG to the end of the list of arguments `cmd_argv' to pass
     328     to the command.
     329     LEN is the length of ARG, including the terminating null.
     330     If this brings the list up to its maximum size, execute the command.
     331  */
     332  void
     333  bc_push_arg (struct buildcmd_control *ctl,
     334               struct buildcmd_state *state,
     335               const char *arg, size_t len,
     336               const char *prefix, size_t pfxlen,
     337               int initial_args)
     338  {
     339    const int terminate = (arg == special_terminating_arg);
     340  
     341    assert (arg != NULL);
     342  
     343    if (!terminate)
     344      {
     345        if (state->cmd_argv_chars + len + pfxlen > ctl->arg_max)
     346          {
     347            if (initial_args || state->cmd_argc == ctl->initial_argc)
     348              die (EXIT_FAILURE, 0,
     349  		 _("cannot fit single argument within argument list size limit"));
     350  
     351            /* xargs option -i (replace_pat) implies -x (exit_if_size_exceeded) */
     352            if (ctl->replace_pat
     353                || (ctl->exit_if_size_exceeded &&
     354                    (ctl->lines_per_exec || ctl->args_per_exec)))
     355              die (EXIT_FAILURE, 0, _("argument list too long"));
     356            bc_do_exec (ctl, state);
     357          }
     358        if (bc_argc_limit_reached (initial_args, ctl, state))
     359              bc_do_exec (ctl, state);
     360      }
     361  
     362    if (!initial_args)
     363      {
     364        state->todo = 1;
     365      }
     366  
     367    if (state->cmd_argc >= state->cmd_argv_alloc)
     368      {
     369        /* XXX: we could use extendbuf() here. */
     370        if (!state->cmd_argv)
     371          {
     372            state->cmd_argv_alloc = 64;
     373            state->cmd_argv = xmalloc (sizeof (char *) * state->cmd_argv_alloc);
     374          }
     375        else
     376          {
     377            state->cmd_argv_alloc *= 2;
     378            state->cmd_argv = xrealloc (state->cmd_argv,
     379  				      sizeof (char *) * state->cmd_argv_alloc);
     380          }
     381      }
     382  
     383    if (terminate)
     384      state->cmd_argv[state->cmd_argc++] = NULL;
     385    else
     386      {
     387        state->cmd_argv[state->cmd_argc++] = state->argbuf + state->cmd_argv_chars;
     388        if (prefix)
     389          {
     390            strcpy (state->argbuf + state->cmd_argv_chars, prefix);
     391            state->cmd_argv_chars += pfxlen;
     392          }
     393  
     394        strcpy (state->argbuf + state->cmd_argv_chars, arg);
     395        state->cmd_argv_chars += len;
     396  
     397        /* If we have now collected enough arguments,
     398         * do the exec immediately.
     399         */
     400        if (bc_argc_limit_reached (initial_args, ctl, state))
     401  	{
     402  	  bc_do_exec (ctl, state);
     403  	}
     404      }
     405  
     406    /* If this is an initial argument, set the high-water mark. */
     407    if (initial_args)
     408      {
     409        state->cmd_initial_argv_chars = state->cmd_argv_chars;
     410      }
     411  }
     412  
     413  
     414  size_t
     415  bc_get_arg_max (void)
     416  {
     417    long val;
     418  
     419    /* We may resort to using LONG_MAX, so check it fits. */
     420    /* XXX: better to do a compile-time check */
     421    assert ( (~(size_t)0) >= LONG_MAX);
     422  
     423  #ifdef _SC_ARG_MAX
     424    val = sysconf (_SC_ARG_MAX);
     425  #else
     426    val = -1;
     427  #endif
     428  
     429    if (val > 0)
     430      return val;
     431  
     432    /* either _SC_ARG_MAX was not available or
     433     * there is no particular limit.
     434     */
     435  #ifdef ARG_MAX
     436    val = ARG_MAX;
     437    if (val > 0)
     438      return val;
     439  #endif
     440  
     441    /* The value returned by this function bounds the
     442     * value applied as the ceiling for the -s option.
     443     * Hence it the system won't tell us what its limit
     444     * is, we allow the user to specify more or less
     445     * whatever value they like.
     446     */
     447    return LONG_MAX;
     448  }
     449  
     450  
     451  static int
     452  cb_exec_noop (struct buildcmd_control * ctl,
     453  	      void *usercontext,
     454  	      int argc,
     455  	      char **argv)
     456  {
     457    /* does nothing. */
     458    (void) ctl;
     459    (void) usercontext;
     460    (void) argc;
     461    (void) argv;
     462  
     463    return 0;
     464  }
     465  
     466  
     467  /* Return how much of ARG_MAX is used by the environment.  */
     468  size_t
     469  bc_size_of_environment (void)
     470  {
     471    size_t len = 0u;
     472    char **envp = environ;
     473  
     474    while (*envp)
     475      len += strlen (*envp++) + 1;
     476  
     477    return len;
     478  }
     479  
     480  
     481  enum BC_INIT_STATUS
     482  bc_init_controlinfo (struct buildcmd_control *ctl,
     483  		     size_t headroom)
     484  {
     485    size_t size_of_environment = bc_size_of_environment ();
     486  
     487    /* POSIX requires that _POSIX_ARG_MAX is 4096.  That is the lowest
     488     * possible value for ARG_MAX on a POSIX compliant system.  See
     489     * https://www.opengroup.org/onlinepubs/009695399/basedefs/limits.h.html
     490     */
     491    ctl->posix_arg_size_min = _POSIX_ARG_MAX;
     492    ctl->posix_arg_size_max = bc_get_arg_max ();
     493  
     494    ctl->exit_if_size_exceeded = 0;
     495  
     496    /* Take the size of the environment into account.  */
     497    if (size_of_environment > ctl->posix_arg_size_max)
     498      {
     499        return BC_INIT_ENV_TOO_BIG;
     500      }
     501    else if ((headroom + size_of_environment) >= ctl->posix_arg_size_max)
     502      {
     503        /* POSIX.2 requires xargs to subtract 2048, but ARG_MAX is
     504         * guaranteed to be at least 4096.  Although xargs could use an
     505         * assertion here, we use a runtime check which returns an error
     506         * code, because our caller may not be xargs.
     507         */
     508        return BC_INIT_CANNOT_ACCOMODATE_HEADROOM;
     509      }
     510    else
     511      {
     512        ctl->posix_arg_size_max -= size_of_environment;
     513        ctl->posix_arg_size_max -= headroom;
     514      }
     515  
     516    /* need to subtract 2 on the following line - for Linux/PPC */
     517    ctl->max_arg_count = (ctl->posix_arg_size_max / sizeof (char*)) - 2u;
     518    assert (ctl->max_arg_count > 0);
     519    ctl->rplen = 0u;
     520    ctl->replace_pat = NULL;
     521    ctl->initial_argc = 0;
     522    ctl->exec_callback = cb_exec_noop;
     523    ctl->lines_per_exec = 0;
     524    ctl->args_per_exec = 0;
     525  
     526    /* Set the initial value of arg_max to the largest value we can
     527     * tolerate.
     528     */
     529    ctl->arg_max = ctl->posix_arg_size_max;
     530  
     531    return BC_INIT_OK;
     532  }
     533  
     534  void
     535  bc_use_sensible_arg_max (struct buildcmd_control *ctl)
     536  {
     537  #ifdef DEFAULT_ARG_SIZE
     538    enum { arg_size = DEFAULT_ARG_SIZE };
     539  #else
     540    enum { arg_size = (128u * 1024u) };
     541  #endif
     542  
     543    /* Check against the upper and lower limits. */
     544    if (arg_size > ctl->posix_arg_size_max)
     545      ctl->arg_max = ctl->posix_arg_size_max;
     546    else if (arg_size < ctl->posix_arg_size_min)
     547      ctl->arg_max = ctl->posix_arg_size_min;
     548    else
     549      ctl->arg_max = arg_size;
     550  }
     551  
     552  
     553  
     554  
     555  void
     556  bc_init_state (const struct buildcmd_control *ctl,
     557  	       struct buildcmd_state *state,
     558  	       void *context)
     559  {
     560    state->cmd_argc = 0;
     561    state->cmd_argv_chars = 0;
     562    state->cmd_argv = NULL;
     563    state->cmd_argv_alloc = 0;
     564    state->largest_successful_arg_count = 0;
     565    state->smallest_failed_arg_count = 0;
     566  
     567    /* XXX: the following memory allocation is inadvisable on systems
     568     * with no ARG_MAX, because ctl->arg_max may actually be close to
     569     * LONG_MAX.   Adding one to it is safe though because earlier we
     570     * subtracted 2048.
     571     */
     572    assert (ctl->arg_max <= (LONG_MAX - 2048L));
     573    state->argbuf = xmalloc (ctl->arg_max + 1u);
     574  
     575    state->cmd_argv_chars = state->cmd_initial_argv_chars = 0;
     576    state->todo = 0;
     577    state->dir_fd = -1;
     578    state->usercontext = context;
     579  }
     580  
     581  void
     582  bc_clear_args (const struct buildcmd_control *ctl,
     583  	       struct buildcmd_state *state)
     584  {
     585    state->cmd_argc = ctl->initial_argc;
     586    state->cmd_argv_chars = state->cmd_initial_argv_chars;
     587    state->todo = 0;
     588    state->dir_fd = -1;
     589  }
     590  
     591  
     592  /* Return nonzero if the value stored in the environment variable ENV_VAR_NAME
     593   * exceeds QUANTITY.
     594   */
     595  static int
     596  exceeds (const char *env_var_name, size_t quantity)
     597  {
     598    const char *val = getenv (env_var_name);
     599    if (val)
     600      {
     601        char *tmp;
     602        unsigned long limit;
     603  
     604        if (xstrtoul (val, &tmp, 10, &limit, NULL) == LONGINT_OK)
     605  	{
     606  	  if (quantity > limit)
     607  	    return 1;
     608  	}
     609        else
     610  	{
     611  	  die (EXIT_FAILURE, errno,
     612  	       _("Environment variable %s is not set to a "
     613  		 "valid decimal number"),
     614  	       env_var_name);
     615  	  return 0;
     616  	}
     617      }
     618    return 0;
     619  }
     620  
     621  /* Return nonzero if the indicated argument list exceeds a testing limit.
     622   * NOTE: argv could be declared 'const char *const *argv', but it works as
     623   * expected only with C++ compilers <http://c-faq.com/ansi/constmismatch.html>.
     624   */
     625  bool
     626  bc_args_exceed_testing_limit (char **argv)
     627  {
     628    size_t chars, args;
     629  
     630    for (chars=args=0; *argv; ++argv)
     631      {
     632        ++args;
     633        chars += strlen(*argv);
     634      }
     635  
     636    return (exceeds ("__GNU_FINDUTILS_EXEC_ARG_COUNT_LIMIT", args) ||
     637  	  exceeds ("__GNU_FINDUTILS_EXEC_ARG_LENGTH_LIMIT", chars));
     638  }