(root)/
findutils-4.9.0/
locate/
locate.c
       1  /* locate -- search databases for filenames that match patterns
       2     Copyright (C) 1994-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  
      18  /* Usage: locate [options] pattern...
      19  
      20     Scan a pathname list for the full pathname of a file, given only
      21     a piece of the name (possibly containing shell globbing metacharacters).
      22     The list has been processed with front-compression, which reduces
      23     the list size by a factor of 4-5.
      24     Recognizes two database formats, old and new.  The old format is
      25     bigram coded, which reduces space by a further 20-25% and uses the
      26     following encoding of the database bytes:
      27  
      28     0-28         likeliest differential counts + offset (14) to make nonnegative
      29     30           escape code for out-of-range count to follow in next halfword
      30     128-255      bigram codes (the 128 most common, as determined by `updatedb')
      31     32-127       single character (printable) ASCII remainder
      32  
      33     Earlier versions of GNU locate used to use a novel two-tiered
      34     string search technique, which was described in Usenix ;login:, Vol
      35     8, No 1, February/March, 1983, p. 8.
      36  
      37     However, latterly code changes to provide additional functionality
      38     became dificult to make with the existing reading scheme, and so
      39     we no longer perform the matching as efficiently as we used to (that is,
      40     we no longer use the same algorithm).
      41  
      42     The old algorithm was:
      43  
      44        First, match a metacharacter-free subpattern and a partial
      45        pathname BACKWARDS to avoid full expansion of the pathname list.
      46        The time savings is 40-50% over forward matching, which cannot
      47        efficiently handle overlapped search patterns and compressed
      48        path remainders.
      49  
      50        Then, match the actual shell glob pattern (if in this form)
      51        against the candidate pathnames using the slower shell filename
      52        matching routines.
      53  
      54  
      55     Written by James A. Woods <jwoods@adobe.com>.
      56     Modified by David MacKenzie <djm@gnu.org>.
      57     Additional work by James Youngman and Bas van Gompel.
      58  */
      59  
      60  /* config.h must be included first. */
      61  #include <config.h>
      62  
      63  /* system headers. */
      64  #include <assert.h>
      65  #include <ctype.h>
      66  #include <errno.h>
      67  #include <fcntl.h>
      68  #include <getopt.h>
      69  #include <grp.h>                /* for setgroups() */
      70  #include <regex.h>
      71  #include <signal.h>
      72  #include <stdbool.h>
      73  #include <stdio.h>
      74  #include <stdlib.h>
      75  #include <string.h>
      76  #include <sys/stat.h>
      77  #include <sys/types.h>
      78  #include <time.h>
      79  #include <unistd.h>
      80  #include <xstrtol.h>
      81  
      82  /* gnulib headers. */
      83  #include "fnmatch.h"
      84  #include "progname.h"
      85  #include "xalloc.h"
      86  #include "error.h"
      87  #include "fcntl--.h"
      88  #include "human.h"
      89  #include "dirname.h"
      90  #include "closeout.h"
      91  #include "quotearg.h"
      92  #include "regextype.h"
      93  #include "stat-time.h"
      94  #include "xstrtol-error.h"
      95  
      96  /* find headers. */
      97  #include "system.h"
      98  #include "bugreports.h"
      99  #include "die.h"
     100  #include "findutils-version.h"
     101  #include "gcc-function-attributes.h"
     102  #include "locatedb.h"
     103  #include "printquoted.h"
     104  #include "splitstring.h"
     105  
     106  
     107  /* Warn if a database is older than this.  8 days allows for a weekly
     108     update that takes up to a day to perform.  */
     109  static unsigned int warn_number_units = 8;
     110  
     111  /* Printable name of units used in WARN_SECONDS */
     112  static const char warn_name_units[] = N_("days");
     113  #define SECONDS_PER_UNIT (60 * 60 * 24)
     114  
     115  enum visit_result
     116    {
     117      VISIT_CONTINUE = 1,  /* please call the next visitor */
     118      VISIT_ACCEPTED = 2,  /* accepted, call no further callbacks for this file */
     119      VISIT_REJECTED = 4,  /* rejected, process next file. */
     120      VISIT_ABORT    = 8   /* rejected, process no more files. */
     121    };
     122  
     123  enum ExistenceCheckType
     124    {
     125      ACCEPT_EITHER,              /* Corresponds to lack of -E/-e option */
     126      ACCEPT_EXISTING,            /* Corresponds to option -e */
     127      ACCEPT_NON_EXISTING         /* Corresponds to option -E */
     128    };
     129  
     130  /* Check for existence of files before printing them out? */
     131  static enum ExistenceCheckType check_existence = ACCEPT_EITHER;
     132  
     133  static int follow_symlinks = 1;
     134  
     135  /* What to separate the results with. */
     136  static int separator = '\n';
     137  
     138  static struct quoting_options * quote_opts = NULL;
     139  static bool stdout_is_a_tty;
     140  static bool print_quoted_filename;
     141  static bool results_were_filtered;
     142  
     143  static const char *selected_secure_db = NULL;
     144  
     145  static int dolocate (int argc, char **argv, int secure_db_fd);
     146  
     147  /* Change the number of days old the database can be
     148   * before we complain about it.
     149   */
     150  static void
     151  set_max_db_age (const char *s)
     152  {
     153    char *end;
     154    unsigned long int val;
     155    /* XXX: we ignore the case where the input is negative, which is allowed(!). */
     156  
     157    if (0 == *s)
     158      {
     159        die (EXIT_FAILURE, 0,
     160             _("The argument for option --max-database-age must not be empty"));
     161      }
     162  
     163  
     164    /* We have to set errno here, otherwise when the function returns ULONG_MAX,
     165     * we would not be able to tell if that is the correct answer, or whether it
     166     * signifies an error.
     167     */
     168    errno = 0;
     169    val = strtoul (s, &end, 10);
     170  
     171    /* Diagnose number too large, non-numbes and trailing junk. */
     172    if ((ULONG_MAX == val && ERANGE == errno) ||
     173        (0 == val && EINVAL == errno))
     174      {
     175        die (EXIT_FAILURE, errno,
     176             _("Invalid argument %s for option --max-database-age"),
     177             quotearg_n_style (0, locale_quoting_style, s));
     178      }
     179    else if (*end)
     180      {
     181        /* errno wasn't set, don't print its message */
     182        die (EXIT_FAILURE, 0,
     183             _("Invalid argument %s for option --max-database-age"),
     184             quotearg_n_style (0, locale_quoting_style, s));
     185      }
     186    else
     187      {
     188        warn_number_units = val;
     189      }
     190  }
     191  
     192  
     193  
     194  /* Read in a 16-bit int, high byte first (network byte order).  */
     195  
     196  static short
     197  get_short (FILE *fp)
     198  {
     199  
     200    register short x;
     201  
     202    x = (signed char) fgetc (fp) << 8;
     203    x |= (fgetc (fp) & 0xff);
     204    return x;
     205  }
     206  
     207  static const char * const metacharacters = "*?[]\\";
     208  
     209  /* Return nonzero if S contains any shell glob characters.
     210   */
     211  static int
     212  contains_metacharacter (const char *s)
     213  {
     214    if (NULL == strpbrk (s, metacharacters))
     215      return 0;
     216    else
     217      return 1;
     218  }
     219  
     220  /* locate_read_str()
     221   *
     222   * Read bytes from FP into the buffer at offset OFFSET in (*BUF),
     223   * until we reach DELIMITER or end-of-file.   We reallocate the buffer
     224   * as necessary, altering (*BUF) and (*SIZ) as appropriate.  No assumption
     225   * is made regarding the content of the data (i.e. the implementation is
     226   * 8-bit clean, the only delimiter is DELIMITER).
     227   *
     228   * Written Fri May 23 18:41:16 2003 by James Youngman, because getstr()
     229   * has been removed from gnulib.
     230   *
     231   * We call the function locate_read_str() to avoid a name clash with the curses
     232   * function getstr().
     233   */
     234  static int
     235  locate_read_str (char **buf, size_t *siz, FILE *fp, int delimiter, int offs)
     236  {
     237    char * p = NULL;
     238    size_t sz = 0;
     239    int nread;
     240    size_t needed;
     241  
     242    nread = getdelim (&p, &sz, delimiter, fp);
     243    if (nread >= 0)
     244      {
     245        assert (p != NULL);
     246  
     247        needed = offs + nread + 1u;
     248        if (needed > (*siz))
     249          {
     250            char *pnew = realloc (*buf, needed);
     251            if (NULL == pnew)
     252              {
     253                return -1;        /* FAIL */
     254              }
     255            else
     256              {
     257                *siz = needed;
     258                *buf = pnew;
     259              }
     260          }
     261        memcpy((*buf)+offs, p, nread + 1);
     262        free(p);
     263      }
     264    return nread;
     265  }
     266  
     267  
     268  struct locate_limits
     269  {
     270    uintmax_t limit;
     271    uintmax_t items_accepted;
     272  };
     273  static struct locate_limits limits;
     274  
     275  
     276  struct locate_stats
     277  {
     278    uintmax_t compressed_bytes;
     279    uintmax_t total_filename_count;
     280    uintmax_t total_filename_length;
     281    uintmax_t whitespace_count;
     282    uintmax_t newline_count;
     283    uintmax_t highbit_filename_count;
     284  };
     285  static struct locate_stats statistics;
     286  
     287  
     288  struct regular_expression
     289  {
     290    struct re_pattern_buffer regex; /* for --regex */
     291  };
     292  
     293  
     294  struct process_data
     295  {
     296    int c;                        /* An input byte.  */
     297    int count; /* The length of the prefix shared with the previous database entry.  */
     298    int len;
     299    char *original_filename;      /* The current input database entry. */
     300    size_t pathsize;              /* Amount allocated for it.  */
     301    char *munged_filename;        /* path or basename(path) */
     302    FILE *fp;                     /* The pathname database.  */
     303    const char *dbfile;           /* Its name, or "<stdin>" */
     304    GetwordEndianState endian_state;
     305    /* for the old database format,
     306       the first and second characters of the most common bigrams.  */
     307    char bigram1[128];
     308    char bigram2[128];
     309  };
     310  
     311  
     312  typedef int (*visitfunc)(struct process_data *procdata,
     313                           void *context);
     314  
     315  struct visitor
     316  {
     317    visitfunc      inspector;
     318    void *         context;
     319    struct visitor *next;
     320  };
     321  
     322  
     323  static struct visitor *inspectors = NULL;
     324  static struct visitor *lastinspector = NULL;
     325  static struct visitor *past_pat_inspector = NULL;
     326  
     327  static inline int visit (const struct visitor *p,
     328                           int accept_flags,
     329                           struct process_data *procdata,
     330                           const struct visitor * const stop)
     331  {
     332    register int result = accept_flags;
     333    while ( (accept_flags & result) && (stop != p) )
     334      {
     335        result = (p->inspector)(procdata, p->context);
     336        p = p->next;
     337      }
     338    return result;
     339  }
     340  
     341  /* 0 or 1 pattern(s) */
     342  static int
     343  process_simple (struct process_data *procdata)
     344  {
     345    return visit (inspectors, (VISIT_CONTINUE|VISIT_ACCEPTED), procdata, NULL);
     346  }
     347  
     348  /* Accept if any pattern matches. */
     349  static int
     350  process_or (struct process_data *procdata)
     351  {
     352    int result;
     353  
     354    result = visit (inspectors, (VISIT_CONTINUE|VISIT_REJECTED), procdata, past_pat_inspector);
     355    if (result == VISIT_CONTINUE)
     356      result = VISIT_REJECTED;
     357    if (result & (VISIT_ABORT | VISIT_REJECTED))
     358      return result;
     359  
     360    result = visit (past_pat_inspector, VISIT_CONTINUE, procdata, NULL);
     361    if (VISIT_CONTINUE == result)
     362      return VISIT_ACCEPTED;
     363    else
     364      return result;
     365  }
     366  
     367  /* Accept if all pattern match. */
     368  static int
     369  process_and (struct process_data *procdata)
     370  {
     371    int result;
     372  
     373    result = visit (inspectors, (VISIT_CONTINUE|VISIT_ACCEPTED), procdata, past_pat_inspector);
     374    if (result == VISIT_CONTINUE)
     375      result = VISIT_REJECTED;
     376    if (result & (VISIT_ABORT | VISIT_REJECTED))
     377      return result;
     378  
     379    result = visit (past_pat_inspector, VISIT_CONTINUE, procdata, NULL);
     380    if (VISIT_CONTINUE == result)
     381      return VISIT_ACCEPTED;
     382    else
     383      return result;
     384  }
     385  
     386  typedef int (*processfunc)(struct process_data *procdata);
     387  
     388  static processfunc mainprocessor = NULL;
     389  
     390  static void
     391  add_visitor (visitfunc fn, void *context)
     392  {
     393    struct visitor *p = xmalloc (sizeof (struct visitor));
     394    p->inspector = fn;
     395    p->context   = context;
     396    p->next = NULL;
     397  
     398    if (NULL == lastinspector)
     399      {
     400        lastinspector = inspectors = p;
     401      }
     402    else
     403      {
     404        lastinspector->next = p;
     405        lastinspector = p;
     406      }
     407  }
     408  
     409  static int
     410  visit_justprint_quoted (struct process_data *procdata, void *context)
     411  {
     412    (void) context;
     413    print_quoted (stdout, quote_opts, stdout_is_a_tty,
     414                  "%s",
     415                  procdata->original_filename);
     416    putchar (separator);
     417    return VISIT_CONTINUE;
     418  }
     419  
     420  static int
     421  visit_justprint_unquoted (struct process_data *procdata, void *context)
     422  {
     423    (void) context;
     424    fputs (procdata->original_filename, stdout);
     425    putchar (separator);
     426    return VISIT_CONTINUE;
     427  }
     428  
     429  static void
     430  toolong (struct process_data *procdata)
     431  {
     432    die (EXIT_FAILURE, 0,
     433         _("locate database %s contains a "
     434           "filename longer than locate can handle"),
     435         procdata->dbfile);
     436  }
     437  
     438  static void
     439  extend (struct process_data *procdata, size_t siz1, size_t siz2)
     440  {
     441    /* Figure out if the addition operation is safe before performing it. */
     442    if (SIZE_MAX - siz1 < siz2)
     443      {
     444        toolong (procdata);
     445      }
     446    else if (procdata->pathsize < (siz1+siz2))
     447      {
     448        procdata->pathsize = siz1+siz2;
     449        procdata->original_filename = x2nrealloc (procdata->original_filename,
     450                                                  &procdata->pathsize,
     451                                                  1);
     452      }
     453  }
     454  
     455  static int
     456  visit_old_format (struct process_data *procdata, void *context)
     457  {
     458    register size_t i;
     459    (void) context;
     460  
     461    if (EOF == procdata->c)
     462      return VISIT_ABORT;
     463  
     464    /* Get the offset in the path where this path info starts.  */
     465    if (procdata->c == LOCATEDB_OLD_ESCAPE)
     466      {
     467        int minval, maxval;
     468        int word;
     469  
     470        procdata->count -= LOCATEDB_OLD_OFFSET;
     471        minval = (0       - procdata->count);
     472        if (procdata->count >= 0)
     473          maxval = (procdata->len - procdata->count);
     474        else
     475          maxval = (procdata->len - 0);
     476        word = getword (procdata->fp, procdata->dbfile,
     477                        maxval, &procdata->endian_state);
     478        assert (word >= minval);
     479        procdata->count += word;
     480        assert (procdata->count >= 0);
     481      }
     482    else
     483      {
     484        procdata->count += (procdata->c - LOCATEDB_OLD_OFFSET);
     485        assert (procdata->count >= 0);
     486      }
     487  
     488    /* Overlay the old path with the remainder of the new.  Read
     489     * more data until we get to the next filename.
     490     */
     491    for (i=procdata->count;
     492         (procdata->c = getc (procdata->fp)) > LOCATEDB_OLD_ESCAPE;)
     493      {
     494        if (EOF == procdata->c)
     495          break;
     496  
     497        if (procdata->c < 0200)
     498          {
     499            /* An ordinary character. */
     500            extend (procdata, i, 1u);
     501            procdata->original_filename[i++] = procdata->c;
     502          }
     503        else
     504          {
     505            /* Bigram markers have the high bit set. */
     506            extend (procdata, i, 2u);
     507            procdata->c &= 0177;
     508            procdata->original_filename[i++] = procdata->bigram1[procdata->c];
     509            procdata->original_filename[i++] = procdata->bigram2[procdata->c];
     510          }
     511      }
     512  
     513    /* Consider the case where we executed the loop body zero times; we
     514     * still need space for the terminating null byte.
     515     */
     516    extend (procdata, i, 1u);
     517    procdata->original_filename[i] = 0;
     518    procdata->len = i;
     519    procdata->munged_filename = procdata->original_filename;
     520  
     521    return VISIT_CONTINUE;
     522  }
     523  
     524  static int
     525  visit_locate02_format (struct process_data *procdata, void *context)
     526  {
     527    register char *s;
     528    int nread;
     529    (void) context;
     530  
     531    if (procdata->c == LOCATEDB_ESCAPE)
     532      procdata->count += (short)get_short (procdata->fp);
     533    else if (procdata->c > 127)
     534      procdata->count += procdata->c - 256;
     535    else
     536      procdata->count += procdata->c;
     537  
     538    if (procdata->count > procdata->len || procdata->count < 0)
     539      {
     540        /* This should not happen generally, but since we're
     541         * reading in data which is outside our control, we
     542         * cannot prevent it.
     543         */
     544        die (EXIT_FAILURE, 0, _("locate database %s is corrupt or invalid"),
     545             quotearg_n_style (0, locale_quoting_style, procdata->dbfile));
     546      }
     547  
     548    /* Overlay the old path with the remainder of the new.  */
     549    nread = locate_read_str (&procdata->original_filename,
     550                             &procdata->pathsize,
     551                             procdata->fp, 0, procdata->count);
     552    if (nread < 1)
     553      return VISIT_ABORT;
     554    procdata->c = getc (procdata->fp);
     555    procdata->len = procdata->count + nread - 1; /* Number of chars in path. */
     556  
     557    if (procdata->len < 1)
     558      {
     559        /* This should not happen generally, but since we're
     560         * reading in data which is outside our control, we
     561         * cannot prevent it.
     562         */
     563        error(1, 0, _("locate database %s is corrupt or invalid"),
     564              quotearg_n_style(0, locale_quoting_style, procdata->dbfile));
     565      }
     566  
     567    s = procdata->original_filename + procdata->len - 1; /* Move to the last char in path.  */
     568    assert (s[0] != '\0');
     569    assert (s[1] == '\0'); /* Our terminator.  */
     570    assert (s[2] == '\0'); /* Added by locate_read_str.  */
     571  
     572    procdata->munged_filename = procdata->original_filename;
     573  
     574    return VISIT_CONTINUE;
     575  }
     576  
     577  static int
     578  visit_basename (struct process_data *procdata, void *context)
     579  {
     580    (void) context;
     581    procdata->munged_filename = last_component (procdata->original_filename);
     582  
     583    return VISIT_CONTINUE;
     584  }
     585  
     586  
     587  /* visit_existing_follow implements -L -e */
     588  static int
     589  visit_existing_follow (struct process_data *procdata, void *context)
     590  {
     591    struct stat st;
     592    (void) context;
     593  
     594    /* munged_filename has been converted in some way (to lower case,
     595     * or is just the base name of the file), and original_filename has not.
     596     * Hence only original_filename is still actually the name of the file
     597     * whose existence we would need to check.
     598     */
     599    if (stat (procdata->original_filename, &st) != 0)
     600      {
     601        return VISIT_REJECTED;
     602      }
     603    else
     604      {
     605        return VISIT_CONTINUE;
     606      }
     607  }
     608  
     609  /* visit_non_existing_follow implements -L -E */
     610  static int
     611  visit_non_existing_follow (struct process_data *procdata, void *context)
     612  {
     613    struct stat st;
     614    (void) context;
     615  
     616    /* munged_filename has been converted in some way (to lower case,
     617     * or is just the base name of the file), and original_filename has not.
     618     * Hence only original_filename is still actually the name of the file
     619     * whose existence we would need to check.
     620     */
     621    if (stat (procdata->original_filename, &st) == 0)
     622      {
     623        return VISIT_REJECTED;
     624      }
     625    else
     626      {
     627        return VISIT_CONTINUE;
     628      }
     629  }
     630  
     631  /* visit_existing_nofollow implements -P -e */
     632  static int
     633  visit_existing_nofollow (struct process_data *procdata, void *context)
     634  {
     635    struct stat st;
     636    (void) context;
     637  
     638    /* munged_filename has been converted in some way (to lower case,
     639     * or is just the base name of the file), and original_filename has not.
     640     * Hence only original_filename is still actually the name of the file
     641     * whose existence we would need to check.
     642     */
     643    if (lstat (procdata->original_filename, &st) != 0)
     644      {
     645        return VISIT_REJECTED;
     646      }
     647    else
     648      {
     649        return VISIT_CONTINUE;
     650      }
     651  }
     652  
     653  /* visit_non_existing_nofollow implements -P -E */
     654  static int
     655  visit_non_existing_nofollow (struct process_data *procdata, void *context)
     656  {
     657    struct stat st;
     658    (void) context;
     659  
     660    /* munged_filename has been converted in some way (to lower case,
     661     * or is just the base name of the file), and original_filename has not.
     662     * Hence only original_filename is still actually the name of the file
     663     * whose existence we would need to check.
     664     */
     665    if (lstat (procdata->original_filename, &st) == 0)
     666      {
     667        return VISIT_REJECTED;
     668      }
     669    else
     670      {
     671        return VISIT_CONTINUE;
     672      }
     673  }
     674  
     675  static int
     676  visit_substring_match_nocasefold_wide (struct process_data *procdata, void *context)
     677  {
     678    const char *pattern = context;
     679  
     680    if (NULL != mbsstr (procdata->munged_filename, pattern))
     681      return VISIT_ACCEPTED;
     682    else
     683      return VISIT_REJECTED;
     684  }
     685  
     686  static int
     687  visit_substring_match_nocasefold_narrow (struct process_data *procdata, void *context)
     688  {
     689    const char *pattern = context;
     690    assert (MB_CUR_MAX == 1);
     691    if (NULL != strstr (procdata->munged_filename, pattern))
     692      return VISIT_ACCEPTED;
     693    else
     694      return VISIT_REJECTED;
     695  }
     696  
     697  static int
     698  visit_substring_match_casefold_wide (struct process_data *procdata, void *context)
     699  {
     700    const char *pattern = context;
     701  
     702    if (NULL != mbscasestr (procdata->munged_filename, pattern))
     703      return VISIT_ACCEPTED;
     704    else
     705      return VISIT_REJECTED;
     706  }
     707  
     708  
     709  static int
     710  visit_substring_match_casefold_narrow (struct process_data *procdata, void *context)
     711  {
     712    const char *pattern = context;
     713  
     714    assert (MB_CUR_MAX == 1);
     715    if (NULL != strcasestr (procdata->munged_filename, pattern))
     716      return VISIT_ACCEPTED;
     717    else
     718      return VISIT_REJECTED;
     719  }
     720  
     721  
     722  static int
     723  visit_globmatch_nofold (struct process_data *procdata, void *context)
     724  {
     725    const char *glob = context;
     726    if (fnmatch (glob, procdata->munged_filename, 0) != 0)
     727      return VISIT_REJECTED;
     728    else
     729      return VISIT_ACCEPTED;
     730  }
     731  
     732  
     733  static int
     734  visit_globmatch_casefold (struct process_data *procdata, void *context)
     735  {
     736    const char *glob = context;
     737    if (fnmatch (glob, procdata->munged_filename, FNM_CASEFOLD) != 0)
     738      return VISIT_REJECTED;
     739    else
     740      return VISIT_ACCEPTED;
     741  }
     742  
     743  
     744  static int
     745  visit_regex (struct process_data *procdata, void *context)
     746  {
     747    struct regular_expression *p = context;
     748    const size_t len = strlen (procdata->munged_filename);
     749  
     750    int rv = re_search (&p->regex, procdata->munged_filename,
     751                        len, 0, len,
     752                        (struct re_registers *) NULL);
     753    if (rv < 0)
     754      {
     755        return VISIT_REJECTED;    /* no match (-1), or internal error (-2) */
     756      }
     757    else
     758      {
     759        return VISIT_ACCEPTED;    /* match */
     760      }
     761  }
     762  
     763  
     764  static int
     765  visit_stats (struct process_data *procdata, void *context)
     766  {
     767    struct locate_stats *p = context;
     768    size_t len = strlen (procdata->original_filename);
     769    const char *s;
     770    int highbit, whitespace, newline;
     771  
     772    ++(p->total_filename_count);
     773    p->total_filename_length += len;
     774  
     775    highbit = whitespace = newline = 0;
     776    for (s=procdata->original_filename; *s; ++s)
     777      {
     778        if ( (int)(*s) & 128 )
     779          highbit = 1;
     780        if ('\n' == *s)
     781          {
     782            newline = whitespace = 1;
     783          }
     784        else if (isspace ((unsigned char)*s))
     785          {
     786            whitespace = 1;
     787          }
     788      }
     789  
     790    if (highbit)
     791      ++(p->highbit_filename_count);
     792    if (whitespace)
     793      ++(p->whitespace_count);
     794    if (newline)
     795      ++(p->newline_count);
     796  
     797    return VISIT_CONTINUE;
     798  }
     799  
     800  
     801  static int
     802  visit_limit (struct process_data *procdata, void *context)
     803  {
     804    struct locate_limits *p = context;
     805  
     806    (void) procdata;
     807  
     808    if (++p->items_accepted >= p->limit)
     809      return VISIT_ABORT;
     810    else
     811      return VISIT_CONTINUE;
     812  }
     813  
     814  static int
     815  visit_count (struct process_data *procdata, void *context)
     816  {
     817    struct locate_limits *p = context;
     818  
     819    (void) procdata;
     820  
     821    ++p->items_accepted;
     822    return VISIT_CONTINUE;
     823  }
     824  
     825  /* Emit the statistics.
     826   */
     827  static void
     828  print_stats (int argc, size_t database_file_size, const struct timespec* database_mtime)
     829  {
     830    char hbuf1[LONGEST_HUMAN_READABLE + 1];
     831    char hbuf2[LONGEST_HUMAN_READABLE + 1];
     832    char hbuf3[LONGEST_HUMAN_READABLE + 1];
     833    char hbuf4[LONGEST_HUMAN_READABLE + 1];
     834  
     835    if (database_mtime)
     836      {
     837        const struct tm *ptm = localtime (&(database_mtime->tv_sec));
     838        if (ptm)
     839          {
     840            enum { TIME_BUF_LEN = 20 };
     841            char whenbuf[TIME_BUF_LEN];
     842            size_t printed = strftime (whenbuf, TIME_BUF_LEN,
     843                                       "%Y:%m:%d %H:%M:%S", ptm);
     844            /* Ensure the buffer is exactly the right length. */
     845            assert (printed == TIME_BUF_LEN-1);
     846            assert (whenbuf[TIME_BUF_LEN-1] == 0);
     847            assert (whenbuf[TIME_BUF_LEN-2] != 0);
     848            printf (_("Database was last modified at %s.%09ld"),
     849                    whenbuf, (long int) database_mtime->tv_nsec);
     850            printed = strftime (whenbuf, TIME_BUF_LEN, "%z", ptm);
     851            assert (printed == 5);
     852            printf(" %s\n", whenbuf);
     853          }
     854      }
     855  
     856    printf (ngettext ("Locate database size: %s byte\n",
     857                    "Locate database size: %s bytes\n",
     858                    database_file_size),
     859           human_readable ((uintmax_t) database_file_size,
     860                           hbuf1, human_ceiling, 1, 1));
     861    printf ( (results_were_filtered ?
     862             _("Matching Filenames: %s\n") :
     863             _("All Filenames: %s\n")),
     864            human_readable (statistics.total_filename_count,
     865                           hbuf1, human_ceiling, 1, 1));
     866    /* XXX: We would ideally use ngettext () here, but I don't know
     867     *      how to use it to handle more than one possibly-plural thing/
     868     */
     869    printf (_("File names have a cumulative length of %s bytes.\n"
     870             "Of those file names,\n"
     871             "\n\t%s contain whitespace, "
     872             "\n\t%s contain newline characters, "
     873             "\n\tand %s contain characters with the high bit set.\n"),
     874           human_readable (statistics.total_filename_length,  hbuf1, human_ceiling, 1, 1),
     875           human_readable (statistics.whitespace_count,       hbuf2, human_ceiling, 1, 1),
     876           human_readable (statistics.newline_count,          hbuf3, human_ceiling, 1, 1),
     877           human_readable (statistics.highbit_filename_count, hbuf4, human_ceiling, 1, 1));
     878  
     879    if (!argc)
     880      {
     881        if (results_were_filtered)
     882          {
     883            printf (_("Some filenames may have been filtered out, "
     884                     "so we cannot compute the compression ratio.\n"));
     885          }
     886        else
     887          {
     888            if (statistics.total_filename_length)
     889              {
     890                /* A negative compression ratio just means that the
     891                 * compressed database is larger than the list of
     892                 * filenames.  This can happen for example for
     893                 * old-format databases containing a small list of short
     894                 * filenames, because the bigram list is 256 bytes.
     895                 */
     896                printf (_("Compression ratio %4.2f%% (higher is better)\n"),
     897                       100.0 * ((double)statistics.total_filename_length
     898                                - (double) database_file_size)
     899                       / (double) statistics.total_filename_length);
     900              }
     901            else
     902              {
     903                printf (_("Compression ratio is undefined\n"));
     904              }
     905          }
     906      }
     907    printf ("\n");
     908  }
     909  
     910  /*
     911   * Return nonzero if the data we read in indicates that we are
     912   * looking at a LOCATE02 locate database.
     913   */
     914  static int
     915  looking_at_gnu_locatedb (const char *data, size_t len)
     916  {
     917    if (len < sizeof (LOCATEDB_MAGIC))
     918      return 0;
     919    else if (0 == memcmp (data, LOCATEDB_MAGIC, sizeof (LOCATEDB_MAGIC)))
     920      return 1;                   /* We saw the magic byte sequence */
     921    else
     922      return 0;
     923  }
     924  
     925  /*
     926   * Return nonzero if the data we read in indicates that we are
     927   * looking at an slocate database.
     928   */
     929  static int
     930  looking_at_slocate_locatedb (const char *filename,
     931                               const char *data,
     932                               size_t len,
     933                               int *seclevel)
     934  {
     935    assert (len <= 2);
     936  
     937    if (len < 2)
     938      {
     939        return 0;
     940      }
     941    else
     942      {
     943        /* Check that the magic number is a one-byte string */
     944        if (0 == data[1])
     945          {
     946            if (isdigit ((unsigned char)data[0]))
     947              {
     948                /* looks promising. */
     949                *seclevel = (data[0] - '0');
     950  
     951                if (*seclevel > 1)
     952                  {
     953                    /* Hmm, well it's probably an slocate database
     954                     * of some awsomely huge security level, like 2.
     955                     * We don't know how to handle those.
     956                     */
     957                    error (0, 0,
     958                           _("locate database %s looks like an slocate "
     959                             "database but it seems to have security level %c, "
     960                             "which GNU findutils does not currently support"),
     961                           quotearg_n_style (0, locale_quoting_style, filename),
     962                           data[1]);
     963                    return 1;
     964                  }
     965                else
     966                  {
     967                    return 1;
     968                  }
     969              }
     970            else
     971              {
     972                /* Not a digit. */
     973                return 0;
     974              }
     975          }
     976        else
     977          {
     978            /* Definitely not slocate. */
     979            return 0;
     980          }
     981      }
     982  }
     983  
     984  
     985  static int
     986  i_am_little_endian (void)
     987  {
     988    union
     989    {
     990      unsigned char uch[4];
     991      unsigned int ui;
     992    } u;
     993    u.ui = 0u;
     994    u.uch[0] = 1;
     995    u.uch[1] = u.uch[2] = u.uch[3] = 0;
     996    return u.ui == 1;
     997  }
     998  
     999  
    1000  
    1001  
    1002  /* Print or count the entries in DBFILE that match shell globbing patterns in
    1003     ARGV. Return the number of entries matched. */
    1004  
    1005  static unsigned long
    1006  search_one_database (int argc,
    1007                       char **argv,
    1008                       const char *dbfile,
    1009                       FILE *fp,
    1010                       off_t filesize,
    1011                       const struct timespec *database_mtime,
    1012                       int ignore_case,
    1013                       int enable_print,
    1014                       int basename_only,
    1015                       int use_limit,
    1016                       struct locate_limits *plimit,
    1017                       int stats,
    1018                       int op_and,
    1019                       int regex,
    1020                       int regex_options)
    1021  {
    1022    char *pathpart;               /* A pattern to consider. */
    1023    int argn;                     /* Index to current pattern in argv. */
    1024    int nread;                 /* number of bytes read from an entry. */
    1025    struct process_data procdata; /* Storage for data shared with visitors. */
    1026    int slocate_seclevel;
    1027    int oldformat;
    1028    int slocatedb_format;
    1029    struct visitor* pvis; /* temp for determining past_pat_inspector. */
    1030    const char *format_name;
    1031    enum ExistenceCheckType do_check_existence;
    1032  
    1033  
    1034    /* We may turn on existence checking for a given database.
    1035     * We ensure that we can return to the previous behaviour
    1036     * by using two variables, do_check_existence (which we act on)
    1037     * and check_existence (which indicates the default before we
    1038     * adjust it on the bassis of what kind of database we;re using
    1039     */
    1040    do_check_existence = check_existence;
    1041  
    1042  
    1043    if (ignore_case)
    1044      regex_options |= RE_ICASE;
    1045  
    1046    oldformat = 0;
    1047    procdata.endian_state = GetwordEndianStateInitial;
    1048    procdata.len = procdata.count = 0;
    1049  
    1050    procdata.dbfile = dbfile;
    1051    procdata.fp = fp;
    1052  
    1053    /* Set up the inspection regime */
    1054    inspectors = NULL;
    1055    lastinspector = NULL;
    1056    past_pat_inspector = NULL;
    1057    results_were_filtered = false;
    1058    procdata.pathsize = 128;      /* Increased as necessary by locate_read_str.  */
    1059    procdata.original_filename = xmalloc (procdata.pathsize);
    1060  
    1061  
    1062    nread = fread (procdata.original_filename, 1, SLOCATE_DB_MAGIC_LEN,
    1063                   procdata.fp);
    1064    slocate_seclevel = 0;
    1065    if (looking_at_slocate_locatedb (procdata.dbfile,
    1066                                     procdata.original_filename,
    1067                                     nread,
    1068                                     &slocate_seclevel))
    1069      {
    1070        /* slocate also uses frcode, but with a different header.
    1071         * We handle the header here and then work with the data
    1072         * in the normal way.
    1073         */
    1074        if (slocate_seclevel > 1)
    1075          {
    1076            /* We don't know what those security levels mean,
    1077             * so do nothing further
    1078             */
    1079            error (0, 0,
    1080                   _("%s is an slocate database of unsupported security level %d; skipping it."),
    1081                   quotearg_n_style (0, locale_quoting_style, procdata.dbfile),
    1082                   slocate_seclevel);
    1083            return 0;
    1084          }
    1085        else if (slocate_seclevel > 0)
    1086          {
    1087            /* Don't show the filenames to the user if they don't exist.
    1088             * Showing stats is safe since filenames are only counted
    1089             * after the existence check
    1090             */
    1091            if (ACCEPT_NON_EXISTING == check_existence)
    1092              {
    1093                /* Do not allow the user to see a list of filenames that they
    1094                 * cannot stat().
    1095                 */
    1096                error (0, 0,
    1097                       _("You specified the -E option, but that option "
    1098                         "cannot be used with slocate-format databases "
    1099                         "with a non-zero security level.  No results will be "
    1100                         "generated for this database.\n"));
    1101                return 0;
    1102              }
    1103            if (ACCEPT_EXISTING != do_check_existence)
    1104              {
    1105                if (enable_print || stats)
    1106                  {
    1107                    error (0, 0,
    1108                           _("%s is an slocate database.  "
    1109                             "Turning on the '-e' option."),
    1110                           quotearg_n_style (0, locale_quoting_style, procdata.dbfile));
    1111                  }
    1112                do_check_existence = ACCEPT_EXISTING;
    1113              }
    1114          }
    1115        add_visitor (visit_locate02_format, NULL);
    1116        format_name = "slocate";
    1117        slocatedb_format = 1;
    1118      }
    1119    else
    1120      {
    1121        int nread2;
    1122  
    1123        slocatedb_format = 0;
    1124        extend (&procdata, sizeof (LOCATEDB_MAGIC), 0u);
    1125        nread2 = fread (procdata.original_filename+nread, 1, sizeof (LOCATEDB_MAGIC)-nread,
    1126                        procdata.fp);
    1127        if (looking_at_gnu_locatedb (procdata.original_filename, nread+nread2))
    1128          {
    1129            add_visitor (visit_locate02_format, NULL);
    1130            format_name = "GNU LOCATE02";
    1131          }
    1132        else                              /* Use the old format */
    1133          {
    1134            int i;
    1135  
    1136            nread += nread2;
    1137            extend (&procdata, 256u, 0u);
    1138            /* Read the list of the most common bigrams in the database.  */
    1139            if (nread < 256)
    1140              {
    1141                int more_read = fread (procdata.original_filename + nread, 1,
    1142                                       256 - nread, procdata.fp);
    1143                if ( (more_read + nread) != 256 )
    1144                  {
    1145                    die (EXIT_FAILURE, 0,
    1146                         _("Old-format locate database %s is "
    1147                           "too short to be valid"),
    1148                         quotearg_n_style (0, locale_quoting_style, dbfile));
    1149  
    1150                  }
    1151              }
    1152  
    1153            for (i = 0; i < 128; i++)
    1154              {
    1155                procdata.bigram1[i] = procdata.original_filename[i << 1];
    1156                procdata.bigram2[i] = procdata.original_filename[(i << 1) + 1];
    1157              }
    1158            format_name = "old";
    1159            oldformat = 1;
    1160            add_visitor (visit_old_format, NULL);
    1161          }
    1162      }
    1163  
    1164    if (basename_only)
    1165      add_visitor (visit_basename, NULL);
    1166  
    1167    /* Add an inspector for each pattern we're looking for. */
    1168    for ( argn = 0; argn < argc; argn++ )
    1169      {
    1170        results_were_filtered = true;
    1171        pathpart = argv[argn];
    1172        if (regex)
    1173          {
    1174            struct regular_expression *p = xmalloc (sizeof (*p));
    1175            const char *error_message = NULL;
    1176  
    1177            memset (&p->regex, 0, sizeof (p->regex));
    1178  
    1179            re_set_syntax (regex_options);
    1180            p->regex.allocated = 100;
    1181            p->regex.buffer = xmalloc (p->regex.allocated);
    1182            p->regex.fastmap = NULL;
    1183            p->regex.syntax = regex_options;
    1184            p->regex.translate = NULL;
    1185  
    1186            error_message = re_compile_pattern (pathpart, strlen (pathpart),
    1187                                                &p->regex);
    1188            if (error_message)
    1189              {
    1190                die (EXIT_FAILURE, 0, "%s", error_message);
    1191              }
    1192            else
    1193              {
    1194                add_visitor (visit_regex, p);
    1195              }
    1196          }
    1197        else if (contains_metacharacter (pathpart))
    1198          {
    1199            if (ignore_case)
    1200              add_visitor (visit_globmatch_casefold, pathpart);
    1201            else
    1202              add_visitor (visit_globmatch_nofold, pathpart);
    1203          }
    1204        else
    1205          {
    1206            /* No glob characters used.  Hence we match on
    1207             * _any part_ of the filename, not just the
    1208             * basename.  This seems odd to me, but it is the
    1209             * traditional behaviour.
    1210             * James Youngman <jay@gnu.org>
    1211             */
    1212            visitfunc matcher;
    1213            if (1 == MB_CUR_MAX)
    1214              {
    1215                /* As an optimisation, use a strstr () matcher if we are
    1216                 * in a unibyte locale.  This can give a x2 speedup in
    1217                 * the C locale.  Some light testing reveals that
    1218                 * glibc's strstr () is somewhere around 40% faster than
    1219                 * gnulib's, so we just use strstr ().
    1220                 */
    1221                matcher = ignore_case ?
    1222                  visit_substring_match_casefold_narrow  :
    1223                  visit_substring_match_nocasefold_narrow;
    1224              }
    1225            else
    1226              {
    1227                matcher = ignore_case ?
    1228                  visit_substring_match_casefold_wide  :
    1229                  visit_substring_match_nocasefold_wide;
    1230              }
    1231            add_visitor (matcher, pathpart);
    1232          }
    1233      }
    1234  
    1235    pvis = lastinspector;
    1236  
    1237    /* We add visit_existing_*() as late as possible to reduce the
    1238     * number of stat() calls.
    1239     */
    1240    switch (do_check_existence)
    1241      {
    1242        case ACCEPT_EXISTING:
    1243          results_were_filtered = true;
    1244          if (follow_symlinks)    /* -L, default */
    1245            add_visitor (visit_existing_follow, NULL);
    1246          else                    /* -P */
    1247            add_visitor (visit_existing_nofollow, NULL);
    1248          break;
    1249  
    1250        case ACCEPT_NON_EXISTING:
    1251          results_were_filtered = true;
    1252          if (follow_symlinks)    /* -L, default */
    1253            add_visitor (visit_non_existing_follow, NULL);
    1254          else                    /* -P */
    1255            add_visitor (visit_non_existing_nofollow, NULL);
    1256          break;
    1257  
    1258        case ACCEPT_EITHER:       /* Default, neither -E nor -e */
    1259          /* do nothing; no extra processing. */
    1260          break;
    1261      }
    1262  
    1263    /* Security issue: The stats visitor must be added immediately
    1264     * before the print visitor, because otherwise the -S option would
    1265     * leak information about files that the caller cannot see.
    1266     */
    1267    if (stats)
    1268      add_visitor (visit_stats, &statistics);
    1269  
    1270    if (enable_print)
    1271      {
    1272        if (print_quoted_filename)
    1273          add_visitor (visit_justprint_quoted,   NULL);
    1274        else
    1275          add_visitor (visit_justprint_unquoted, NULL);
    1276      }
    1277  
    1278  
    1279    if (use_limit)
    1280      add_visitor (visit_limit, plimit);
    1281    else
    1282      add_visitor (visit_count, plimit);
    1283  
    1284  
    1285    if (argc > 1)
    1286      {
    1287        past_pat_inspector = pvis->next;
    1288        if (op_and)
    1289          mainprocessor = process_and;
    1290        else
    1291          mainprocessor = process_or;
    1292      }
    1293    else
    1294      mainprocessor = process_simple;
    1295  
    1296    if (stats)
    1297      {
    1298        printf (_("Database %s is in the %s format.\n"),
    1299                procdata.dbfile,
    1300                format_name);
    1301      }
    1302  
    1303  
    1304    procdata.c = getc (procdata.fp);
    1305    if (slocatedb_format  && (procdata.c != EOF))
    1306      {
    1307        /* Make slocate database look like GNU locate database. */
    1308        ungetc(procdata.c, procdata.fp);
    1309        procdata.c = 0;
    1310      }
    1311    /* If we are searching for filename patterns, the inspector list
    1312     * will contain an entry for each pattern for which we are searching.
    1313     */
    1314    while ( (procdata.c != EOF) &&
    1315            (VISIT_ABORT != (mainprocessor)(&procdata)) )
    1316      {
    1317        /* Do nothing; all the work is done in the visitor functions. */
    1318      }
    1319  
    1320    if (stats)
    1321      {
    1322        if (oldformat)
    1323          {
    1324            int host_little_endian = i_am_little_endian ();
    1325            const char *little = _("The database has little-endian "
    1326                                   "machine-word encoding.\n");
    1327            const char *big    = _("The database has big-endian "
    1328                                   "machine-word encoding.\n");
    1329  
    1330            if (GetwordEndianStateNative == procdata.endian_state)
    1331              {
    1332                printf ("%s", (host_little_endian ? little : big));
    1333              }
    1334            else if (GetwordEndianStateSwab == procdata.endian_state)
    1335              {
    1336                printf ("%s", (host_little_endian ? big : little));
    1337              }
    1338            else
    1339              {
    1340                printf (_("The database machine-word encoding order "
    1341                         "is not obvious.\n"));
    1342              }
    1343          }
    1344        if (filesize || (database_mtime != NULL))
    1345          print_stats (argc, filesize, database_mtime);
    1346      }
    1347  
    1348    if (ferror (procdata.fp))
    1349      {
    1350        error (0, errno, "%s",
    1351               quotearg_n_style (0, locale_quoting_style, procdata.dbfile));
    1352        return 0;
    1353      }
    1354    return plimit->items_accepted;
    1355  }
    1356  
    1357  
    1358  extern char *version_string;
    1359  
    1360  static void _GL_ATTRIBUTE_NORETURN
    1361  usage (int status)
    1362  {
    1363    if (status != EXIT_SUCCESS)
    1364      {
    1365        fprintf (stderr, _("Try '%s --help' for more information.\n"), program_name);
    1366        exit (status);
    1367      }
    1368  
    1369    fprintf (stdout, _("\
    1370  Usage: %s [-d path | --database=path] [-e | -E | --[non-]existing]\n\
    1371        [-i | --ignore-case] [-w | --wholename] [-b | --basename] \n\
    1372        [--limit=N | -l N] [-S | --statistics] [-0 | --null] [-c | --count]\n\
    1373        [-P | -H | --nofollow] [-L | --follow] [-m | --mmap] [-s | --stdio]\n\
    1374        [-A | --all] [-p | --print] [-r | --regex] [--regextype=TYPE]\n\
    1375        [--max-database-age D] [--version] [--help]\n\
    1376        pattern...\n"),
    1377             program_name);
    1378  
    1379    explain_how_to_report_bugs (stdout, program_name);
    1380    exit (status);
    1381  }
    1382  
    1383  enum
    1384    {
    1385      REGEXTYPE_OPTION = CHAR_MAX + 1,
    1386      MAX_DB_AGE
    1387    };
    1388  
    1389  
    1390  static struct option const longopts[] =
    1391  {
    1392    {"database", required_argument, NULL, 'd'},
    1393    {"existing", no_argument, NULL, 'e'},
    1394    {"non-existing", no_argument, NULL, 'E'},
    1395    {"ignore-case", no_argument, NULL, 'i'},
    1396    {"all", no_argument, NULL, 'A'},
    1397    {"help", no_argument, NULL, 'h'},
    1398    {"version", no_argument, NULL, 'v'},
    1399    {"null", no_argument, NULL, '0'},
    1400    {"count", no_argument, NULL, 'c'},
    1401    {"wholename", no_argument, NULL, 'w'},
    1402    {"wholepath", no_argument, NULL, 'w'}, /* Synonym. */
    1403    {"basename", no_argument, NULL, 'b'},
    1404    {"print", no_argument, NULL, 'p'},
    1405    {"stdio", no_argument, NULL, 's'},
    1406    {"mmap",  no_argument, NULL, 'm'},
    1407    {"limit",  required_argument, NULL, 'l'},
    1408    {"regex",  no_argument, NULL, 'r'},
    1409    {"regextype",  required_argument, NULL, REGEXTYPE_OPTION},
    1410    {"statistics",  no_argument, NULL, 'S'},
    1411    {"follow",      no_argument, NULL, 'L'},
    1412    {"nofollow",    no_argument, NULL, 'P'},
    1413    {"max-database-age",    required_argument, NULL, MAX_DB_AGE},
    1414    {NULL, no_argument, NULL, 0}
    1415  };
    1416  
    1417  
    1418  static int
    1419  drop_privs (void)
    1420  {
    1421    const char * what = "failed";
    1422    const uid_t orig_euid = geteuid ();
    1423    const uid_t uid       = getuid ();
    1424    const gid_t gid       = getgid ();
    1425  
    1426  #if HAVE_SETGROUPS
    1427    /* Use of setgroups () is restricted to root only. */
    1428    if (0 == orig_euid)
    1429      {
    1430        /* We're either root or running setuid-root. */
    1431        gid_t groups[1];
    1432        groups[0] = gid;
    1433        if (0 != setgroups (1u, groups))
    1434          {
    1435            what = _("failed to drop group privileges");
    1436            goto fail;
    1437          }
    1438      }
    1439  #endif
    1440  
    1441    /* Drop any setuid privileges */
    1442    if (uid != orig_euid)
    1443      {
    1444        if (0 == uid)
    1445          {
    1446            /* We're really root anyway, but are setuid to something else. Leave it. */
    1447          }
    1448        else
    1449          {
    1450            errno = 0;
    1451            if (0 != setuid (getuid ()))
    1452              {
    1453                what = _("failed to drop setuid privileges");
    1454                goto fail;
    1455              }
    1456  
    1457            /* Defend against the case where the attacker runs us with the
    1458             * capability to call setuid () turned off, which on some systems
    1459             * will cause the above attempt to drop privileges fail (leaving us
    1460             * privileged).
    1461             */
    1462            else
    1463              {
    1464                /* Check that we can no longer switch bask to root */
    1465                if (0 == setuid (0))
    1466                  {
    1467                    what = _("Failed to fully drop privileges");
    1468                    /* The errno value here is not interesting (since
    1469                     * the system call we are complaining about
    1470                     * succeeded when we wanted it to fail).  Arrange
    1471                     * for the call to error () not to print the errno
    1472                     * value by setting errno=0.
    1473                     */
    1474                    errno = 0;
    1475                    goto fail;
    1476                  }
    1477              }
    1478          }
    1479      }
    1480  
    1481    /* Drop any setgid privileges */
    1482    errno = 0;
    1483    if (0 != setgid (gid))
    1484      {
    1485        what = _("failed to drop setgid privileges");
    1486        goto fail;
    1487      }
    1488  
    1489    /* success. */
    1490    return 0;
    1491  
    1492   fail:
    1493    die (EXIT_FAILURE, errno, "%s",
    1494         quotearg_n_style (0, locale_quoting_style, what));
    1495    abort ();
    1496    kill (0, SIGKILL);
    1497    _exit (1);
    1498    /*NOTREACHED*/
    1499    /* ... we hope. */
    1500    for (;;)
    1501      {
    1502        /* deliberate infinite loop */
    1503      }
    1504  }
    1505  
    1506  static int
    1507  opendb (const char *name)
    1508  {
    1509    int fd = open (name, O_RDONLY
    1510  #if defined O_LARGEFILE
    1511                  |O_LARGEFILE
    1512  #endif
    1513                  );
    1514    if (fd >= 0)
    1515      {
    1516        /* Make sure it won't survive an exec */
    1517        if (0 != fcntl (fd, F_SETFD, FD_CLOEXEC))
    1518          {
    1519            close (fd);
    1520            fd = -1;
    1521          }
    1522      }
    1523    return fd;
    1524  }
    1525  
    1526  static void
    1527  cleanup_quote_opts (void)
    1528  {
    1529    free (quote_opts);
    1530  }
    1531  
    1532  
    1533  static int
    1534  dolocate (int argc, char **argv, int secure_db_fd)
    1535  {
    1536    char *path_element = NULL;
    1537    size_t path_element_pos, path_element_len;
    1538    const char *user_selected_locate_path;
    1539    const char *db_name;
    1540    const char *path_separators = ":";
    1541    unsigned long int found = 0uL;
    1542    int ignore_case = 0;
    1543    int print = 0;
    1544    int just_count = 0;
    1545    int basename_only = 0;
    1546    int use_limit = 0;
    1547    int regex = 0;
    1548    int regex_options = RE_SYNTAX_EMACS;
    1549    int stats = 0;
    1550    int op_and = 0;
    1551    FILE *fp;
    1552    bool did_stdin = false;       /* Set to prevent rereading stdin. */
    1553  
    1554    if (argv[0])
    1555      set_program_name (argv[0]);
    1556    else
    1557      set_program_name ("locate");
    1558  
    1559  #ifdef HAVE_SETLOCALE
    1560    setlocale (LC_ALL, "");
    1561  #endif
    1562    bindtextdomain (PACKAGE, LOCALEDIR);
    1563    textdomain (PACKAGE);
    1564  
    1565    quote_opts = clone_quoting_options (NULL);
    1566    if (atexit (close_stdout) || atexit (cleanup_quote_opts))
    1567      {
    1568        die (EXIT_FAILURE, errno, _("The atexit library function failed"));
    1569      }
    1570  
    1571    limits.limit = 0;
    1572    limits.items_accepted = 0;
    1573  
    1574    print_quoted_filename = true;
    1575  
    1576    /* We cannot simultaneously trust $LOCATE_PATH and use the
    1577     * setuid-access-controlled database, since that could cause a leak
    1578     * of private data.
    1579     */
    1580    user_selected_locate_path = getenv ("LOCATE_PATH");
    1581  
    1582    check_existence = ACCEPT_EITHER;
    1583  
    1584    for (;;)
    1585      {
    1586        int opti = -1;
    1587        int optc = getopt_long (argc, argv, "Abcd:eEil:prsm0SwHPL", longopts,
    1588                                &opti);
    1589        if (optc == -1)
    1590          break;
    1591  
    1592        switch (optc)
    1593          {
    1594          case '0':
    1595            separator = 0;
    1596            print_quoted_filename = false; /* print filename 'raw'. */
    1597            break;
    1598  
    1599          case 'A':
    1600            op_and = 1;
    1601            break;
    1602  
    1603          case 'b':
    1604            basename_only = 1;
    1605            break;
    1606  
    1607          case 'c':
    1608            just_count = 1;
    1609            break;
    1610  
    1611          case 'd':
    1612            user_selected_locate_path = optarg;
    1613            assert (optarg != NULL);
    1614            break;
    1615  
    1616          case 'e':
    1617            check_existence = ACCEPT_EXISTING;
    1618            break;
    1619  
    1620          case 'E':
    1621            check_existence = ACCEPT_NON_EXISTING;
    1622            break;
    1623  
    1624          case 'i':
    1625            ignore_case = 1;
    1626            break;
    1627  
    1628          case 'h':
    1629            usage (EXIT_SUCCESS);
    1630  
    1631          case MAX_DB_AGE:
    1632            /* XXX: nothing in the test suite for this option. */
    1633            set_max_db_age (optarg);
    1634            break;
    1635  
    1636          case 'p':
    1637            print = 1;
    1638            break;
    1639  
    1640          case 'v':
    1641            display_findutils_version ("locate");
    1642            return 0;
    1643  
    1644          case 'w':
    1645            basename_only = 0;
    1646            break;
    1647  
    1648          case 'r':
    1649            regex = 1;
    1650            break;
    1651  
    1652          case REGEXTYPE_OPTION:
    1653            regex_options = get_regex_type (optarg);
    1654            break;
    1655  
    1656          case 'S':
    1657            stats = 1;
    1658            break;
    1659  
    1660          case 'L':
    1661            follow_symlinks = 1;
    1662            break;
    1663  
    1664            /* In find, -P and -H differ in the way they handle paths
    1665             * given on the command line.  This is not relevant for
    1666             * locate, but the -H option is supported because it is
    1667             * probably more intuitive to do so.
    1668             */
    1669          case 'P':
    1670          case 'H':
    1671            follow_symlinks = 0;
    1672            break;
    1673  
    1674          case 'l':
    1675            {
    1676              char *end = optarg;
    1677              strtol_error err = xstrtoumax (optarg, &end, 10, &limits.limit,
    1678                                             NULL);
    1679              if (LONGINT_OK != err)
    1680                xstrtol_fatal (err, opti, optc, longopts, optarg);
    1681              use_limit = 1;
    1682            }
    1683            break;
    1684  
    1685          case 's':                       /* use stdio */
    1686          case 'm':                       /* use mmap  */
    1687            /* These options are implemented simply for
    1688             * compatibility with FreeBSD
    1689             */
    1690            break;
    1691  
    1692          default:
    1693            usage (EXIT_FAILURE);
    1694          }
    1695      }
    1696  
    1697  
    1698    /* If the user gave the -d option or set LOCATE_PATH,
    1699     * relinquish access to the secure database.
    1700     */
    1701    if (user_selected_locate_path)
    1702      {
    1703        if (secure_db_fd >= 0)
    1704          {
    1705            close (secure_db_fd);
    1706            secure_db_fd = -1;
    1707          }
    1708      }
    1709  
    1710    if (!just_count && !stats)
    1711      print = 1;
    1712  
    1713    if (stats)
    1714      {
    1715        if (optind == argc)
    1716          use_limit = 0;
    1717      }
    1718    else
    1719      {
    1720        if (!just_count && optind == argc)
    1721          {
    1722            error (0, 0, _("pattern argument expected"));
    1723            usage (EXIT_FAILURE);
    1724          }
    1725      }
    1726  
    1727    if (1 == isatty (STDOUT_FILENO))
    1728      stdout_is_a_tty = true;
    1729    else
    1730      stdout_is_a_tty = false;
    1731  
    1732    if (user_selected_locate_path)
    1733      {
    1734        splitstring (user_selected_locate_path, path_separators, true,
    1735                     &path_element_pos, &path_element_len);
    1736      }
    1737  
    1738    /* Bail out early if limit already reached. */
    1739    while (!use_limit || limits.limit > limits.items_accepted)
    1740      {
    1741        struct stat st;
    1742        struct timespec database_mtime;
    1743        int have_mtime;
    1744        int fd;
    1745        off_t filesize;
    1746  
    1747        statistics.compressed_bytes =
    1748        statistics.total_filename_count =
    1749        statistics.total_filename_length =
    1750        statistics.whitespace_count =
    1751        statistics.newline_count =
    1752        statistics.highbit_filename_count = 0u;
    1753  
    1754        if (user_selected_locate_path)
    1755          {
    1756            /* Take the next element from the list of databases */
    1757            if (1 == path_element_len
    1758                && '-' == user_selected_locate_path[path_element_pos])
    1759              {
    1760                if (did_stdin)
    1761                  {
    1762                    error (0, 0,
    1763                           _("warning: the locate database can only be read from stdin once."));
    1764                    return 0;
    1765                  }
    1766                else
    1767                  {
    1768                    db_name = "<stdin>";
    1769                    fd = 0;
    1770                    did_stdin = true;
    1771                  }
    1772              }
    1773            else
    1774              {
    1775                if (0 == path_element_len
    1776                    || (1 == path_element_len
    1777                        && '.' == user_selected_locate_path[path_element_pos]))
    1778                  {
    1779                    db_name = LOCATE_DB;
    1780                  }
    1781                else
    1782                  {
    1783                    path_element = strndup (&user_selected_locate_path[path_element_pos],
    1784                                            path_element_len);
    1785                    db_name = path_element;
    1786                  }
    1787  
    1788                /* open the database */
    1789                fd = opendb (db_name);
    1790                if (fd < 0)
    1791                  {
    1792                    error (0, errno, "%s",
    1793                           quotearg_n_style (0, locale_quoting_style, db_name));
    1794                    return 0;
    1795                  }
    1796              }
    1797          }
    1798        else
    1799          {
    1800            if (-1 == secure_db_fd)
    1801              {
    1802                /* Already searched the database, it's time to exit the loop */
    1803                break;
    1804              }
    1805            else
    1806              {
    1807                db_name = selected_secure_db;
    1808                fd = secure_db_fd;
    1809                secure_db_fd = -1;
    1810              }
    1811          }
    1812  
    1813        /* Check the database to see if it is old. */
    1814        if (fstat (fd, &st))
    1815          {
    1816            error (0, errno, "%s",
    1817                   quotearg_n_style (0, locale_quoting_style, db_name));
    1818            /* continue anyway */
    1819            filesize = (off_t)0;
    1820            have_mtime = 0;
    1821          }
    1822        else
    1823          {
    1824            time_t now;
    1825  
    1826            filesize = st.st_size;
    1827            database_mtime = get_stat_mtime(&st);
    1828            have_mtime = 1;
    1829  
    1830            if ((time_t)-1 == time (&now))
    1831              {
    1832                /* If we can't tell the time, we don't know how old the
    1833                 * database is.  But since the message is just advisory,
    1834                 * we continue anyway.
    1835                 */
    1836                error (0, errno, _("time system call failed"));
    1837              }
    1838            else
    1839              {
    1840                double age          = difftime (now, st.st_mtime);
    1841                double warn_seconds = SECONDS_PER_UNIT * warn_number_units;
    1842                if (age > warn_seconds)
    1843                  {
    1844                    /* For example:
    1845                       warning: database `fred' is more than 8 days old (actual age is 10 days)*/
    1846                    error (0, 0,
    1847                           _("warning: database %s is more than %u %s old (actual age is %.1f %s)"),
    1848                           quotearg_n_style (0,  locale_quoting_style, db_name),
    1849                           warn_number_units,              _(warn_name_units),
    1850                           (age/(double)SECONDS_PER_UNIT), _(warn_name_units));
    1851                  }
    1852              }
    1853          }
    1854  
    1855        fp = fdopen (fd, "r");
    1856        if (NULL == fp)
    1857          {
    1858            error (0, errno, "%s",
    1859                   quotearg_n_style (0, locale_quoting_style, db_name));
    1860            return 0;
    1861          }
    1862  
    1863        /* Search this database for all patterns simultaneously */
    1864        found = search_one_database (argc - optind, &argv[optind],
    1865                                     db_name, fp, filesize,
    1866                                     have_mtime ? (&database_mtime) : NULL,
    1867                                     ignore_case, print, basename_only,
    1868                                     use_limit, &limits, stats,
    1869                                     op_and, regex, regex_options);
    1870  
    1871        /* Close the databsase (even if it is stdin) */
    1872        if (fclose (fp) == EOF)
    1873          {
    1874            error (0, errno, "%s",
    1875                   quotearg_n_style (0, locale_quoting_style, db_name));
    1876            return 0;
    1877          }
    1878         if (path_element)
    1879          {
    1880            free (path_element);
    1881            path_element = NULL;
    1882          }
    1883  
    1884         if (!user_selected_locate_path)
    1885           {
    1886             /* We're not actually iterating through the values in
    1887                $LOCATE_PATH so we don't want to check for the next
    1888                element in user_selected_locate_path (since we manually set db_name =
    1889                LOCATE_DB without using user_selected_locate_path). */
    1890             break;
    1891           }
    1892         else if (!splitstring (user_selected_locate_path, path_separators, false,
    1893                                &path_element_pos, &path_element_len))
    1894           {
    1895             break;
    1896           }
    1897      }
    1898  
    1899    if (just_count)
    1900      {
    1901        printf ("%lu\n", found);
    1902      }
    1903  
    1904    if (found || (use_limit && (limits.limit==0)) || stats )
    1905      return 0;
    1906    else
    1907      return 1;
    1908  }
    1909  
    1910  static int
    1911  open_secure_db (void)
    1912  {
    1913    int fd, i;
    1914  
    1915    const char * secure_db_list[] =
    1916      {
    1917        LOCATE_DB,
    1918        "/var/lib/slocate/slocate.db",
    1919        NULL
    1920      };
    1921    for (i=0; secure_db_list[i]; ++i)
    1922      {
    1923        fd = opendb (secure_db_list[i]);
    1924        if (fd >= 0)
    1925          {
    1926            selected_secure_db = secure_db_list[i];
    1927            return fd;
    1928          }
    1929      }
    1930    return -1;
    1931  }
    1932  
    1933  int
    1934  main (int argc, char **argv)
    1935  {
    1936    int dbfd = open_secure_db ();
    1937    drop_privs ();
    1938  
    1939    return dolocate (argc, argv, dbfd);
    1940  }