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 }