1 |
pebenito 08/01/30 14:02:05 |
2 |
|
3 |
Added: findutils-4.3.12-selinux.diff |
4 |
Log: |
5 |
sys-apps/findutils: update selinux patch. |
6 |
(Portage version: 2.1.4) |
7 |
|
8 |
Revision Changes Path |
9 |
1.1 sys-apps/findutils/files/findutils-4.3.12-selinux.diff |
10 |
|
11 |
file : http://sources.gentoo.org/viewcvs.py/gentoo-x86/sys-apps/findutils/files/findutils-4.3.12-selinux.diff?rev=1.1&view=markup |
12 |
plain: http://sources.gentoo.org/viewcvs.py/gentoo-x86/sys-apps/findutils/files/findutils-4.3.12-selinux.diff?rev=1.1&content-type=text/plain |
13 |
|
14 |
Index: findutils-4.3.12-selinux.diff |
15 |
=================================================================== |
16 |
diff -purN findutils-4.3.12.orig/find/Makefile.am findutils-4.3.12/find/Makefile.am |
17 |
--- findutils-4.3.12.orig/find/Makefile.am 2007-07-22 08:29:31.000000000 -0400 |
18 |
+++ findutils-4.3.12/find/Makefile.am 2008-01-30 08:46:05.750843391 -0500 |
19 |
@@ -2,6 +2,7 @@ AUTOMAKE_OPTIONS = std-options |
20 |
localedir = $(datadir)/locale |
21 |
# noinst_PROGRAMS = regexprops |
22 |
# regexprops_SOURCES = regexprops.c |
23 |
+DEFS = @DEFS@ -I. -I$(srcdir) -I.. -DWITH_SELINUX |
24 |
|
25 |
noinst_LIBRARIES = libfindtools.a |
26 |
libfindtools_a_SOURCES = finddata.c fstype.c parser.c pred.c tree.c util.c |
27 |
@@ -26,7 +27,7 @@ endif |
28 |
|
29 |
EXTRA_DIST = defs.h $(man_MANS) |
30 |
INCLUDES = -I../gnulib/lib -I$(top_srcdir)/lib -I$(top_srcdir)/gnulib/lib -I../intl -DLOCALEDIR=\"$(localedir)\" |
31 |
-LDADD = ./libfindtools.a ../lib/libfind.a ../gnulib/lib/libgnulib.a @INTLLIBS@ @LIB_CLOCK_GETTIME@ @FINDLIBS@ |
32 |
+LDADD = ./libfindtools.a ../lib/libfind.a ../gnulib/lib/libgnulib.a @INTLLIBS@ @LIB_CLOCK_GETTIME@ @FINDLIBS@ -lselinux |
33 |
man_MANS = find.1 |
34 |
SUBDIRS = . testsuite |
35 |
|
36 |
diff -purN findutils-4.3.12.orig/find/Makefile.in findutils-4.3.12/find/Makefile.in |
37 |
--- findutils-4.3.12.orig/find/Makefile.in 2007-12-19 16:27:24.000000000 -0500 |
38 |
+++ findutils-4.3.12/find/Makefile.in 2008-01-30 08:46:05.750843391 -0500 |
39 |
@@ -275,7 +275,7 @@ CPP = @CPP@ |
40 |
CPPFLAGS = @CPPFLAGS@ |
41 |
CYGPATH_W = @CYGPATH_W@ |
42 |
DEFAULT_ARG_SIZE = @DEFAULT_ARG_SIZE@ |
43 |
-DEFS = @DEFS@ |
44 |
+DEFS = @DEFS@ -I. -I$(srcdir) -I.. -DWITH_SELINUX |
45 |
DEPDIR = @DEPDIR@ |
46 |
DIRENT_H = @DIRENT_H@ |
47 |
ECHO_C = @ECHO_C@ |
48 |
@@ -601,7 +601,7 @@ libfindtools_a_SOURCES = finddata.c fsty |
49 |
@WITH_FTS_TRUE@oldfind_SOURCES = find.c |
50 |
@WITH_FTS_FALSE@ftsfind_SOURCES = ftsfind.c |
51 |
EXTRA_DIST = defs.h $(man_MANS) |
52 |
-LDADD = ./libfindtools.a ../lib/libfind.a ../gnulib/lib/libgnulib.a @INTLLIBS@ @LIB_CLOCK_GETTIME@ @FINDLIBS@ |
53 |
+LDADD = ./libfindtools.a ../lib/libfind.a ../gnulib/lib/libgnulib.a @INTLLIBS@ @LIB_CLOCK_GETTIME@ @FINDLIBS@ -lselinux |
54 |
man_MANS = find.1 |
55 |
SUBDIRS = . testsuite |
56 |
all: all-recursive |
57 |
diff -purN findutils-4.3.12.orig/find/defs.h findutils-4.3.12/find/defs.h |
58 |
--- findutils-4.3.12.orig/find/defs.h 2007-11-30 05:19:58.000000000 -0500 |
59 |
+++ findutils-4.3.12/find/defs.h 2008-01-30 08:46:05.754843619 -0500 |
60 |
@@ -90,6 +90,9 @@ int get_statinfo PARAMS((const char *pat |
61 |
#define MODE_RWX (S_IXUSR | S_IXGRP | S_IXOTH | MODE_RW) |
62 |
#define MODE_ALL (S_ISUID | S_ISGID | S_ISVTX | MODE_RWX) |
63 |
|
64 |
+#ifdef WITH_SELINUX |
65 |
+#include <selinux/selinux.h> |
66 |
+#endif /*WITH_SELINUX*/ |
67 |
|
68 |
struct predicate; |
69 |
struct options; |
70 |
@@ -314,6 +317,9 @@ struct predicate |
71 |
struct samefile_file_id samefileid; /* samefile */ |
72 |
mode_t type; /* type */ |
73 |
struct format_val printf_vec; /* printf fprintf fprint ls fls print0 fprint0 print */ |
74 |
+#ifdef WITH_SELINUX |
75 |
+ security_context_t scontext; /* scontext */ |
76 |
+#endif /*WITH_SELINUX*/ |
77 |
} args; |
78 |
|
79 |
/* The next predicate in the user input sequence, |
80 |
@@ -458,7 +464,9 @@ PREDICATEFUNCTION pred_used; |
81 |
PREDICATEFUNCTION pred_user; |
82 |
PREDICATEFUNCTION pred_writable; |
83 |
PREDICATEFUNCTION pred_xtype; |
84 |
- |
85 |
+#ifdef WITH_SELINUX |
86 |
+PREDICATEFUNCTION pred_scontext; |
87 |
+#endif /* WITH_SELINUX */ |
88 |
|
89 |
|
90 |
int launch PARAMS((const struct buildcmd_control *ctl, |
91 |
@@ -605,10 +613,13 @@ struct options |
92 |
*/ |
93 |
unsigned short optimisation_level; |
94 |
|
95 |
- |
96 |
/* How should we quote filenames in error messages and so forth? |
97 |
*/ |
98 |
enum quoting_style err_quoting_style; |
99 |
+ |
100 |
+#ifdef WITH_SELINUX |
101 |
+ int (*x_getfilecon)(); |
102 |
+#endif /* WITH_SELINUX */ |
103 |
}; |
104 |
extern struct options options; |
105 |
|
106 |
diff -purN findutils-4.3.12.orig/find/find.1 findutils-4.3.12/find/find.1 |
107 |
--- findutils-4.3.12.orig/find/find.1 2007-12-19 14:53:14.000000000 -0500 |
108 |
+++ findutils-4.3.12/find/find.1 2008-01-30 08:49:54.983906639 -0500 |
109 |
@@ -935,6 +935,10 @@ checks the type of the file that |
110 |
.B \-type |
111 |
does not check. |
112 |
|
113 |
+.IP "\-context \fIscontext\fR" |
114 |
+.IP "\--context \fIscontext\fR" |
115 |
+(SELinux only) File has the security context \fIscontext\fR. |
116 |
+ |
117 |
.SS ACTIONS |
118 |
.IP "\-delete\fR" |
119 |
Delete files; true if removal succeeded. If the removal failed, an |
120 |
@@ -1340,6 +1344,8 @@ File's type (like in |
121 |
U=unknown type (shouldn't happen) |
122 |
.IP %Y |
123 |
File's type (like %y), plus follow symlinks: L=loop, N=nonexistent |
124 |
+.IP %Z |
125 |
+(SELinux only) file's security context. |
126 |
.PP |
127 |
A `%' character followed by any other character is discarded, but the |
128 |
other character is printed (don't rely on this, as further format |
129 |
diff -purN findutils-4.3.12.orig/find/find.1.orig findutils-4.3.12/find/find.1.orig |
130 |
--- findutils-4.3.12.orig/find/find.1.orig 1969-12-31 19:00:00.000000000 -0500 |
131 |
+++ findutils-4.3.12/find/find.1.orig 2007-12-19 14:53:14.000000000 -0500 |
132 |
@@ -0,0 +1,2027 @@ |
133 |
+.TH FIND 1 \" -*- nroff -*- |
134 |
+.SH NAME |
135 |
+find \- search for files in a directory hierarchy |
136 |
+.SH SYNOPSIS |
137 |
+.B find |
138 |
+[\-H] [\-L] [\-P] [\-D debugopts] [\-Olevel] [path...] [expression] |
139 |
+.SH DESCRIPTION |
140 |
+This manual page |
141 |
+documents the GNU version of |
142 |
+.BR find . |
143 |
+GNU |
144 |
+.B find |
145 |
+searches the directory tree rooted at each given file name by |
146 |
+evaluating the given expression from left to right, according to the |
147 |
+rules of precedence (see section OPERATORS), until the outcome is |
148 |
+known (the left hand side is false for \fIand\fR operations, true for |
149 |
+\fIor\fR), at which point |
150 |
+.B find |
151 |
+moves on to the next file name. |
152 |
+.PP |
153 |
+If you are using |
154 |
+.B find |
155 |
+in an environment where security is important (for example if you are |
156 |
+using it to seach directories that are writable by other users), you |
157 |
+should read the "Security Considerations" chapter of the findutils |
158 |
+documentation, which is called \fBFinding Files\fP and comes with |
159 |
+findutils. That document also includes a lot more detail |
160 |
+and discussion than this manual page, so you may find it a more useful |
161 |
+source of information. |
162 |
+.SH OPTIONS |
163 |
+The |
164 |
+.BR \-H , |
165 |
+.B \-L |
166 |
+and |
167 |
+.B \-P |
168 |
+options control the treatment of symbolic |
169 |
+links. Command-line arguments following these are taken to be names |
170 |
+of files or directories to be examined, up to the first argument that |
171 |
+begins with `\-', or the argument `(' or `!'. That argument and any |
172 |
+following arguments are taken to be the expression describing what is |
173 |
+to be searched for. If no paths are given, the current directory is |
174 |
+used. If no expression is given, the expression |
175 |
+.B \-print |
176 |
+is used |
177 |
+(but you should probably consider using |
178 |
+.B \-print0 |
179 |
+instead, anyway). |
180 |
+.PP |
181 |
+This manual page talks about `options' within the expression list. |
182 |
+These options control the behaviour of |
183 |
+.B find |
184 |
+but are specified immediately after the last path name. The five |
185 |
+`real' options |
186 |
+.BR \-H , |
187 |
+.BR \-L , |
188 |
+.BR \-P , |
189 |
+.B \-D |
190 |
+and |
191 |
+.B \-O |
192 |
+must appear before |
193 |
+the first path name, if at all. A double dash |
194 |
+.B \-\- |
195 |
+can also be used |
196 |
+to signal that any remaining arguments are not options (though |
197 |
+ensuring that all start points begin with either `./' or `/' is |
198 |
+generally safer if you use wildcards in the list of start points). |
199 |
+.IP \-P |
200 |
+Never follow symbolic links. This is the default behaviour. When |
201 |
+.B find |
202 |
+examines or prints information a file, and the file is a symbolic |
203 |
+link, the information used shall be taken from the properties of the |
204 |
+symbolic link itself. |
205 |
+ |
206 |
+.IP \-L |
207 |
+Follow symbolic links. When |
208 |
+.B find |
209 |
+examines or prints information about files, the information used shall |
210 |
+be taken from the properties of the file to which the link points, not |
211 |
+from the link itself (unless it is a broken symbolic link or |
212 |
+.B find |
213 |
+is unable to examine the file to which the link points). Use of this |
214 |
+option implies |
215 |
+.BR \-noleaf . |
216 |
+If you later use the |
217 |
+.B \-P |
218 |
+option, |
219 |
+.B \-noleaf |
220 |
+will still be in effect. If |
221 |
+.B \-L |
222 |
+is in effect and |
223 |
+.B find |
224 |
+discovers a symbolic link to a subdirectory during its search, |
225 |
+the subdirectory pointed to by the symbolic link will be searched. |
226 |
+.IP |
227 |
+When the |
228 |
+.B \-L |
229 |
+option is in effect, the |
230 |
+.B \-type |
231 |
+predicate will always |
232 |
+match against the type of the file that a symbolic link points to |
233 |
+rather than the link itself (unless the symbolic link is broken). |
234 |
+Using |
235 |
+.B \-L |
236 |
+causes the |
237 |
+.B \-lname |
238 |
+and |
239 |
+.B \-ilname |
240 |
+predicates always to return |
241 |
+false. |
242 |
+ |
243 |
+.IP \-H |
244 |
+Do not follow symbolic links, except while processing the command |
245 |
+line arguments. When |
246 |
+.B find |
247 |
+examines or prints information about files, the information used |
248 |
+shall be taken from the properties of the symbolic link itself. The |
249 |
+only exception to this behaviour is when a file specified on the |
250 |
+command line is a symbolic link, and the link can be resolved. For |
251 |
+that situation, the information used is taken from whatever the link |
252 |
+points to (that is, the link is followed). The information about the |
253 |
+link itself is used as a fallback if the file pointed to by the |
254 |
+symbolic link cannot be examined. If |
255 |
+.B \-H |
256 |
+is in effect and one of the |
257 |
+paths specified on the command line is a symbolic link to a directory, |
258 |
+the contents of that directory will be examined (though of course |
259 |
+\-maxdepth 0 would prevent this). |
260 |
+.P |
261 |
+If more than one of |
262 |
+.BR \-H , |
263 |
+.B \-L |
264 |
+and |
265 |
+.B \-P |
266 |
+is specified, each overrides the |
267 |
+others; the last one appearing on the command line takes effect. |
268 |
+Since it is the default, the |
269 |
+.B \-P |
270 |
+option should be considered to be in |
271 |
+effect unless either |
272 |
+.B \-H |
273 |
+or |
274 |
+.B \-L |
275 |
+is specified. |
276 |
+ |
277 |
+GNU |
278 |
+.B find |
279 |
+frequently stats files during the processing of the command line |
280 |
+itself, before any searching has begun. These options also affect how |
281 |
+those arguments are processed. Specifically, there are a number of |
282 |
+tests that compare files listed on the command line against a file we |
283 |
+are currently considering. In each case, the file specified on the |
284 |
+command line will have been examined and some of its properties will |
285 |
+have been saved. If the named file is in fact a symbolic link, and |
286 |
+the |
287 |
+.B \-P |
288 |
+option is in effect (or if neither |
289 |
+.B \-H |
290 |
+nor |
291 |
+.B \-L |
292 |
+were specified), the information used for the comparison will be taken from |
293 |
+the properties of the symbolic link. Otherwise, it will be taken from |
294 |
+the properties of the file the link points to. If |
295 |
+.B find |
296 |
+cannot follow the link (for example because it has insufficient |
297 |
+privileges or the link points to a nonexistent file) the properties of |
298 |
+the link itself will be used. |
299 |
+.P |
300 |
+When the |
301 |
+.B \-H |
302 |
+or |
303 |
+.B \-L options are in effect, any symbolic links listed |
304 |
+as the argument of |
305 |
+.B \-newer |
306 |
+will be dereferenced, and the timestamp |
307 |
+will be taken from the file to which the symbolic link points. The |
308 |
+same consideration applies to |
309 |
+.BR \-newerXY , |
310 |
+.B \-anewer |
311 |
+and |
312 |
+.BR \-cnewer . |
313 |
+ |
314 |
+The |
315 |
+.B \-follow |
316 |
+option has a similar effect to |
317 |
+.BR \-L , |
318 |
+though it takes |
319 |
+effect at the point where it appears (that is, if |
320 |
+.B \-L |
321 |
+is not used but |
322 |
+.B \-follow |
323 |
+is, any symbolic links appearing after |
324 |
+.B \-follow |
325 |
+on the |
326 |
+command line will be dereferenced, and those before it will not). |
327 |
+ |
328 |
+.IP "\-D debugoptions" |
329 |
+Print diagnostic information; this can be helpful to diagnose problems |
330 |
+with why |
331 |
+.B find |
332 |
+is not doing what you want. The list of debug options should be comma |
333 |
+separated. Compatibility of the debug options is not guaranteed |
334 |
+between releases of findutils. For a complete list of valid debug |
335 |
+options, see the output of |
336 |
+.B find \-D |
337 |
+.BR help . |
338 |
+Valid debug options include |
339 |
+.RS |
340 |
+.IP help |
341 |
+Explain the debugging options |
342 |
+.IP tree |
343 |
+Show the expression tree in its original and optimised form. |
344 |
+.IP stat |
345 |
+Print messages as files are examined with the |
346 |
+.B stat |
347 |
+and |
348 |
+.B lstat |
349 |
+system calls. The |
350 |
+.B find |
351 |
+program tries to minimise such calls. |
352 |
+.IP opt |
353 |
+Prints diagnostic information relating to the optimisation of the |
354 |
+expression tree; see the \-O option. |
355 |
+.IP rates |
356 |
+Prints a summary indicating how often each predicate succeeded or |
357 |
+failed. |
358 |
+.RE |
359 |
+.IP \-Olevel |
360 |
+Enables query optimisation. The |
361 |
+.B find |
362 |
+program reorders tests to speed up execution while preserving the |
363 |
+overall effect; that is, predicates with side effects are not |
364 |
+reordered relative to each other. The optimisations performed at each |
365 |
+optimisation level are as follows. |
366 |
+.RS |
367 |
+.IP 0 |
368 |
+Equivalent to optimisation level 1. |
369 |
+.IP 1 |
370 |
+This is the default optimisation level and corresponds to the |
371 |
+traditional behaviour. Expressions are reordered so that tests based |
372 |
+only on the names of files (for example |
373 |
+.B \-name |
374 |
+and |
375 |
+.BR \-regex ) |
376 |
+are performed first. |
377 |
+.IP 2 |
378 |
+Any |
379 |
+.B \-type |
380 |
+or |
381 |
+.B \-xtype |
382 |
+tests are performed after any tests based only on the names of files, |
383 |
+but before any tests that require information from the inode. On many |
384 |
+modern versions of Unix, file types are returned by |
385 |
+.B readdir() |
386 |
+and so these predicates are faster to evaluate than predicates which |
387 |
+need to stat the file first. |
388 |
+.IP 3 |
389 |
+At this optimisation level, the full cost-based query optimiser is |
390 |
+enabled. The order of tests is modified so that cheap (i.e. fast) |
391 |
+tests are performed first and more expensive ones are performed later, |
392 |
+if necessary. Within each cost band, predicates are evaluated earlier |
393 |
+or later according to whether they are likely to succeed or not. For |
394 |
+.BR \-o , |
395 |
+predicates which are likely to succeed are evaluated earlier, and for |
396 |
+.BR \-a , |
397 |
+predicates which are likely to fail are evaluated earlier. |
398 |
+.RE |
399 |
+.IP |
400 |
+The cost-based optimiser has a fixed idea of how likely any given test |
401 |
+is to succeed. In some cases the probability takes account of the |
402 |
+specific nature of the test (for example, |
403 |
+.B \-type f |
404 |
+is assumed to be more likely to succeed than |
405 |
+.BR "\-type c" ). |
406 |
+The cost-based optimiser is currently being evaluated. If it does |
407 |
+not actually improve the performance of |
408 |
+.BR find , |
409 |
+it will be removed again. Conversely, optimisations that prove to be |
410 |
+reliable, robust and effective may be enabled at lower optimisation |
411 |
+levels over time. However, the default behaviour (i.e. optimisation |
412 |
+level 1) will not be changed in the 4.3.x release series. The |
413 |
+findutils test suite runs all the tests on |
414 |
+.B find |
415 |
+at each optimisation level and ensures that the result is the same. |
416 |
+.P |
417 |
+.SH EXPRESSIONS |
418 |
+The expression is made up of options (which affect overall operation |
419 |
+rather than the processing of a specific file, and always return |
420 |
+true), tests (which return a true or false value), and actions (which |
421 |
+have side effects and return a true or false value), all separated by |
422 |
+operators. |
423 |
+.B \-and |
424 |
+is assumed where the operator is omitted. |
425 |
+ |
426 |
+If the expression contains no actions other than |
427 |
+.BR \-prune , |
428 |
+.B \-print |
429 |
+is |
430 |
+performed on all files for which the expression is true. |
431 |
+ |
432 |
+.SS OPTIONS |
433 |
+.P |
434 |
+All options always return true. Except for |
435 |
+.BR \-daystart , |
436 |
+.B \-follow |
437 |
+and |
438 |
+.BR \-regextype , |
439 |
+the options affect all tests, including tests specified |
440 |
+before the option. This is because the options are processed when the |
441 |
+command line is parsed, while the tests don't do anything until files |
442 |
+are examined. The |
443 |
+.BR \-daystart , |
444 |
+.B \-follow |
445 |
+and |
446 |
+.B \-regextype |
447 |
+options are different in this respect, and have an effect only on tests which |
448 |
+appear later in the command line. Therefore, for clarity, it is best |
449 |
+to place them at the beginning of the expression. A warning is issued |
450 |
+if you don't do this. |
451 |
+ |
452 |
+.IP \-d |
453 |
+A synonym for \-depth, for compatibility with FreeBSD, NetBSD, MacOS X and OpenBSD. |
454 |
+ |
455 |
+.IP \-daystart |
456 |
+Measure times (for |
457 |
+.BR \-amin , |
458 |
+.BR \-atime , |
459 |
+.BR \-cmin , |
460 |
+.BR \-ctime , |
461 |
+.BR \-mmin , |
462 |
+and |
463 |
+.BR \-mtime ) |
464 |
+from the beginning of today rather than from 24 hours ago. This |
465 |
+option only affects tests which appear later on the command line. |
466 |
+ |
467 |
+.IP \-depth |
468 |
+Process each directory's contents before the directory itself. The |
469 |
+\-delete action also implies |
470 |
+.BR \-depth . |
471 |
+ |
472 |
+.IP \-follow |
473 |
+Deprecated; use the |
474 |
+.B \-L |
475 |
+option instead. Dereference symbolic links. |
476 |
+Implies |
477 |
+.BR \-noleaf . |
478 |
+The |
479 |
+.B \-follow |
480 |
+option affects only those tests which |
481 |
+appear after it on the command line. Unless the |
482 |
+.B \-H |
483 |
+or |
484 |
+.B \-L |
485 |
+option has |
486 |
+been specified, the position of the |
487 |
+.B \-follow |
488 |
+option changes the behaviour of the |
489 |
+.B \-newer |
490 |
+predicate; any files listed as the argument |
491 |
+of |
492 |
+.B \-newer |
493 |
+will be dereferenced if they are symbolic links. The same |
494 |
+consideration applies to |
495 |
+.BR \-newerXY , |
496 |
+.B \-anewer |
497 |
+and |
498 |
+.BR \-cnewer . |
499 |
+Similarly, the |
500 |
+.B \-type |
501 |
+predicate will always match against the type of the file |
502 |
+that a symbolic link points to rather than the link itself. Using |
503 |
+.B \-follow |
504 |
+causes the |
505 |
+.B \-lname and |
506 |
+.B \-ilname |
507 |
+predicates always to return false. |
508 |
+ |
509 |
+.IP "\-help, \-\-help" |
510 |
+Print a summary of the command-line usage of |
511 |
+.B find |
512 |
+and exit. |
513 |
+ |
514 |
+.IP \-ignore_readdir_race |
515 |
+Normally, \fBfind\fR will emit an error message when it fails to stat a file. |
516 |
+If you give this option and a file is deleted between the time \fBfind\fR |
517 |
+reads the name of the file from the directory and the time it tries to stat |
518 |
+the file, no error message will be issued. This also applies to files |
519 |
+or directories whose names are given on the command line. This option takes |
520 |
+effect at the time the command line is read, which means that you cannot search |
521 |
+one part of the filesystem with this option on and part of it with this option |
522 |
+off (if you need to do that, you will need to issue two \fBfind\fR commands |
523 |
+instead, one with the option and one without it). |
524 |
+ |
525 |
+.IP "\-maxdepth \fIlevels\fR" |
526 |
+Descend at most \fIlevels\fR (a non-negative integer) levels of |
527 |
+directories below the command line arguments. |
528 |
+.B \-maxdepth 0 |
529 |
+ means only apply the tests and actions to the command line arguments. |
530 |
+ |
531 |
+.IP "\-mindepth \fIlevels\fR" |
532 |
+Do not apply any tests or actions at levels less than \fIlevels\fR (a |
533 |
+non-negative integer). |
534 |
+.B \-mindepth 1 |
535 |
+means process all files except the command line arguments. |
536 |
+ |
537 |
+.IP \-mount |
538 |
+Don't descend directories on other filesystems. An alternate name for |
539 |
+.BR \-xdev , |
540 |
+for compatibility with some other versions of |
541 |
+.BR find . |
542 |
+ |
543 |
+.IP \-noignore_readdir_race |
544 |
+Turns off the effect of |
545 |
+.BR \-ignore_readdir_race . |
546 |
+ |
547 |
+.IP "\-noleaf" |
548 |
+Do not optimize by assuming that directories contain 2 fewer |
549 |
+subdirectories than their hard link count. This option is needed when |
550 |
+searching filesystems that do not follow the Unix directory-link |
551 |
+convention, such as CD-ROM or MS-DOS filesystems or AFS volume mount |
552 |
+points. Each directory on a normal Unix filesystem has at least 2 |
553 |
+hard links: its name and its `.' entry. Additionally, its |
554 |
+subdirectories (if any) each have a `..' entry linked to that |
555 |
+directory. When |
556 |
+.B find |
557 |
+is examining a directory, after it has statted 2 fewer subdirectories |
558 |
+than the directory's link count, it knows that the rest of the entries |
559 |
+in the directory are non-directories (`leaf' files in the directory |
560 |
+tree). If only the files' names need to be examined, there is no need |
561 |
+to stat them; this gives a significant increase in search speed. |
562 |
+ |
563 |
+.IP "\-regextype \fItype\fR" |
564 |
+Changes the regular expression syntax understood by |
565 |
+.B \-regex |
566 |
+and |
567 |
+.B \-iregex |
568 |
+tests which occur later on the command line. Currently-implemented |
569 |
+types are emacs (this is the default), posix-awk, posix-basic, |
570 |
+posix-egrep and posix-extended. |
571 |
+ |
572 |
+.IP "\-version, \-\-version" |
573 |
+Print the \fBfind\fR version number and exit. |
574 |
+ |
575 |
+.IP "\-warn, \-nowarn" |
576 |
+Turn warning messages on or off. These warnings apply only to the |
577 |
+command line usage, not to any conditions that |
578 |
+.B find |
579 |
+might encounter when it searches directories. The default behaviour |
580 |
+corresponds to |
581 |
+.B \-warn |
582 |
+if standard input is a tty, and to |
583 |
+.B \-nowarn |
584 |
+otherwise. |
585 |
+ |
586 |
+.IP \-xdev |
587 |
+Don't descend directories on other filesystems. |
588 |
+ |
589 |
+.SS TESTS |
590 |
+Some tests, for example |
591 |
+.B \-newerXY |
592 |
+and |
593 |
+.BR -samefile , |
594 |
+allow comparison between the file currently being examined and some |
595 |
+reference file specified on the command line. When these tests are |
596 |
+used, the interpretation of the reference file is determined by the |
597 |
+options |
598 |
+.BR \-H , |
599 |
+.B \-L |
600 |
+and |
601 |
+.B \-P |
602 |
+and any previous |
603 |
+.BR \-follow , |
604 |
+but the reference file is only examined once, at the time the command |
605 |
+line is parsed. If the reference file cannot be examined (for |
606 |
+example, the |
607 |
+.BR stat (2) |
608 |
+system call fails for it), an error message is issued, and |
609 |
+.B find |
610 |
+exits with a nonzero status. |
611 |
+.P |
612 |
+Numeric arguments can be specified as |
613 |
+.IP \fI+n\fP |
614 |
+for greater than |
615 |
+.IR n , |
616 |
+.IP \fI\-n\fP |
617 |
+for less than |
618 |
+.IR n , |
619 |
+.IP \fIn\fP |
620 |
+for exactly |
621 |
+.IR n . |
622 |
+.P |
623 |
+ |
624 |
+.IP "\-amin \fIn\fR" |
625 |
+File was last accessed \fIn\fR minutes ago. |
626 |
+ |
627 |
+.IP "\-anewer \fIfile\fR" |
628 |
+File was last accessed more recently than \fIfile\fR was modified. If |
629 |
+\fIfile\fR is a symbolic link and the |
630 |
+.B \-H |
631 |
+option or the |
632 |
+.B \-L |
633 |
+option is in effect, the access time of the file it points to is |
634 |
+always used. |
635 |
+ |
636 |
+.IP "\-atime \fIn\fR" |
637 |
+File was last accessed \fIn\fR*24 hours ago. |
638 |
+When find figures out how many 24-hour periods ago the file |
639 |
+was last accessed, any fractional part is ignored, so to match |
640 |
+.B \-atime |
641 |
+.BR +1 , |
642 |
+a file has to have been accessed at least |
643 |
+.I two |
644 |
+days ago. |
645 |
+ |
646 |
+.IP "\-cmin \fIn\fR" |
647 |
+File's status was last changed \fIn\fR minutes ago. |
648 |
+ |
649 |
+.IP "\-cnewer \fIfile\fR" |
650 |
+File's status was last changed more recently than \fIfile\fR was |
651 |
+modified. If \fIfile\fR is a symbolic link and the |
652 |
+.B \-H |
653 |
+option or the |
654 |
+.B \-L |
655 |
+option is in effect, the status-change time of the file it points |
656 |
+to is always used. |
657 |
+ |
658 |
+.IP "\-ctime \fIn\fR" |
659 |
+File's status was last changed \fIn\fR*24 hours ago. |
660 |
+See the comments for |
661 |
+.B \-atime |
662 |
+to understand how rounding affects the interpretation of file status |
663 |
+change times. |
664 |
+ |
665 |
+.IP \-empty |
666 |
+File is empty and is either a regular file or a directory. |
667 |
+ |
668 |
+.IP \-executable |
669 |
+Matches files which are executable and directories which are |
670 |
+searchable (in a file name resolution sense). This takes into account |
671 |
+access control lists and other permissions artefacts which the |
672 |
+.B \-perm |
673 |
+test ignores. This test makes use of the |
674 |
+.BR access (2) |
675 |
+system call, and so can be fooled by NFS servers which do UID |
676 |
+mapping (or root-squashing), since many systems implement |
677 |
+.BR access (2) |
678 |
+in the client's kernel and so cannot make use of the UID mapping |
679 |
+information held on the server. Because this test is based only on |
680 |
+the result of the |
681 |
+.BR access (2) |
682 |
+system call, there is no guarantee that a file for which this test |
683 |
+succeeds can actually be executed. |
684 |
+ |
685 |
+.IP \-false |
686 |
+Always false. |
687 |
+ |
688 |
+.IP "\-fstype \fItype\fR" |
689 |
+File is on a filesystem of type \fItype\fR. The valid filesystem |
690 |
+types vary among different versions of Unix; an incomplete list of |
691 |
+filesystem types that are accepted on some version of Unix or another |
692 |
+is: ufs, 4.2, 4.3, nfs, tmp, mfs, S51K, S52K. You can use |
693 |
+.B \-printf |
694 |
+with the %F directive to see the types of your filesystems. |
695 |
+ |
696 |
+.IP "\-gid \fIn\fR" |
697 |
+File's numeric group ID is \fIn\fR. |
698 |
+ |
699 |
+.IP "\-group \fIgname\fR" |
700 |
+File belongs to group \fIgname\fR (numeric group ID allowed). |
701 |
+ |
702 |
+.IP "\-ilname \fIpattern\fR" |
703 |
+Like |
704 |
+.BR \-lname , |
705 |
+but the match is case insensitive. |
706 |
+If the |
707 |
+.B \-L |
708 |
+option or the |
709 |
+.B \-follow |
710 |
+option is in effect, this test returns false unless the symbolic link |
711 |
+is broken. |
712 |
+ |
713 |
+.IP "\-iname \fIpattern\fR" |
714 |
+Like |
715 |
+.BR \-name , |
716 |
+but the match is case insensitive. For example, the |
717 |
+patterns `fo*' and `F??' match the file names `Foo', `FOO', `foo', |
718 |
+`fOo', etc. In these patterns, unlike filename expansion by the |
719 |
+shell, an initial '.' can be matched by `*'. That is, |
720 |
+.B find \-name *bar |
721 |
+will match the file `.foobar'. Please note that you should quote |
722 |
+patterns as a matter of course, otherwise the shell will expand any |
723 |
+wildcard characters in them. |
724 |
+ |
725 |
+.IP "\-inum \fIn\fR" |
726 |
+File has inode number \fIn\fR. It is normally easier to use the |
727 |
+.B \-samefile |
728 |
+test instead. |
729 |
+ |
730 |
+.IP "\-ipath \fIpattern\fR" |
731 |
+Behaves in the same way as |
732 |
+.BR \-iwholename . |
733 |
+This option is deprecated, so please do not use it. |
734 |
+ |
735 |
+.IP "\-iregex \fIpattern\fR" |
736 |
+Like |
737 |
+.BR \-regex , |
738 |
+but the match is case insensitive. |
739 |
+ |
740 |
+.IP "\-iwholename \fIpattern\fR" |
741 |
+Like |
742 |
+.BR \-wholename , |
743 |
+but the match is case insensitive. |
744 |
+ |
745 |
+.IP "\-links \fIn\fR" |
746 |
+File has \fIn\fR links. |
747 |
+ |
748 |
+.IP "\-lname \fIpattern\fR" |
749 |
+File is a symbolic link whose contents match shell pattern |
750 |
+\fIpattern\fR. The metacharacters do not treat `/' or `.' specially. |
751 |
+If the |
752 |
+.B \-L |
753 |
+option or the |
754 |
+.B \-follow |
755 |
+option is in effect, this test returns false unless the symbolic link |
756 |
+is broken. |
757 |
+ |
758 |
+.IP "\-mmin \fIn\fR" |
759 |
+File's data was last modified \fIn\fR minutes ago. |
760 |
+ |
761 |
+.IP "\-mtime \fIn\fR" |
762 |
+File's data was last modified \fIn\fR*24 hours ago. |
763 |
+See the comments for |
764 |
+.B \-atime |
765 |
+to understand how rounding affects the interpretation of file |
766 |
+modification times. |
767 |
+ |
768 |
+.IP "\-name \fIpattern\fR" |
769 |
+Base of file name (the path with the leading directories removed) |
770 |
+matches shell pattern \fIpattern\fR. The metacharacters (`*', `?', |
771 |
+and `[]') match a `.' at the start of the base name (this is a change |
772 |
+in findutils-4.2.2; see section STANDARDS CONFORMANCE below). To ignore a |
773 |
+directory and the files under it, use |
774 |
+.BR \-prune ; |
775 |
+see an example in the |
776 |
+description of |
777 |
+.BR \-path . |
778 |
+Braces are not recognised as being |
779 |
+special, despite the fact that some shells including Bash imbue braces |
780 |
+with a special meaning in shell patterns. The filename matching is |
781 |
+performed with the use of the |
782 |
+.BR fnmatch (3) |
783 |
+library function. Don't forget to enclose the pattern in quotes |
784 |
+in order to protect it from expansion by the shell. |
785 |
+ |
786 |
+.IP "\-newer \fIfile\fR" |
787 |
+File was modified more recently than \fIfile\fR. If \fIfile\fR is a |
788 |
+symbolic link and the |
789 |
+.B \-H |
790 |
+option or the |
791 |
+.B \-L |
792 |
+option is in effect, the |
793 |
+modification time of the file it points to is always used. |
794 |
+ |
795 |
+.IP "\-newerXY \fIreference\fR" |
796 |
+Compares the timestamp of the current file with \fIreference\fR. |
797 |
+The |
798 |
+.I reference |
799 |
+argument is normally the name of a file (and one of its timestamps is |
800 |
+used for the comparison) but it may also be a string describing an |
801 |
+absolute time. |
802 |
+.I X |
803 |
+and |
804 |
+.I Y |
805 |
+are placeholders for other letters, and these letters select which |
806 |
+time belonging to |
807 |
+how |
808 |
+.I reference |
809 |
+is used for the comparison. |
810 |
+.TS |
811 |
+ll |
812 |
+ll |
813 |
+ll |
814 |
+ll |
815 |
+llw(2i). |
816 |
+a The access time of the file \fIreference\fR |
817 |
+B The birth time of the file \fIreference\fR |
818 |
+c The inode status change time of \fIreference\fR |
819 |
+m The modification time of the file \fIreference\fR |
820 |
+t \fIreference\fR is interpreted directly as a time |
821 |
+.TE |
822 |
+ |
823 |
+Some combinations are invalid; for example, it is invalid for |
824 |
+.I X |
825 |
+to be |
826 |
+.IR t . |
827 |
+Some combinations are not implemented on all systems; for example |
828 |
+.I B |
829 |
+is not supported on all systems. If an invalid or unsupported |
830 |
+combination of |
831 |
+.I XY |
832 |
+is specified, a fatal error results. Time specifications are |
833 |
+interpreted as for the argument to the |
834 |
+.B \-d |
835 |
+option of GNU |
836 |
+.BR date . |
837 |
+If you try to use the birth time of a reference file, and the birth |
838 |
+time cannot be determined, a fatal error message results. If you |
839 |
+specify a test which refers to the birth time of files being examined, |
840 |
+this test will fail for any files where the birth time is unknown. |
841 |
+ |
842 |
+.IP \-nogroup |
843 |
+No group corresponds to file's numeric group ID. |
844 |
+ |
845 |
+.IP \-nouser |
846 |
+No user corresponds to file's numeric user ID. |
847 |
+ |
848 |
+.IP "\-path \fIpattern\fR" |
849 |
+File name matches shell pattern \fIpattern\fR. The metacharacters do |
850 |
+not treat `/' or `.' specially; so, for example, |
851 |
+.br |
852 |
+.in +1i |
853 |
+find . \-path "./sr*sc" |
854 |
+.br |
855 |
+.in -1i |
856 |
+will print an entry for a directory called `./src/misc' (if one |
857 |
+exists). To ignore a whole directory tree, use |
858 |
+.B \-prune |
859 |
+rather than |
860 |
+checking every file in the tree. For example, to skip the |
861 |
+directory `src/emacs' and all files and directories under it, and |
862 |
+print the names of the other files found, do something like this: |
863 |
+.br |
864 |
+.in +1i |
865 |
+find . \-path ./src/emacs \-prune \-o \-print |
866 |
+.br |
867 |
+.in -1i |
868 |
+Note that the pattern match test applies to the whole file name, |
869 |
+starting from one of the start points named on the command line. It |
870 |
+would only make sense to use an absolute path name here if the |
871 |
+relevant start point is also an absolute path. This means that this |
872 |
+command will never match anything: |
873 |
+.br |
874 |
+.in +1i |
875 |
+find bar \-path /foo/bar/myfile \-print |
876 |
+.br |
877 |
+.in -1i |
878 |
+The predicate |
879 |
+.B \-path |
880 |
+is also supported by HP-UX |
881 |
+.B find |
882 |
+and will be in a forthcoming version of the POSIX standard. |
883 |
+ |
884 |
+.IP "\-perm \fImode\fR" |
885 |
+File's permission bits are exactly \fImode\fR (octal or symbolic). |
886 |
+Since an exact match is required, if you want to use this form for |
887 |
+symbolic modes, you may have to specify a rather complex mode string. |
888 |
+For example |
889 |
+.B \-perm g=w |
890 |
+will only match files which have mode 0020 |
891 |
+(that is, ones for which group write permission is the only permission |
892 |
+set). It is more likely that you will want to use the `/' or `-' |
893 |
+forms, for example |
894 |
+.BR "\-perm \-g=w" , |
895 |
+which matches any file with group write permission. See the |
896 |
+.B EXAMPLES |
897 |
+section for some illustrative examples. |
898 |
+ |
899 |
+.IP "\-perm \-\fImode\fR" |
900 |
+All of the permission bits \fImode\fR are set for the file. |
901 |
+Symbolic modes are accepted in this form, and this is usually the way |
902 |
+in which would want to use them. You must specify `u', `g' or `o' if |
903 |
+you use a symbolic mode. See the |
904 |
+.B EXAMPLES |
905 |
+section for some illustrative examples. |
906 |
+ |
907 |
+.IP "\-perm /\fImode\fR" |
908 |
+Any of the permission bits \fImode\fR are set for the file. Symbolic |
909 |
+modes are accepted in this form. You must specify `u', `g' or `o' if |
910 |
+you use a symbolic mode. See the |
911 |
+.B EXAMPLES |
912 |
+section for some illustrative examples. If no permission bits in |
913 |
+.I mode |
914 |
+are set, this test currently matches no files. However, it will soon |
915 |
+be changed to match any file (the idea is to be more consistent with |
916 |
+the behaviour of |
917 |
+.B \-perm |
918 |
+.BR \-000 ). |
919 |
+ |
920 |
+.IP "\-perm +\fImode\fR" |
921 |
+Deprecated, old way of searching for files with any of the permission |
922 |
+bits in \fImode\fR set. You should use |
923 |
+.B \-perm \fI/mode\fR |
924 |
+instead. Trying to use the `+' syntax with symbolic modes will yield |
925 |
+surprising results. For example, `+u+x' is a valid symbolic mode |
926 |
+(equivalent to +u,+x, i.e. 0111) and will therefore not be evaluated |
927 |
+as |
928 |
+.B \-perm +\fImode\fR |
929 |
+but instead as the exact mode specifier |
930 |
+.B \-perm \fImode\fR |
931 |
+and so it matches files with exact permissions 0111 instead of files with any |
932 |
+execute bit set. If you found this paragraph confusing, you're not |
933 |
+alone - just use |
934 |
+.B \-perm /\fImode\fR. |
935 |
+This form of the |
936 |
+.B \-perm |
937 |
+test is deprecated because the POSIX specification requires the |
938 |
+interpretation of a leading `+' as being part of a symbolic mode, and |
939 |
+so we switched to using `/' instead. |
940 |
+ |
941 |
+.IP \-readable |
942 |
+Matches files which are readable. This takes into account access |
943 |
+control lists and other permissions artefacts which the |
944 |
+.B \-perm |
945 |
+test ignores. This test makes use of the |
946 |
+.BR access (2) |
947 |
+system call, and so can be fooled by NFS servers which do UID |
948 |
+mapping (or root-squashing), since many systems implement |
949 |
+.BR access (2) |
950 |
+in the client's kernel and so cannot make use of the UID mapping |
951 |
+information held on the server. |
952 |
+ |
953 |
+.IP "\-regex \fIpattern\fR" |
954 |
+File name matches regular expression \fIpattern\fR. This is a match |
955 |
+on the whole path, not a search. For example, to match a file named |
956 |
+`./fubar3', you can use the regular expression `.*bar.' or `.*b.*3', |
957 |
+but not `f.*r3'. The regular expressions understood by |
958 |
+.B find |
959 |
+are by default Emacs Regular Expressions, but this can be |
960 |
+changed with the |
961 |
+.B \-regextype |
962 |
+option. |
963 |
+ |
964 |
+.IP "\-samefile \fIname\fR" |
965 |
+File refers to the same inode as \fIname\fR. When |
966 |
+.B \-L |
967 |
+is in effect, this can include symbolic links. |
968 |
+ |
969 |
+.IP "\-size \fIn\fR[cwbkMG]" |
970 |
+File uses \fIn\fP units of space. The following suffixes |
971 |
+can be used: |
972 |
+.RS |
973 |
+.IP `b' |
974 |
+for 512-byte blocks (this is the default if no suffix is used) |
975 |
+.IP `c' |
976 |
+for bytes |
977 |
+.IP `w' |
978 |
+for two-byte words |
979 |
+.IP `k' |
980 |
+for Kilobytes (units of 1024 bytes) |
981 |
+.IP `M' |
982 |
+for Megabytes (units of 1048576 bytes) |
983 |
+.IP `G' |
984 |
+for Gigabytes (units of 1073741824 bytes) |
985 |
+.RE |
986 |
+.IP |
987 |
+The size does not count indirect blocks, but it does count blocks in |
988 |
+sparse files that are not actually allocated. Bear in mind that the |
989 |
+`%k' and `%b' format specifiers of |
990 |
+.B \-printf |
991 |
+handle sparse files |
992 |
+differently. The `b' suffix always denotes 512-byte blocks and never |
993 |
+1 Kilobyte blocks, which is different to the behaviour of |
994 |
+.BR \-ls . |
995 |
+ |
996 |
+.IP \-true |
997 |
+Always true. |
998 |
+ |
999 |
+.IP "\-type \fIc\fR" |
1000 |
+File is of type \fIc\fR: |
1001 |
+.RS |
1002 |
+.IP b |
1003 |
+block (buffered) special |
1004 |
+.IP c |
1005 |
+character (unbuffered) special |
1006 |
+.IP d |
1007 |
+directory |
1008 |
+.IP p |
1009 |
+named pipe (FIFO) |
1010 |
+.IP f |
1011 |
+regular file |
1012 |
+.IP l |
1013 |
+symbolic link; this is never true if the |
1014 |
+.B \-L |
1015 |
+option or the |
1016 |
+.B \-follow |
1017 |
+option is in effect, unless the symbolic link is broken. If you want |
1018 |
+to search for symbolic links when |
1019 |
+.B \-L |
1020 |
+is in effect, use |
1021 |
+.BR \-xtype . |
1022 |
+.IP s |
1023 |
+socket |
1024 |
+.IP D |
1025 |
+door (Solaris) |
1026 |
+.RE |
1027 |
+.IP "\-uid \fIn\fR" |
1028 |
+File's numeric user ID is \fIn\fR. |
1029 |
+ |
1030 |
+.IP "\-used \fIn\fR" |
1031 |
+File was last accessed \fIn\fR days after its status was last changed. |
1032 |
+ |
1033 |
+.IP "\-user \fIuname\fR" |
1034 |
+File is owned by user \fIuname\fR (numeric user ID allowed). |
1035 |
+ |
1036 |
+.IP "\-wholename \fIpattern\fR" |
1037 |
+See \-path. This alternative is less portable than |
1038 |
+.BR \-path . |
1039 |
+ |
1040 |
+.IP "\-writable" |
1041 |
+Matches files which are writable. This takes into account access |
1042 |
+control lists and other permissions artefacts which the |
1043 |
+.B \-perm |
1044 |
+test ignores. This test makes use of the |
1045 |
+.BR access (2) |
1046 |
+system call, and so can be fooled by NFS servers which do UID |
1047 |
+mapping (or root-squashing), since many systems implement |
1048 |
+.BR access (2) |
1049 |
+in the client's kernel and so cannot make use of the UID mapping |
1050 |
+information held on the server. |
1051 |
+ |
1052 |
+.IP "\-xtype \fIc\fR" |
1053 |
+The same as |
1054 |
+.B \-type |
1055 |
+unless the file is a symbolic link. For symbolic |
1056 |
+links: if the |
1057 |
+.B \-H |
1058 |
+or |
1059 |
+.B \-P |
1060 |
+option was specified, true if the file is a |
1061 |
+link to a file of type \fIc\fR; if the |
1062 |
+.B \-L |
1063 |
+option has been given, true |
1064 |
+if \fIc\fR is `l'. In other words, for symbolic links, |
1065 |
+.B \-xtype |
1066 |
+checks the type of the file that |
1067 |
+.B \-type |
1068 |
+does not check. |
1069 |
+ |
1070 |
+.SS ACTIONS |
1071 |
+.IP "\-delete\fR" |
1072 |
+Delete files; true if removal succeeded. If the removal failed, an |
1073 |
+error message is issued. |
1074 |
+If |
1075 |
+.B \-delete |
1076 |
+fails, |
1077 |
+.BR find 's |
1078 |
+exit status will be nonzero |
1079 |
+(when it eventually exits). |
1080 |
+Use of |
1081 |
+.B \-delete |
1082 |
+automatically turns on the |
1083 |
+.B \-depth |
1084 |
+option. |
1085 |
+ |
1086 |
+.BR Warnings : |
1087 |
+Don't forget that the find command line is |
1088 |
+evaluated as an expression, so putting |
1089 |
+.B \-delete |
1090 |
+first will make |
1091 |
+.B find |
1092 |
+try to delete everything below the starting points you specified. |
1093 |
+When testing a |
1094 |
+.B find |
1095 |
+command line that you later intend to use with |
1096 |
+.BR \-delete , |
1097 |
+you should explicitly specify |
1098 |
+.B \-depth |
1099 |
+in order to avoid later surprises. Because |
1100 |
+.B \-delete |
1101 |
+implies |
1102 |
+.BR \-depth , |
1103 |
+you cannot usefully use |
1104 |
+.B \-prune |
1105 |
+and |
1106 |
+.B \-delete |
1107 |
+together. |
1108 |
+ |
1109 |
+.IP "\-exec \fIcommand\fR ;" |
1110 |
+Execute \fIcommand\fR; true if 0 status is returned. All following |
1111 |
+arguments to |
1112 |
+.B find |
1113 |
+are taken to be arguments to the command until an argument consisting |
1114 |
+of `;' is encountered. The string `{}' is replaced by the current |
1115 |
+file name being processed everywhere it occurs in the arguments to the |
1116 |
+command, not just in arguments where it is alone, as in some versions |
1117 |
+of |
1118 |
+.BR find . |
1119 |
+Both of these constructions might need to be escaped (with a `\e') or |
1120 |
+quoted to protect them from expansion by the shell. See the |
1121 |
+.B EXAMPLES |
1122 |
+section for examples of the use of the |
1123 |
+.B \-exec |
1124 |
+option. The specified |
1125 |
+command is run once for each matched file. |
1126 |
+The command is executed in the starting directory. There are |
1127 |
+unavoidable security problems surrounding use of the |
1128 |
+.B \-exec |
1129 |
+action; |
1130 |
+you should use the |
1131 |
+.B \-execdir |
1132 |
+option instead. |
1133 |
+ |
1134 |
+.IP "\-exec \fIcommand\fR {} +" |
1135 |
+This variant of the |
1136 |
+.B \-exec |
1137 |
+action runs the specified command on the |
1138 |
+selected files, but the command line is built by appending each |
1139 |
+selected file name at the end; the total number of invocations of the |
1140 |
+command will be much less than the number of matched files. The |
1141 |
+command line is built in much the same way that |
1142 |
+.B xargs |
1143 |
+builds its command lines. Only one instance of `{}' is allowed within |
1144 |
+the command. The command is executed in the starting directory. |
1145 |
+ |
1146 |
+.IP "\-execdir \fIcommand\fR ;" |
1147 |
+.IP "\-execdir \fIcommand\fR {} +" |
1148 |
+Like |
1149 |
+.BR \-exec , |
1150 |
+but the specified command is run from the subdirectory |
1151 |
+containing the matched file, which is not normally the directory in |
1152 |
+which you started |
1153 |
+.BR find . |
1154 |
+This a much more secure method for invoking commands, as it avoids |
1155 |
+race conditions during resolution of the paths to the matched files. |
1156 |
+As with the |
1157 |
+.B \-exec |
1158 |
+action, the `+' form of |
1159 |
+.B \-execdir |
1160 |
+will build a |
1161 |
+command line to process more than one matched file, but any given |
1162 |
+invocation of |
1163 |
+.I command |
1164 |
+will only list files that exist in the same subdirectory. If you use |
1165 |
+this option, you must ensure that your |
1166 |
+.B $PATH |
1167 |
+environment variable does not reference `.'; |
1168 |
+otherwise, an attacker can run any commands they like by leaving an |
1169 |
+appropriately-named file in a directory in which you will run |
1170 |
+.BR \-execdir . |
1171 |
+The same applies to having entries in |
1172 |
+.B $PATH |
1173 |
+which are empty or which are not absolute directory names. |
1174 |
+ |
1175 |
+.IP "\-fls \fIfile\fR" |
1176 |
+True; like |
1177 |
+.B \-ls |
1178 |
+but write to \fIfile\fR like |
1179 |
+.BR \-fprint . |
1180 |
+The output file is always created, even if the predicate is never |
1181 |
+matched. |
1182 |
+See the |
1183 |
+.B UNUSUAL FILENAMES |
1184 |
+section for information about how unusual characters in filenames are handled. |
1185 |
+ |
1186 |
+.IP "\-fprint \fIfile\fR" |
1187 |
+True; print the full file name into file \fIfile\fR. If \fIfile\fR |
1188 |
+does not exist when \fBfind\fR is run, it is created; if it does |
1189 |
+exist, it is truncated. The file names ``/dev/stdout'' and |
1190 |
+``/dev/stderr'' are handled specially; they refer to the standard |
1191 |
+output and standard error output, respectively. |
1192 |
+The output file is always created, even if the predicate is never matched. |
1193 |
+See the |
1194 |
+.B UNUSUAL FILENAMES |
1195 |
+section for information about how unusual characters in filenames are handled. |
1196 |
+ |
1197 |
+.IP "\-fprint0 \fIfile\fR" |
1198 |
+True; like |
1199 |
+.B \-print0 |
1200 |
+but write to \fIfile\fR like |
1201 |
+.BR \-fprint . |
1202 |
+The output file is always created, even if the predicate is never matched. |
1203 |
+See the |
1204 |
+.B UNUSUAL FILENAMES |
1205 |
+section for information about how unusual characters in filenames are handled. |
1206 |
+ |
1207 |
+.IP "\-fprintf \fIfile\fR \fIformat\fR" |
1208 |
+True; like |
1209 |
+.B \-printf |
1210 |
+but write to \fIfile\fR like |
1211 |
+.BR \-fprint . |
1212 |
+The output file is always created, even if the predicate is never matched. |
1213 |
+See the |
1214 |
+.B UNUSUAL FILENAMES |
1215 |
+section for information about how unusual characters in filenames are handled. |
1216 |
+ |
1217 |
+.IP \-ls |
1218 |
+True; list current file in |
1219 |
+.B ls \-dils |
1220 |
+format on standard output. |
1221 |
+The block counts are of 1K blocks, unless the environment variable |
1222 |
+POSIXLY_CORRECT is set, in which case 512-byte blocks are used. |
1223 |
+See the |
1224 |
+.B UNUSUAL FILENAMES |
1225 |
+section for information about how unusual characters in filenames are handled. |
1226 |
+ |
1227 |
+.IP "\-ok \fIcommand\fR ;" |
1228 |
+Like |
1229 |
+.B \-exec |
1230 |
+but ask the user first (on the standard input); if the |
1231 |
+response does not start with `y' or `Y', do not run the command, and |
1232 |
+return false. If the command is run, its standard input is redirected |
1233 |
+from |
1234 |
+.BR /dev/null . |
1235 |
+ |
1236 |
+.IP "\-okdir \fIcommand\fR ;" |
1237 |
+Like |
1238 |
+.B \-execdir |
1239 |
+but ask the user first (on the standard input); if the |
1240 |
+response does not start with `y' or `Y', do not run the command, and |
1241 |
+return false. If the command is run, its standard input is redirected |
1242 |
+from |
1243 |
+.BR /dev/null . |
1244 |
+ |
1245 |
+.IP \-print |
1246 |
+True; print the full file name on the standard output, followed by a |
1247 |
+newline. If you are piping the output of |
1248 |
+.B find |
1249 |
+into another program and there is the faintest possibility that the files |
1250 |
+which you are searching for might contain a newline, then you should |
1251 |
+seriously consider using the |
1252 |
+.B \-print0 |
1253 |
+option instead of |
1254 |
+.BR \-print . |
1255 |
+See the |
1256 |
+.B UNUSUAL FILENAMES |
1257 |
+section for information about how unusual characters in filenames are handled. |
1258 |
+ |
1259 |
+.IP \-print0 |
1260 |
+True; print the full file name on the standard output, followed by a |
1261 |
+null character (instead of the newline character that |
1262 |
+.B \-print |
1263 |
+uses). |
1264 |
+This allows file names that contain newlines or other types of white |
1265 |
+space to be correctly interpreted by programs that process the |
1266 |
+\fBfind\fR output. This option corresponds to the |
1267 |
+.B \-0 |
1268 |
+option of |
1269 |
+.BR xargs . |
1270 |
+ |
1271 |
+.IP "\-printf \fIformat\fR" |
1272 |
+True; print \fIformat\fR on the standard output, interpreting `\e' |
1273 |
+escapes and `%' directives. Field widths and precisions can be |
1274 |
+specified as with the `printf' C function. Please note that many of |
1275 |
+the fields are printed as %s rather than %d, and this may mean that |
1276 |
+flags don't work as you might expect. This also means that the `\-' |
1277 |
+flag does work (it forces fields to be left-aligned). Unlike |
1278 |
+.BR \-print , |
1279 |
+.B \-printf |
1280 |
+does not add a newline at the end of the string. The escapes |
1281 |
+and directives are: |
1282 |
+.RS |
1283 |
+.IP \ea |
1284 |
+Alarm bell. |
1285 |
+.IP \eb |
1286 |
+Backspace. |
1287 |
+.IP \ec |
1288 |
+Stop printing from this format immediately and flush the output. |
1289 |
+.IP \ef |
1290 |
+Form feed. |
1291 |
+.IP \en |
1292 |
+Newline. |
1293 |
+.IP \er |
1294 |
+Carriage return. |
1295 |
+.IP \et |
1296 |
+Horizontal tab. |
1297 |
+.IP \ev |
1298 |
+Vertical tab. |
1299 |
+.IP \e\0 |
1300 |
+ASCII NUL. |
1301 |
+.IP \e\e |
1302 |
+A literal backslash (`\e'). |
1303 |
+.IP \eNNN |
1304 |
+The character whose ASCII code is NNN (octal). |
1305 |
+.PP |
1306 |
+A `\e' character followed by any other character is treated as an |
1307 |
+ordinary character, so they both are printed. |
1308 |
+.IP %% |
1309 |
+A literal percent sign. |
1310 |
+.IP %a |
1311 |
+File's last access time in the format returned by the C `ctime' function. |
1312 |
+.IP %A\fIk\fP |
1313 |
+File's last access time in the format specified by \fIk\fR, which is |
1314 |
+either `@' or a directive for the C `strftime' function. The possible |
1315 |
+values for \fIk\fR are listed below; some of them might not be |
1316 |
+available on all systems, due to differences in `strftime' between |
1317 |
+systems. |
1318 |
+.RS |
1319 |
+.IP @ |
1320 |
+seconds since Jan. 1, 1970, 00:00 GMT, with fractional part. |
1321 |
+.PP |
1322 |
+Time fields: |
1323 |
+.IP H |
1324 |
+hour (00..23) |
1325 |
+.IP I |
1326 |
+hour (01..12) |
1327 |
+.IP k |
1328 |
+hour ( 0..23) |
1329 |
+.IP l |
1330 |
+hour ( 1..12) |
1331 |
+.IP M |
1332 |
+minute (00..59) |
1333 |
+.IP p |
1334 |
+locale's AM or PM |
1335 |
+.IP r |
1336 |
+time, 12-hour (hh:mm:ss [AP]M) |
1337 |
+.IP S |
1338 |
+Second (00.00 .. 61.00). There is a fractional part. |
1339 |
+.IP T |
1340 |
+time, 24-hour (hh:mm:ss) |
1341 |
+.IP + |
1342 |
+Date and time, separated by `+', for example |
1343 |
+`2004\-04\-28+22:22:05.0'. This is a GNU extension. The time is |
1344 |
+given in the current timezone (which may be affected by setting the TZ |
1345 |
+environment variable). The seconds field includes a fractional part. |
1346 |
+.IP X |
1347 |
+locale's time representation (H:M:S) |
1348 |
+.IP Z |
1349 |
+time zone (e.g., EDT), or nothing if no time zone is determinable |
1350 |
+.PP |
1351 |
+Date fields: |
1352 |
+.IP a |
1353 |
+locale's abbreviated weekday name (Sun..Sat) |
1354 |
+.IP A |
1355 |
+locale's full weekday name, variable length (Sunday..Saturday) |
1356 |
+.IP b |
1357 |
+locale's abbreviated month name (Jan..Dec) |
1358 |
+.IP B |
1359 |
+locale's full month name, variable length (January..December) |
1360 |
+.IP c |
1361 |
+locale's date and time (Sat Nov 04 12:02:33 EST 1989). The format is |
1362 |
+the same as for |
1363 |
+.BR ctime (3) |
1364 |
+and so to preserve compatibility with that format, there is no fractional part |
1365 |
+in the seconds field. |
1366 |
+.IP d |
1367 |
+day of month (01..31) |
1368 |
+.IP D |
1369 |
+date (mm/dd/yy) |
1370 |
+.IP h |
1371 |
+same as b |
1372 |
+.IP j |
1373 |
+day of year (001..366) |
1374 |
+.IP m |
1375 |
+month (01..12) |
1376 |
+.IP U |
1377 |
+week number of year with Sunday as first day of week (00..53) |
1378 |
+.IP w |
1379 |
+day of week (0..6) |
1380 |
+.IP W |
1381 |
+week number of year with Monday as first day of week (00..53) |
1382 |
+.IP x |
1383 |
+locale's date representation (mm/dd/yy) |
1384 |
+.IP y |
1385 |
+last two digits of year (00..99) |
1386 |
+.IP Y |
1387 |
+year (1970...) |
1388 |
+.RE |
1389 |
+.IP %b |
1390 |
+The amount of disk space used for this file in 512-byte blocks. Since disk |
1391 |
+space is allocated in multiples of the filesystem block size this is usually |
1392 |
+greater than %s/512, but it can also be smaller if the file is a sparse file. |
1393 |
+.IP %c |
1394 |
+File's last status change time in the format returned by the C `ctime' |
1395 |
+function. |
1396 |
+.IP %C\fIk\fP |
1397 |
+File's last status change time in the format specified by \fIk\fR, |
1398 |
+which is the same as for %A. |
1399 |
+.IP %d |
1400 |
+File's depth in the directory tree; 0 means the file is a command line |
1401 |
+argument. |
1402 |
+.IP %D |
1403 |
+The device number on which the file exists (the st_dev field of struct |
1404 |
+stat), in decimal. |
1405 |
+.IP %f |
1406 |
+File's name with any leading directories removed (only the last element). |
1407 |
+.IP %F |
1408 |
+Type of the filesystem the file is on; this value can be used for |
1409 |
+\-fstype. |
1410 |
+.IP %g |
1411 |
+File's group name, or numeric group ID if the group has no name. |
1412 |
+.IP %G |
1413 |
+File's numeric group ID. |
1414 |
+.IP %h |
1415 |
+Leading directories of file's name (all but the last element). |
1416 |
+If the file name contains no slashes (since it is in the current |
1417 |
+directory) the %h specifier expands to ".". |
1418 |
+.IP %H |
1419 |
+Command line argument under which file was found. |
1420 |
+.IP %i |
1421 |
+File's inode number (in decimal). |
1422 |
+.IP %k |
1423 |
+The amount of disk space used for this file in 1K blocks. Since disk space is |
1424 |
+allocated in multiples of the filesystem block size this is usually greater |
1425 |
+than %s/1024, but it can also be smaller if the file is a sparse file. |
1426 |
+.IP %l |
1427 |
+Object of symbolic link (empty string if file is not a symbolic link). |
1428 |
+.IP %m |
1429 |
+File's permission bits (in octal). This option uses the `traditional' |
1430 |
+numbers which most Unix implementations use, but if your particular |
1431 |
+implementation uses an unusual ordering of octal permissions bits, you |
1432 |
+will see a difference between the actual value of the file's mode and |
1433 |
+the output of %m. Normally you will want to have a leading |
1434 |
+zero on this number, and to do this, you should use the |
1435 |
+.B # |
1436 |
+flag (as in, for example, `%#m'). |
1437 |
+.IP %M |
1438 |
+File's permissions (in symbolic form, as for |
1439 |
+.BR ls ). |
1440 |
+This directive is supported in findutils 4.2.5 and later. |
1441 |
+.IP %n |
1442 |
+Number of hard links to file. |
1443 |
+.IP %p |
1444 |
+File's name. |
1445 |
+.IP %P |
1446 |
+File's name with the name of the command line argument under which |
1447 |
+it was found removed. |
1448 |
+.IP %s |
1449 |
+File's size in bytes. |
1450 |
+.IP %S |
1451 |
+File's sparseness. This is calculated as (BLOCKSIZE*st_blocks / |
1452 |
+st_size). The exact value you will get for an ordinary file of a |
1453 |
+certain length is system-dependent. However, normally sparse files |
1454 |
+will have values less than 1.0, and files which use indirect blocks |
1455 |
+may have a value which is greater than 1.0. The value used for |
1456 |
+BLOCKSIZE is system-dependent, but is usually 512 bytes. If the file |
1457 |
+size is zero, the value printed is undefined. On systems which lack |
1458 |
+support for st_blocks, a file's sparseness is assumed to be 1.0. |
1459 |
+.IP %t |
1460 |
+File's last modification time in the format returned by the C `ctime' |
1461 |
+function. |
1462 |
+.IP %T\fIk\fP |
1463 |
+File's last modification time in the format specified by \fIk\fR, |
1464 |
+which is the same as for %A. |
1465 |
+.IP %u |
1466 |
+File's user name, or numeric user ID if the user has no name. |
1467 |
+.IP %U |
1468 |
+File's numeric user ID. |
1469 |
+.IP %y |
1470 |
+File's type (like in |
1471 |
+.BR "ls \-l" ), |
1472 |
+U=unknown type (shouldn't happen) |
1473 |
+.IP %Y |
1474 |
+File's type (like %y), plus follow symlinks: L=loop, N=nonexistent |
1475 |
+.PP |
1476 |
+A `%' character followed by any other character is discarded, but the |
1477 |
+other character is printed (don't rely on this, as further format |
1478 |
+characters may be introduced). A `%' at the end of the format |
1479 |
+argument causes undefined behaviour since there is no following |
1480 |
+character. In some locales, it may hide your door keys, while in |
1481 |
+others it may remove the final page from the novel you are reading. |
1482 |
+ |
1483 |
+The %m and %d directives support the |
1484 |
+.B # |
1485 |
+, |
1486 |
+.B 0 |
1487 |
+and |
1488 |
+.B + |
1489 |
+flags, but the other directives do not, even if they |
1490 |
+print numbers. Numeric directives that do not support these flags |
1491 |
+include |
1492 |
+.BR G , |
1493 |
+.BR U , |
1494 |
+.BR b , |
1495 |
+.BR D , |
1496 |
+.B k |
1497 |
+and |
1498 |
+.BR n . |
1499 |
+The `\-' format flag is supported and changes the alignment of a field |
1500 |
+from right-justified (which is the default) to left-justified. |
1501 |
+.PP |
1502 |
+See the |
1503 |
+.B UNUSUAL FILENAMES |
1504 |
+section for information about how unusual characters in filenames are handled. |
1505 |
+ |
1506 |
+ |
1507 |
+.RE |
1508 |
+.IP \-prune |
1509 |
+True; if the file is a directory, do not descend into it. If |
1510 |
+.B \-depth |
1511 |
+is given, false; no effect. Because |
1512 |
+.B \-delete |
1513 |
+implies |
1514 |
+.BR \-depth , |
1515 |
+you cannot usefully use |
1516 |
+.B \-prune |
1517 |
+and |
1518 |
+.B \-delete together. |
1519 |
+ |
1520 |
+.IP "\-quit" |
1521 |
+Exit immediately. No child processes will be left running, but no more |
1522 |
+paths specified on the command line will be processed. For example, |
1523 |
+.B find /tmp/foo /tmp/bar \-print \-quit |
1524 |
+will print only |
1525 |
+.BR /tmp/foo . |
1526 |
+Any command lines which have been built up with |
1527 |
+.B \-execdir ... {} + |
1528 |
+will be invoked before |
1529 |
+.B find |
1530 |
+exits. The exit status may or may not be zero, depending on whether |
1531 |
+an error has already occurred. |
1532 |
+ |
1533 |
+.SS UNUSUAL FILENAMES |
1534 |
+Many of the actions of |
1535 |
+.B find |
1536 |
+result in the printing of data which is under the control of other |
1537 |
+users. This includes file names, sizes, modification times and so |
1538 |
+forth. File names are a potential problem since they can contain any |
1539 |
+character except `\e0' and `/'. Unusual characters in file names can |
1540 |
+do unexpected and often undesirable things to your terminal (for |
1541 |
+example, changing the settings of your function keys on some |
1542 |
+terminals). Unusual characters are handled differently by various |
1543 |
+actions, as described below. |
1544 |
+ |
1545 |
+.IP "\-print0, \-fprint0\" |
1546 |
+Always print the exact filename, unchanged, even if the output is |
1547 |
+going to a terminal. |
1548 |
+ |
1549 |
+.IP "\-ls, \-fls" |
1550 |
+Unusual characters are always escaped. White space, backslash, and |
1551 |
+double quote characters are printed using C-style escaping (for |
1552 |
+example `\ef', `\e"'). Other unusual characters are printed using an |
1553 |
+octal escape. Other printable characters (for |
1554 |
+.B \-ls |
1555 |
+and |
1556 |
+.B \-fls |
1557 |
+these are the characters between octal 041 and 0176) are printed as-is. |
1558 |
+ |
1559 |
+.IP "\-printf, \-fprintf" |
1560 |
+If the output is not going to a terminal, it is printed as-is. |
1561 |
+Otherwise, the result depends on which directive is in use. The |
1562 |
+directives %D, %F, %g, %G, %H, %Y, and %y expand to values which are |
1563 |
+not under control of files' owners, and so are printed as-is. The |
1564 |
+directives %a, %b, %c, %d, %i, %k, %m, %M, %n, %s, %t, %u and %U have |
1565 |
+values which are under the control of files' owners but which cannot |
1566 |
+be used to send arbitrary data to the terminal, and so these are |
1567 |
+printed as-is. The directives %f, %h, %l, %p and %P are quoted. This |
1568 |
+quoting is performed in the same way as for GNU |
1569 |
+.BR ls . |
1570 |
+This is not the same quoting mechanism as the one used for |
1571 |
+.B \-ls |
1572 |
+and |
1573 |
+.BR \-fls . |
1574 |
+If you are able to decide what format to use for the output of |
1575 |
+.B find |
1576 |
+then it is normally better to use `\e0' as a terminator |
1577 |
+than to use newline, as file names can contain white space and newline |
1578 |
+characters. |
1579 |
+ |
1580 |
+.IP "\-print, \-fprint" |
1581 |
+Quoting is handled in the same way as for |
1582 |
+.B \-printf |
1583 |
+and |
1584 |
+.BR \-fprintf . |
1585 |
+If you are using |
1586 |
+.B find |
1587 |
+in a script or in a situation where the matched files might have |
1588 |
+arbitrary names, you should consider using |
1589 |
+.B \-print0 |
1590 |
+instead of |
1591 |
+.BR \-print . |
1592 |
+.P |
1593 |
+The |
1594 |
+.B \-ok |
1595 |
+and |
1596 |
+.B \-okdir |
1597 |
+actions print the current filename as-is. This may change in a future release. |
1598 |
+.SS OPERATORS |
1599 |
+.P |
1600 |
+Listed in order of decreasing precedence: |
1601 |
+ |
1602 |
+.IP "( \fIexpr\fR )" |
1603 |
+Force precedence. Since parentheses are special to the shell, you |
1604 |
+will normally need to quote them. Many of the examples in this manual |
1605 |
+page use backslashes for this purpose: `\e(...\e)' instead of `(...)'. |
1606 |
+ |
1607 |
+.IP "! \fIexpr\fR" |
1608 |
+True if \fIexpr\fR is false. This character will also usually need |
1609 |
+protection from interpretation by the shell. |
1610 |
+ |
1611 |
+.IP "\-not \fIexpr\fR" |
1612 |
+Same as ! \fIexpr\fR, but not POSIX compliant. |
1613 |
+ |
1614 |
+.IP "\fIexpr1 expr2\fR" |
1615 |
+Two expressions in a row are taken to be joined with an |
1616 |
+implied "and"; \fIexpr2\fR is not evaluated if \fIexpr1\fR is false. |
1617 |
+ |
1618 |
+.IP "\fIexpr1\fR \-a \fIexpr2\fR" |
1619 |
+Same as \fIexpr1 expr2\fR. |
1620 |
+ |
1621 |
+.IP "\fIexpr1\fR \-and \fIexpr2\fR" |
1622 |
+Same as \fIexpr1 expr2\fR, but not POSIX compliant. |
1623 |
+ |
1624 |
+.IP "\fIexpr1\fR \-o \fIexpr2\fR" |
1625 |
+Or; \fIexpr2\fR is not evaluated if \fIexpr1\fR is true. |
1626 |
+ |
1627 |
+.IP "\fIexpr1\fR \-or \fIexpr2\fR" |
1628 |
+Same as \fIexpr1\fR |
1629 |
+.B \-o |
1630 |
+\fIexpr2\fR, but not POSIX compliant. |
1631 |
+ |
1632 |
+.IP "\fIexpr1\fR , \fIexpr2\fR" |
1633 |
+List; both \fIexpr1\fR and \fIexpr2\fR are always evaluated. The |
1634 |
+value of \fIexpr1\fR is discarded; the value of the list is the value |
1635 |
+of \fIexpr2\fR. The comma operator can be useful for searching for |
1636 |
+several different types of thing, but traversing the filesystem |
1637 |
+hierarchy only once. The |
1638 |
+.B \-fprintf |
1639 |
+action can be used to list the various matched items into several |
1640 |
+different output files. |
1641 |
+ |
1642 |
+ |
1643 |
+.SH "STANDARDS CONFORMANCE" |
1644 |
+For closest compliance to the POSIX standard, you should set the |
1645 |
+POSIXLY_CORRECT environment variable. The following options are |
1646 |
+specified in the POSIX standard (IEEE Std 1003.1, 2003 Edition): |
1647 |
+ |
1648 |
+.IP \fB\-H\fR |
1649 |
+This option is supported. |
1650 |
+ |
1651 |
+.IP \fB\-L\fR |
1652 |
+This option is supported. |
1653 |
+ |
1654 |
+.IP \fB\-name\fR |
1655 |
+This option is supported, but POSIX conformance depends on the |
1656 |
+POSIX conformance of the system's |
1657 |
+.BR fnmatch (3) |
1658 |
+library function. As of findutils-4.2.2, shell metacharacters |
1659 |
+(`*', `?' or `[]' for example) will match a leading `.', because |
1660 |
+IEEE PASC interpretation 126 requires this. This is a change from |
1661 |
+previous versions of findutils. |
1662 |
+ |
1663 |
+.IP \fB\-type\fR |
1664 |
+Supported. POSIX specifies `b', `c', `d', `l', `p', `f' and `s'. |
1665 |
+GNU find also supports `D', representing a Door, where the OS provides these. |
1666 |
+ |
1667 |
+.IP \fB\-ok\fR |
1668 |
+Supported. Interpretation of the response is not locale-dependent |
1669 |
+(see ENVIRONMENT VARIABLES). |
1670 |
+ |
1671 |
+.IP \fB\-newer\fR |
1672 |
+Supported. If the file specified is a symbolic link, it is always |
1673 |
+dereferenced. This is a change from previous behaviour, which used to |
1674 |
+take the relevant time from the symbolic link; see the HISTORY section |
1675 |
+below. |
1676 |
+ |
1677 |
+.IP \fB\-perm\fR |
1678 |
+Supported. If the POSIXLY_CORRECT environment variable is not set, |
1679 |
+some mode arguments (for example +a+x) which are not valid in POSIX |
1680 |
+are supported for backward-compatibility. |
1681 |
+ |
1682 |
+.IP "Other predicates" |
1683 |
+The predicates |
1684 |
+.BR \-atime , |
1685 |
+.BR \-ctime , |
1686 |
+.BR \-depth , |
1687 |
+.BR \-group , |
1688 |
+.BR \-links , |
1689 |
+.BR \-mtime , |
1690 |
+.BR \-nogroup , |
1691 |
+.BR \-nouser , |
1692 |
+.BR \-print , |
1693 |
+.BR \-prune , |
1694 |
+.BR \-size , |
1695 |
+.BR \-user |
1696 |
+and |
1697 |
+.B \-xdev |
1698 |
+are all supported. |
1699 |
+ |
1700 |
+.P |
1701 |
+The POSIX standard specifies parentheses `(', `)', negation `!' and the |
1702 |
+`and' and `or' operators ( |
1703 |
+.BR \-a , |
1704 |
+.BR \-o ). |
1705 |
+.P |
1706 |
+All other options, predicates, expressions and so forth are extensions |
1707 |
+beyond the POSIX standard. Many of these extensions are not unique to |
1708 |
+GNU find, however. |
1709 |
+.P |
1710 |
+The POSIX standard requires that |
1711 |
+.B find |
1712 |
+detects loops: |
1713 |
+.IP |
1714 |
+The |
1715 |
+.B find |
1716 |
+utility shall detect infinite loops; that is, entering a |
1717 |
+previously visited directory that is an ancestor of the last file |
1718 |
+encountered. When it detects an infinite loop, find shall write a |
1719 |
+diagnostic message to standard error and shall either recover its |
1720 |
+position in the hierarchy or terminate. |
1721 |
+.P |
1722 |
+GNU |
1723 |
+.B find |
1724 |
+complies with these requirements. The link count of |
1725 |
+directories which contain entries which are hard links to an ancestor |
1726 |
+will often be lower than they otherwise should be. This can mean that |
1727 |
+GNU find will sometimes optimise away the visiting of a subdirectory |
1728 |
+which is actually a link to an ancestor. Since |
1729 |
+.B find |
1730 |
+does not actually enter such a subdirectory, it is allowed to avoid |
1731 |
+emitting a diagnostic message. Although this behaviour may be |
1732 |
+somewhat confusing, it is unlikely that anybody actually depends on |
1733 |
+this behaviour. If the leaf optimisation has been turned off with |
1734 |
+.BR \-noleaf , |
1735 |
+the directory entry will always be examined and the diagnostic message |
1736 |
+will be issued where it is appropriate. Symbolic links cannot be used |
1737 |
+to create filesystem cycles as such, but if the |
1738 |
+.B \-L |
1739 |
+option or the |
1740 |
+.B \-follow |
1741 |
+option is in use, a diagnostic message is issued when |
1742 |
+.B find |
1743 |
+encounters a loop of symbolic links. As with loops containing hard |
1744 |
+links, the leaf optimisation will often mean that |
1745 |
+.B find |
1746 |
+knows that it doesn't need to call |
1747 |
+.I stat() |
1748 |
+or |
1749 |
+.I chdir() |
1750 |
+on the symbolic link, so this diagnostic is frequently not necessary. |
1751 |
+.P |
1752 |
+The |
1753 |
+.B \-d |
1754 |
+option is supported for compatibility with various BSD systems, |
1755 |
+but you should use the POSIX-compliant option |
1756 |
+.B \-depth |
1757 |
+instead. |
1758 |
+.P |
1759 |
+The POSIXLY_CORRECT environment variable does not affect the behaviour |
1760 |
+of the |
1761 |
+.B \-regex |
1762 |
+or |
1763 |
+.B \-iregex |
1764 |
+tests because those tests aren't specified in the POSIX standard. |
1765 |
+.SH "ENVIRONMENT VARIABLES" |
1766 |
+ |
1767 |
+.IP LANG |
1768 |
+Provides a default value for the internationalization variables that |
1769 |
+are unset or null. |
1770 |
+ |
1771 |
+.IP LC_ALL |
1772 |
+If set to a non-empty string value, override the values of all the |
1773 |
+other internationalization variables. |
1774 |
+ |
1775 |
+.IP LC_COLLATE |
1776 |
+The POSIX standard specifies that this variable affects the pattern |
1777 |
+matching to be used for the |
1778 |
+.B \-name |
1779 |
+option. GNU find uses the |
1780 |
+.BR fnmatch (3) |
1781 |
+library function, and so support for `LC_COLLATE' depends on the |
1782 |
+system library. |
1783 |
+ |
1784 |
+.IP |
1785 |
+POSIX also specifies that the `LC_COLLATE' environment |
1786 |
+variable affects the interpretation of the user's response to the |
1787 |
+query issued by |
1788 |
+.BR \-ok' , |
1789 |
+but this is not the case for GNU find. |
1790 |
+ |
1791 |
+.IP LC_CTYPE |
1792 |
+This variable affects the treatment of character classes used with |
1793 |
+the |
1794 |
+.B \-name |
1795 |
+test, if the system's |
1796 |
+.BR fnmatch (3) |
1797 |
+library function supports this. It has no effect on the behaviour |
1798 |
+of the |
1799 |
+.B \-ok |
1800 |
+expression. |
1801 |
+ |
1802 |
+.IP LC_MESSAGES |
1803 |
+Determines the locale to be used for internationalised messages. |
1804 |
+ |
1805 |
+.IP NLSPATH |
1806 |
+Determines the location of the internationalisation message catalogues. |
1807 |
+ |
1808 |
+.IP PATH |
1809 |
+Affects the directories which are searched to find the executables |
1810 |
+invoked by |
1811 |
+.BR \-exec , |
1812 |
+.BR \-execdir , |
1813 |
+.B \-ok |
1814 |
+and |
1815 |
+.BR \-okdir . |
1816 |
+ |
1817 |
+.IP POSIXLY_CORRECT |
1818 |
+Determines the block size used by |
1819 |
+.B \-ls |
1820 |
+and |
1821 |
+.BR \-fls . |
1822 |
+If |
1823 |
+.B POSIXLY_CORRECT |
1824 |
+is set, blocks are units of 512 bytes. Otherwise |
1825 |
+they are units of 1024 bytes. |
1826 |
+.IP |
1827 |
+Setting this variable also turns off |
1828 |
+warning messages (that is, implies |
1829 |
+.BR \-nowarn ) |
1830 |
+by default, because POSIX requires that apart from |
1831 |
+the output for |
1832 |
+.BR \-ok , |
1833 |
+all messages printed on stderr are diagnositcs and must result in a |
1834 |
+non-zero exit status. |
1835 |
+.IP |
1836 |
+When POSIXLY_CORRECT is not set, |
1837 |
+.B \-perm |
1838 |
++zzz |
1839 |
+is treated just like |
1840 |
+.B \-perm |
1841 |
+/zzz |
1842 |
+if |
1843 |
++zzz is not a valid symbolic mode. When POSIXLY_CORRECT is set, such |
1844 |
+constructs are treated as an error. |
1845 |
+ |
1846 |
+.IP TZ |
1847 |
+Affects the time zone used for some of the time-related format |
1848 |
+directives of |
1849 |
+.B \-printf |
1850 |
+and |
1851 |
+.BR \-fprintf . |
1852 |
+.SH "EXAMPLES" |
1853 |
+.nf |
1854 |
+.B find /tmp \-name core \-type f \-print | xargs /bin/rm \-f |
1855 |
+ |
1856 |
+.fi |
1857 |
+Find files named |
1858 |
+.B core |
1859 |
+in or below the directory |
1860 |
+.B /tmp |
1861 |
+and delete them. Note that this will work incorrectly if there are |
1862 |
+any filenames containing newlines, single or double quotes, or spaces. |
1863 |
+.P |
1864 |
+.B find /tmp \-name core \-type f \-print0 | xargs \-0 /bin/rm \-f |
1865 |
+ |
1866 |
+.fi |
1867 |
+Find files named |
1868 |
+.B core |
1869 |
+in or below the directory |
1870 |
+.B /tmp |
1871 |
+and delete them, processing filenames in such a way that file or |
1872 |
+directory names containing single or double quotes, spaces or newlines |
1873 |
+are correctly handled. The |
1874 |
+.B \-name |
1875 |
+test comes before the |
1876 |
+.B \-type |
1877 |
+test in order to avoid having to call |
1878 |
+.B stat(2) |
1879 |
+on every file. |
1880 |
+ |
1881 |
+.P |
1882 |
+.nf |
1883 |
+.B find . \-type f \-exec file \(aq{}\(aq \e\; |
1884 |
+ |
1885 |
+.fi |
1886 |
+Runs `file' on every file in or below the current directory. Notice |
1887 |
+that the braces are enclosed in single quote marks to protect them |
1888 |
+from interpretation as shell script punctuation. The semicolon is |
1889 |
+similarly protected by the use of a backslash, though single quotes |
1890 |
+could have been used in that case also. |
1891 |
+ |
1892 |
+.P |
1893 |
+.nf |
1894 |
+.B find / \e |
1895 |
+.B \e( \-perm \-4000 \-fprintf /root/suid.txt "%#m %u %p\en" \e) , \e |
1896 |
+.B \e( \-size +100M \-fprintf /root/big.txt "%\-10s %p\en" \e) |
1897 |
+ |
1898 |
+.fi |
1899 |
+Traverse the filesystem just once, listing setuid files and |
1900 |
+directories into |
1901 |
+.B /root/suid.txt |
1902 |
+and large files into |
1903 |
+.BR /root/big.txt . |
1904 |
+ |
1905 |
+.P |
1906 |
+.nf |
1907 |
+.B find $HOME \-mtime 0 |
1908 |
+ |
1909 |
+.fi |
1910 |
+Search for files in your home directory which have been modified in |
1911 |
+the last twenty-four hours. This command works this way because the |
1912 |
+time since each file was last modified is divided by 24 hours and any |
1913 |
+remainder is discarded. That means that to match |
1914 |
+.B \-mtime |
1915 |
+.BR 0 , |
1916 |
+a file will have to have a modification in the past which is less than |
1917 |
+24 hours ago. |
1918 |
+ |
1919 |
+.P |
1920 |
+.nf |
1921 |
+.B find /sbin /usr/sbin -executable \e! -readable \-print |
1922 |
+ |
1923 |
+.fi |
1924 |
+Search for files which are executable but not readable. |
1925 |
+ |
1926 |
+.P |
1927 |
+.nf |
1928 |
+.B find . \-perm 664 |
1929 |
+ |
1930 |
+.fi |
1931 |
+Search for files which have read and write permission for their owner, |
1932 |
+and group, but which other users can read but not write to. Files |
1933 |
+which meet these criteria but have other permissions bits set (for |
1934 |
+example if someone can execute the file) will not be matched. |
1935 |
+ |
1936 |
+.P |
1937 |
+.nf |
1938 |
+.B find . \-perm \-664 |
1939 |
+ |
1940 |
+.fi |
1941 |
+Search for files which have read and write permission for their owner |
1942 |
+and group, and which other users can read, without regard to the |
1943 |
+presence of any extra permission bits (for example the executable |
1944 |
+bit). This will match a file which has mode 0777, for example. |
1945 |
+ |
1946 |
+.P |
1947 |
+.nf |
1948 |
+.B find . \-perm /222 |
1949 |
+ |
1950 |
+.fi |
1951 |
+Search for files which are writable by somebody (their owner, or |
1952 |
+their group, or anybody else). |
1953 |
+ |
1954 |
+.P |
1955 |
+.nf |
1956 |
+.B find . \-perm /220 |
1957 |
+.B find . \-perm /u+w,g+w |
1958 |
+.B find . \-perm /u=w,g=w |
1959 |
+ |
1960 |
+.fi |
1961 |
+All three of these commands do the same thing, but the first one uses |
1962 |
+the octal representation of the file mode, and the other two use the |
1963 |
+symbolic form. These commands all search for files which are |
1964 |
+writable by either their owner or their group. The files don't have |
1965 |
+to be writable by both the owner and group to be matched; either will |
1966 |
+do. |
1967 |
+ |
1968 |
+.P |
1969 |
+.nf |
1970 |
+.B find . \-perm \-220 |
1971 |
+.B find . \-perm \-g+w,u+w |
1972 |
+ |
1973 |
+.fi |
1974 |
+Both these commands do the same thing; search for files which are |
1975 |
+writable by both their owner and their group. |
1976 |
+ |
1977 |
+.P |
1978 |
+.nf |
1979 |
+.B find . \-perm \-444 \-perm /222 ! \-perm /111 |
1980 |
+.B find . \-perm \-a+r \-perm /a+w ! \-perm /a+x |
1981 |
+ |
1982 |
+.fi |
1983 |
+These two commands both search for files that are readable for |
1984 |
+everybody ( |
1985 |
+.B \-perm \-444 |
1986 |
+or |
1987 |
+.BR "\-perm \-a+r" ), |
1988 |
+have at least one write bit |
1989 |
+set ( |
1990 |
+.B \-perm /222 |
1991 |
+or |
1992 |
+.BR "\-perm /a+w" ) |
1993 |
+but are not executable for anybody ( |
1994 |
+.B ! \-perm /111 |
1995 |
+and |
1996 |
+.B ! \-perm /a+x |
1997 |
+respectively). |
1998 |
+ |
1999 |
+.P |
2000 |
+.nf |
2001 |
+.B cd /source-dir |
2002 |
+.B find . \-name .snapshot \-prune \-o \e( \e! \-name "*~" \-print0 \e)| |
2003 |
+.B cpio \-pmd0 /dest-dir |
2004 |
+ |
2005 |
+.fi |
2006 |
+This command copies the contents of |
2007 |
+.B /source-dir |
2008 |
+to |
2009 |
+.BR /dest-dir , |
2010 |
+but omits files and directories named |
2011 |
+.B .snapshot |
2012 |
+(and anything in them). It also omits files or directories whose name |
2013 |
+ends in |
2014 |
+.BR ~ , |
2015 |
+but not their contents. The construct |
2016 |
+.B \-prune \-o \e( ... \-print0 \e) |
2017 |
+is quite common. The idea here is that the expression before |
2018 |
+.B \-prune |
2019 |
+matches things which are to be pruned. However, the |
2020 |
+.B \-prune |
2021 |
+action itself returns true, so the following |
2022 |
+.B \-o |
2023 |
+ensures that the right hand side is evaluated only for those |
2024 |
+directories which didn't get pruned (the contents of the pruned |
2025 |
+directories are not even visited, so their contents are irrelevant). |
2026 |
+The expression on the right hand side of the |
2027 |
+.B \-o |
2028 |
+is in parentheses only for clarity. It emphasises that the |
2029 |
+.B \-print0 |
2030 |
+action takes place only for things that didn't have |
2031 |
+.B \-prune |
2032 |
+applied to them. Because the default `and' condition between tests |
2033 |
+binds more tightly than |
2034 |
+.BR \-o , |
2035 |
+this is the default anyway, but the parentheses help to show |
2036 |
+what is going on. |
2037 |
+ |
2038 |
+.SH EXIT STATUS |
2039 |
+.PP |
2040 |
+.B find |
2041 |
+exits with status 0 if all files are processed successfully, greater |
2042 |
+than 0 if errors occur. This is deliberately a very broad |
2043 |
+description, but if the return value is non-zero, you should not rely |
2044 |
+on the correctness of the results of |
2045 |
+.BR find . |
2046 |
+ |
2047 |
+.SH "SEE ALSO" |
2048 |
+\fBlocate\fP(1), \fBlocatedb\fP(5), \fBupdatedb\fP(1), \fBxargs\fP(1), |
2049 |
+\fBchmod\fP(1), \fBfnmatch\fP(3), \fBregex\fP(7), \fBstat\fP(2), |
2050 |
+\fBlstat\fP(2), \fBls\fP(1), \fBprintf\fP(3), \fBstrftime\fP(3), |
2051 |
+\fBctime\fP(3), \fBFinding Files\fP (on-line in Info, or printed). |
2052 |
+.SH "HISTORY" |
2053 |
+As of findutils-4.2.2, shell metacharacters (`*', `?' or `[]' for |
2054 |
+example) used in filename patterns will match a leading `.', because |
2055 |
+IEEE POSIX interpretation 126 requires this. |
2056 |
+.P |
2057 |
+The syntax |
2058 |
+\.B \-perm +MODE |
2059 |
+was deprecated in findutils-4.2.21, in favour of |
2060 |
+\.B \-perm |
2061 |
+.BR /MODE . |
2062 |
+As of findutils-4.3.3, |
2063 |
+.B \-perm /000 |
2064 |
+now matches all files instead of none. |
2065 |
+.P |
2066 |
+Nanosecond-resolution |
2067 |
+timestamps were implemented in findutils-4.3.3. |
2068 |
+.P |
2069 |
+As of findutils-4.3.11, the |
2070 |
+.B \-delete |
2071 |
+action sets |
2072 |
+.BR find 's |
2073 |
+exit status to a nonzero value when it fails. |
2074 |
+However, |
2075 |
+.B find |
2076 |
+will not exit immediately. Previously, |
2077 |
+.BR find 's |
2078 |
+exit status was unaffected by the failure of |
2079 |
+.BR \-delete . |
2080 |
+.TS |
2081 |
+l l l . |
2082 |
+Feature Added in Also occurs in |
2083 |
+\-newerXY 4.3.3 BSD |
2084 |
+\-D 4.3.1 |
2085 |
+\-O 4.3.1 |
2086 |
+\-readable 4.3.0 |
2087 |
+\-writable 4.3.0 |
2088 |
+\-executable 4.3.0 |
2089 |
+\-regextype 4.2.24 |
2090 |
+\-exec ... + 4.2.12 POSIX |
2091 |
+\-execdir 4.2.12 BSD |
2092 |
+\-okdir 4.2.12 |
2093 |
+\-samefile 4.2.11 |
2094 |
+\-H 4.2.5 POSIX |
2095 |
+\-L 4.2.5 POSIX |
2096 |
+\-P 4.2.5 BSD |
2097 |
+\-delete 4.2.3 |
2098 |
+\-quit 4.2.3 |
2099 |
+\-d 4.2.3 BSD |
2100 |
+\-wholename 4.2.0 |
2101 |
+\-iwholename 4.2.0 |
2102 |
+\-ignore_readdir_race 4.2.0 |
2103 |
+\-fls 4.0 |
2104 |
+\-ilname 3.8 |
2105 |
+\-iname 3.8 |
2106 |
+\-ipath 3.8 |
2107 |
+\-iregex 3.8 |
2108 |
+.TE |
2109 |
+.SH "NON-BUGS" |
2110 |
+.nf |
2111 |
+.B $ find . \-name *.c \-print |
2112 |
+find: paths must precede expression |
2113 |
+Usage: find [\-H] [\-L] [\-P] [\-Olevel] [\-D help|tree|search|stat|rates|opt|exec] [path...] [expression] |
2114 |
+.fi |
2115 |
+.P |
2116 |
+This happens because |
2117 |
+.I *.c |
2118 |
+has been expanded by the shell |
2119 |
+resulting in |
2120 |
+.B find |
2121 |
+actually receiving a command line like this: |
2122 |
+.nf |
2123 |
+ |
2124 |
+.B find . \-name bigram.c code.c frcode.c locate.c \-print |
2125 |
+ |
2126 |
+.fi |
2127 |
+That command is of course not going to work. Instead of doing things |
2128 |
+this way, you should enclose the pattern in quotes or escape the wildcard: |
2129 |
+.nf |
2130 |
+.B $ find . \-name \e*.c \-print |
2131 |
+.fi |
2132 |
+ |
2133 |
+.SH "BUGS" |
2134 |
+.P |
2135 |
+There are security problems inherent in the behaviour that the POSIX |
2136 |
+standard specifies for |
2137 |
+.BR find , |
2138 |
+which therefore cannot be fixed. For example, the |
2139 |
+.B \-exec |
2140 |
+action is |
2141 |
+inherently insecure, and |
2142 |
+.B \-execdir |
2143 |
+should be used instead. |
2144 |
+Please see \fBFinding Files\fP for more information. |
2145 |
+.P |
2146 |
+The environment variable |
2147 |
+.B LC_COLLATE |
2148 |
+has no effect on the |
2149 |
+.B \-ok |
2150 |
+action. |
2151 |
+.P |
2152 |
+The best way to report a bug is to use the form at |
2153 |
+http://savannah.gnu.org/bugs/?group=findutils. |
2154 |
+The reason for this is that you will then be able to track progress in |
2155 |
+fixing the problem. Other comments about \fBfind\fP(1) and about |
2156 |
+the findutils package in general can be sent to the |
2157 |
+.I bug\-findutils |
2158 |
+mailing list. To join the list, send email to |
2159 |
+.IR bug\-findutils\-request@×××.org . |
2160 |
diff -purN findutils-4.3.12.orig/find/find.c findutils-4.3.12/find/find.c |
2161 |
--- findutils-4.3.12.orig/find/find.c 2007-12-19 16:12:34.000000000 -0500 |
2162 |
+++ findutils-4.3.12/find/find.c 2008-01-30 08:46:05.754843619 -0500 |
2163 |
@@ -1270,7 +1270,7 @@ process_path (char *pathname, char *name |
2164 |
static void |
2165 |
process_dir (char *pathname, char *name, int pathlen, const struct stat *statp, char *parent) |
2166 |
{ |
2167 |
- int subdirs_left; /* Number of unexamined subdirs in PATHNAME. */ |
2168 |
+ int subdirs_left=0; /* Number of unexamined subdirs in PATHNAME. */ |
2169 |
boolean subdirs_unreliable; /* if true, cannot use dir link count as subdir limif (if false, it may STILL be unreliable) */ |
2170 |
unsigned int idx; /* Which entry are we on? */ |
2171 |
struct stat stat_buf; |
2172 |
diff -purN findutils-4.3.12.orig/find/find.c.orig findutils-4.3.12/find/find.c.orig |
2173 |
--- findutils-4.3.12.orig/find/find.c.orig 1969-12-31 19:00:00.000000000 -0500 |
2174 |
+++ findutils-4.3.12/find/find.c.orig 2007-12-19 16:12:34.000000000 -0500 |
2175 |
@@ -0,0 +1,1537 @@ |
2176 |
+/* find -- search for files in a directory hierarchy |
2177 |
+ Copyright (C) 1990, 91, 92, 93, 94, 2000, |
2178 |
+ 2003, 2004, 2005, 2007 Free Software Foundation, Inc. |
2179 |
+ |
2180 |
+ This program is free software: you can redistribute it and/or modify |
2181 |
+ it under the terms of the GNU General Public License as published by |
2182 |
+ the Free Software Foundation, either version 3 of the License, or |
2183 |
+ (at your option) any later version. |
2184 |
+ |
2185 |
+ This program is distributed in the hope that it will be useful, |
2186 |
+ but WITHOUT ANY WARRANTY; without even the implied warranty of |
2187 |
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2188 |
+ GNU General Public License for more details. |
2189 |
+ |
2190 |
+ You should have received a copy of the GNU General Public License |
2191 |
+ along with this program. If not, see <http://www.gnu.org/licenses/>. |
2192 |
+*/ |
2193 |
+/* GNU find was written by Eric Decker <cire@×××××.com>, |
2194 |
+ with enhancements by David MacKenzie <djm@×××.org>, |
2195 |
+ Jay Plett <jay@××××××××××××××××××××.us>, |
2196 |
+ and Tim Wood <axolotl!tim@××××.com>. |
2197 |
+ The idea for -print0 and xargs -0 came from |
2198 |
+ Dan Bernstein <brnstnd@×××××××××××××××.edu>. |
2199 |
+ Improvements have been made by James Youngman <jay@×××.org>. |
2200 |
+*/ |
2201 |
+ |
2202 |
+ |
2203 |
+#include <config.h> |
2204 |
+#include "defs.h" |
2205 |
+ |
2206 |
+#define USE_SAFE_CHDIR 1 |
2207 |
+#undef STAT_MOUNTPOINTS |
2208 |
+ |
2209 |
+ |
2210 |
+#include <errno.h> |
2211 |
+#include <assert.h> |
2212 |
+ |
2213 |
+#include <sys/stat.h> |
2214 |
+#include <fcntl.h> |
2215 |
+#include <openat.h> |
2216 |
+ |
2217 |
+#include "xalloc.h" |
2218 |
+#include "human.h" |
2219 |
+#include "canonicalize.h" |
2220 |
+#include <modetype.h> |
2221 |
+ |
2222 |
+#include "closein.h" |
2223 |
+#include "savedirinfo.h" |
2224 |
+#include "buildcmd.h" |
2225 |
+#include "dirname.h" |
2226 |
+#include "quote.h" |
2227 |
+#include "quotearg.h" |
2228 |
+#include "xgetcwd.h" |
2229 |
+#include "error.h" |
2230 |
+ |
2231 |
+#ifdef HAVE_LOCALE_H |
2232 |
+#include <locale.h> |
2233 |
+#endif |
2234 |
+ |
2235 |
+#if ENABLE_NLS |
2236 |
+# include <libintl.h> |
2237 |
+# define _(Text) gettext (Text) |
2238 |
+#else |
2239 |
+# define _(Text) Text |
2240 |
+#define textdomain(Domain) |
2241 |
+#define bindtextdomain(Package, Directory) |
2242 |
+#define ngettext(singular,plural,n) ((1==n) ? singular : plural) |
2243 |
+#endif |
2244 |
+#ifdef gettext_noop |
2245 |
+# define N_(String) gettext_noop (String) |
2246 |
+#else |
2247 |
+/* See locate.c for explanation as to why not use (String) */ |
2248 |
+# define N_(String) String |
2249 |
+#endif |
2250 |
+ |
2251 |
+#ifdef STAT_MOUNTPOINTS |
2252 |
+static void init_mounted_dev_list(int mandatory); |
2253 |
+#endif |
2254 |
+ |
2255 |
+static void process_top_path PARAMS((char *pathname, mode_t mode)); |
2256 |
+static int process_path PARAMS((char *pathname, char *name, boolean leaf, char *parent, mode_t type)); |
2257 |
+static void process_dir PARAMS((char *pathname, char *name, int pathlen, const struct stat *statp, char *parent)); |
2258 |
+ |
2259 |
+ |
2260 |
+ |
2261 |
+/* Name this program was run with. */ |
2262 |
+char *program_name; |
2263 |
+ |
2264 |
+/* A file descriptor open to the initial working directory. |
2265 |
+ Doing it this way allows us to work when the i.w.d. has |
2266 |
+ unreadable parents. */ |
2267 |
+int starting_desc; |
2268 |
+ |
2269 |
+/* The stat buffer of the initial working directory. */ |
2270 |
+static struct stat starting_stat_buf; |
2271 |
+ |
2272 |
+enum ChdirSymlinkHandling |
2273 |
+ { |
2274 |
+ SymlinkHandleDefault, /* Normally the right choice */ |
2275 |
+ SymlinkFollowOk /* see comment in process_top_path() */ |
2276 |
+ }; |
2277 |
+ |
2278 |
+ |
2279 |
+enum TraversalDirection |
2280 |
+ { |
2281 |
+ TraversingUp, |
2282 |
+ TraversingDown |
2283 |
+ }; |
2284 |
+ |
2285 |
+enum WdSanityCheckFatality |
2286 |
+ { |
2287 |
+ FATAL_IF_SANITY_CHECK_FAILS, |
2288 |
+ RETRY_IF_SANITY_CHECK_FAILS, |
2289 |
+ NON_FATAL_IF_SANITY_CHECK_FAILS |
2290 |
+ }; |
2291 |
+ |
2292 |
+ |
2293 |
+int get_current_dirfd(void) |
2294 |
+{ |
2295 |
+ return AT_FDCWD; |
2296 |
+} |
2297 |
+ |
2298 |
+ |
2299 |
+int |
2300 |
+main (int argc, char **argv) |
2301 |
+{ |
2302 |
+ int i; |
2303 |
+ int end_of_leading_options = 0; /* First arg after any -H/-L etc. */ |
2304 |
+ struct predicate *eval_tree; |
2305 |
+ |
2306 |
+ program_name = argv[0]; |
2307 |
+ state.exit_status = 0; |
2308 |
+ |
2309 |
+ /* Set the option defaults before we do the locale |
2310 |
+ * initialisation as check_nofollow() needs to be executed in the |
2311 |
+ * POSIX locale. |
2312 |
+ */ |
2313 |
+ set_option_defaults(&options); |
2314 |
+ |
2315 |
+#ifdef HAVE_SETLOCALE |
2316 |
+ setlocale (LC_ALL, ""); |
2317 |
+#endif |
2318 |
+ bindtextdomain (PACKAGE, LOCALEDIR); |
2319 |
+ textdomain (PACKAGE); |
2320 |
+ atexit (close_stdin); |
2321 |
+ |
2322 |
+ /* Check for -P, -H or -L options. */ |
2323 |
+ end_of_leading_options = process_leading_options(argc, argv); |
2324 |
+ |
2325 |
+ if (options.debug_options & DebugStat) |
2326 |
+ options.xstat = debug_stat; |
2327 |
+ |
2328 |
+#ifdef DEBUG |
2329 |
+ fprintf (stderr, "cur_day_start = %s", ctime (&options.cur_day_start)); |
2330 |
+#endif /* DEBUG */ |
2331 |
+ |
2332 |
+ /* state.cwd_dir_fd has to be initialised before we call build_expression_tree() |
2333 |
+ * because command-line parsing may lead us to stat some files. |
2334 |
+ */ |
2335 |
+ state.cwd_dir_fd = AT_FDCWD; |
2336 |
+ |
2337 |
+ /* We are now processing the part of the "find" command line |
2338 |
+ * after the -H/-L options (if any). |
2339 |
+ */ |
2340 |
+ eval_tree = build_expression_tree(argc, argv, end_of_leading_options); |
2341 |
+ |
2342 |
+ |
2343 |
+ /* safely_chdir() needs to check that it has ended up in the right place. |
2344 |
+ * To avoid bailing out when something gets automounted, it checks if |
2345 |
+ * the target directory appears to have had a directory mounted on it as |
2346 |
+ * we chdir()ed. The problem with this is that in order to notice that |
2347 |
+ * a file system was mounted, we would need to lstat() all the mount points. |
2348 |
+ * That strategy loses if our machine is a client of a dead NFS server. |
2349 |
+ * |
2350 |
+ * Hence if safely_chdir() and wd_sanity_check() can manage without needing |
2351 |
+ * to know the mounted device list, we do that. |
2352 |
+ */ |
2353 |
+ if (!options.open_nofollow_available) |
2354 |
+ { |
2355 |
+#ifdef STAT_MOUNTPOINTS |
2356 |
+ init_mounted_dev_list(0); |
2357 |
+#endif |
2358 |
+ } |
2359 |
+ |
2360 |
+ |
2361 |
+ starting_desc = open (".", O_RDONLY |
2362 |
+#if defined O_LARGEFILE |
2363 |
+ |O_LARGEFILE |
2364 |
+#endif |
2365 |
+ ); |
2366 |
+ if (0 <= starting_desc && fchdir (starting_desc) != 0) |
2367 |
+ { |
2368 |
+ close (starting_desc); |
2369 |
+ starting_desc = -1; |
2370 |
+ } |
2371 |
+ |
2372 |
+ if (starting_desc < 0) |
2373 |
+ { |
2374 |
+ starting_dir = xgetcwd (); |
2375 |
+ if (! starting_dir) |
2376 |
+ error (1, errno, _("cannot get current directory")); |
2377 |
+ } |
2378 |
+ set_stat_placeholders(&starting_stat_buf); |
2379 |
+ if ((*options.xstat) (".", &starting_stat_buf) != 0) |
2380 |
+ error (1, errno, _("cannot stat current directory")); |
2381 |
+ |
2382 |
+ /* If no paths are given, default to ".". */ |
2383 |
+ for (i = end_of_leading_options; i < argc && !looks_like_expression(argv[i], true); i++) |
2384 |
+ { |
2385 |
+ process_top_path (argv[i], 0); |
2386 |
+ } |
2387 |
+ |
2388 |
+ /* If there were no path arguments, default to ".". */ |
2389 |
+ if (i == end_of_leading_options) |
2390 |
+ { |
2391 |
+ /* |
2392 |
+ * We use a temporary variable here because some actions modify |
2393 |
+ * the path temporarily. Hence if we use a string constant, |
2394 |
+ * we get a coredump. The best example of this is if we say |
2395 |
+ * "find -printf %H" (note, not "find . -printf %H"). |
2396 |
+ */ |
2397 |
+ char defaultpath[2] = "."; |
2398 |
+ process_top_path (defaultpath, 0); |
2399 |
+ } |
2400 |
+ |
2401 |
+ /* If "-exec ... {} +" has been used, there may be some |
2402 |
+ * partially-full command lines which have been built, |
2403 |
+ * but which are not yet complete. Execute those now. |
2404 |
+ */ |
2405 |
+ show_success_rates(eval_tree); |
2406 |
+ cleanup(); |
2407 |
+ return state.exit_status; |
2408 |
+} |
2409 |
+ |
2410 |
+boolean is_fts_enabled(int *ftsoptions) |
2411 |
+{ |
2412 |
+ /* this version of find (i.e. this main()) does not use fts. */ |
2413 |
+ *ftsoptions = 0; |
2414 |
+ return false; |
2415 |
+} |
2416 |
+ |
2417 |
+ |
2418 |
+static char * |
2419 |
+specific_dirname(const char *dir) |
2420 |
+{ |
2421 |
+ char dirbuf[1024]; |
2422 |
+ |
2423 |
+ if (0 == strcmp(".", dir)) |
2424 |
+ { |
2425 |
+ /* OK, what's '.'? */ |
2426 |
+ if (NULL != getcwd(dirbuf, sizeof(dirbuf))) |
2427 |
+ { |
2428 |
+ return strdup(dirbuf); |
2429 |
+ } |
2430 |
+ else |
2431 |
+ { |
2432 |
+ return strdup(dir); |
2433 |
+ } |
2434 |
+ } |
2435 |
+ else |
2436 |
+ { |
2437 |
+ char *result = canonicalize_filename_mode(dir, CAN_EXISTING); |
2438 |
+ if (NULL == result) |
2439 |
+ return strdup(dir); |
2440 |
+ else |
2441 |
+ return result; |
2442 |
+ } |
2443 |
+} |
2444 |
+ |
2445 |
+ |
2446 |
+ |
2447 |
+/* Return non-zero if FS is the name of a file system that is likely to |
2448 |
+ * be automounted |
2449 |
+ */ |
2450 |
+static int |
2451 |
+fs_likely_to_be_automounted(const char *fs) |
2452 |
+{ |
2453 |
+ return ( (0==strcmp(fs, "nfs")) || (0==strcmp(fs, "autofs")) || (0==strcmp(fs, "subfs"))); |
2454 |
+} |
2455 |
+ |
2456 |
+ |
2457 |
+ |
2458 |
+#ifdef STAT_MOUNTPOINTS |
2459 |
+static dev_t *mounted_devices = NULL; |
2460 |
+static size_t num_mounted_devices = 0u; |
2461 |
+ |
2462 |
+ |
2463 |
+static void |
2464 |
+init_mounted_dev_list(int mandatory) |
2465 |
+{ |
2466 |
+ assert (NULL == mounted_devices); |
2467 |
+ assert (0 == num_mounted_devices); |
2468 |
+ mounted_devices = get_mounted_devices(&num_mounted_devices); |
2469 |
+ if (mandatory && (NULL == mounted_devices)) |
2470 |
+ { |
2471 |
+ error(1, 0, "Cannot read list of mounted devices."); |
2472 |
+ } |
2473 |
+} |
2474 |
+ |
2475 |
+static void |
2476 |
+refresh_mounted_dev_list(void) |
2477 |
+{ |
2478 |
+ if (mounted_devices) |
2479 |
+ { |
2480 |
+ free(mounted_devices); |
2481 |
+ mounted_devices = 0; |
2482 |
+ } |
2483 |
+ num_mounted_devices = 0u; |
2484 |
+ init_mounted_dev_list(1); |
2485 |
+} |
2486 |
+ |
2487 |
+ |
2488 |
+/* Search for device DEV in the array LIST, which is of size N. */ |
2489 |
+static int |
2490 |
+dev_present(dev_t dev, const dev_t *list, size_t n) |
2491 |
+{ |
2492 |
+ if (list) |
2493 |
+ { |
2494 |
+ while (n-- > 0u) |
2495 |
+ { |
2496 |
+ if ( (*list++) == dev ) |
2497 |
+ return 1; |
2498 |
+ } |
2499 |
+ } |
2500 |
+ return 0; |
2501 |
+} |
2502 |
+ |
2503 |
+enum MountPointStateChange |
2504 |
+ { |
2505 |
+ MountPointRecentlyMounted, |
2506 |
+ MountPointRecentlyUnmounted, |
2507 |
+ MountPointStateUnchanged |
2508 |
+ }; |
2509 |
+ |
2510 |
+ |
2511 |
+ |
2512 |
+static enum MountPointStateChange |
2513 |
+get_mount_state(dev_t newdev) |
2514 |
+{ |
2515 |
+ int new_is_present, new_was_present; |
2516 |
+ |
2517 |
+ new_was_present = dev_present(newdev, mounted_devices, num_mounted_devices); |
2518 |
+ refresh_mounted_dev_list(); |
2519 |
+ new_is_present = dev_present(newdev, mounted_devices, num_mounted_devices); |
2520 |
+ |
2521 |
+ if (new_was_present == new_is_present) |
2522 |
+ return MountPointStateUnchanged; |
2523 |
+ else if (new_is_present) |
2524 |
+ return MountPointRecentlyMounted; |
2525 |
+ else |
2526 |
+ return MountPointRecentlyUnmounted; |
2527 |
+} |
2528 |
+ |
2529 |
+ |
2530 |
+ |
2531 |
+/* We stat()ed a directory, chdir()ed into it (we know this |
2532 |
+ * since direction is TraversingDown), stat()ed it again, |
2533 |
+ * and noticed that the device numbers are different. Check |
2534 |
+ * if the file system was recently mounted. |
2535 |
+ * |
2536 |
+ * If it was, it looks like chdir()ing into the directory |
2537 |
+ * caused a file system to be mounted. Maybe automount is |
2538 |
+ * running. Anyway, that's probably OK - but it happens |
2539 |
+ * only when we are moving downward. |
2540 |
+ * |
2541 |
+ * We also allow for the possibility that a similar thing |
2542 |
+ * has happened with the unmounting of a file system. This |
2543 |
+ * is much rarer, as it relies on an automounter timeout |
2544 |
+ * occurring at exactly the wrong moment. |
2545 |
+ */ |
2546 |
+static enum WdSanityCheckFatality |
2547 |
+dirchange_is_fatal(const char *specific_what, |
2548 |
+ enum WdSanityCheckFatality isfatal, |
2549 |
+ int silent, |
2550 |
+ struct stat *newinfo) |
2551 |
+{ |
2552 |
+ enum MountPointStateChange transition = get_mount_state(newinfo->st_dev); |
2553 |
+ switch (transition) |
2554 |
+ { |
2555 |
+ case MountPointRecentlyUnmounted: |
2556 |
+ isfatal = NON_FATAL_IF_SANITY_CHECK_FAILS; |
2557 |
+ if (!silent) |
2558 |
+ { |
2559 |
+ error (0, 0, |
2560 |
+ _("Warning: file system %s has recently been unmounted."), |
2561 |
+ safely_quote_err_filename(0, specific_what)); |
2562 |
+ } |
2563 |
+ break; |
2564 |
+ |
2565 |
+ case MountPointRecentlyMounted: |
2566 |
+ isfatal = NON_FATAL_IF_SANITY_CHECK_FAILS; |
2567 |
+ if (!silent) |
2568 |
+ { |
2569 |
+ error (0, 0, |
2570 |
+ _("Warning: file system %s has recently been mounted."), |
2571 |
+ safely_quote_err_filename(0, specific_what)); |
2572 |
+ } |
2573 |
+ break; |
2574 |
+ |
2575 |
+ case MountPointStateUnchanged: |
2576 |
+ /* leave isfatal as it is */ |
2577 |
+ break; |
2578 |
+ } |
2579 |
+ |
2580 |
+ return isfatal; |
2581 |
+} |
2582 |
+ |
2583 |
+ |
2584 |
+#endif |
2585 |
+ |
2586 |
+ |
2587 |
+ |
2588 |
+/* Examine the results of the stat() of a directory from before we |
2589 |
+ * entered or left it, with the results of stat()ing it afterward. If |
2590 |
+ * these are different, the file system tree has been modified while we |
2591 |
+ * were traversing it. That might be an attempt to use a race |
2592 |
+ * condition to persuade find to do something it didn't intend |
2593 |
+ * (e.g. an attempt by an ordinary user to exploit the fact that root |
2594 |
+ * sometimes runs find on the whole file system). However, this can |
2595 |
+ * also happen if automount is running (certainly on Solaris). With |
2596 |
+ * automount, moving into a directory can cause a file system to be |
2597 |
+ * mounted there. |
2598 |
+ * |
2599 |
+ * To cope sensibly with this, we will raise an error if we see the |
2600 |
+ * device number change unless we are chdir()ing into a subdirectory, |
2601 |
+ * and the directory we moved into has been mounted or unmounted "recently". |
2602 |
+ * Here "recently" means since we started "find" or we last re-read |
2603 |
+ * the /etc/mnttab file. |
2604 |
+ * |
2605 |
+ * If the device number does not change but the inode does, that is a |
2606 |
+ * problem. |
2607 |
+ * |
2608 |
+ * If the device number and inode are both the same, we are happy. |
2609 |
+ * |
2610 |
+ * If a file system is (un)mounted as we chdir() into the directory, that |
2611 |
+ * may mean that we're now examining a section of the file system that might |
2612 |
+ * have been excluded from consideration (via -prune or -quit for example). |
2613 |
+ * Hence we print a warning message to indicate that the output of find |
2614 |
+ * might be inconsistent due to the change in the file system. |
2615 |
+ */ |
2616 |
+static boolean |
2617 |
+wd_sanity_check(const char *thing_to_stat, |
2618 |
+ const char *progname, |
2619 |
+ const char *what, |
2620 |
+ dev_t old_dev, |
2621 |
+ ino_t old_ino, |
2622 |
+ struct stat *newinfo, |
2623 |
+ int parent, |
2624 |
+ int line_no, |
2625 |
+ enum TraversalDirection direction, |
2626 |
+ enum WdSanityCheckFatality isfatal, |
2627 |
+ boolean *changed) /* output parameter */ |
2628 |
+{ |
2629 |
+ const char *fstype; |
2630 |
+ char *specific_what = NULL; |
2631 |
+ int silent = 0; |
2632 |
+ const char *current_dir = "."; |
2633 |
+ |
2634 |
+ *changed = false; |
2635 |
+ |
2636 |
+ set_stat_placeholders(newinfo); |
2637 |
+ if ((*options.xstat) (current_dir, newinfo) != 0) |
2638 |
+ fatal_file_error(thing_to_stat); |
2639 |
+ |
2640 |
+ if (old_dev != newinfo->st_dev) |
2641 |
+ { |
2642 |
+ *changed = true; |
2643 |
+ specific_what = specific_dirname(what); |
2644 |
+ fstype = filesystem_type(newinfo, current_dir); |
2645 |
+ silent = fs_likely_to_be_automounted(fstype); |
2646 |
+ |
2647 |
+ /* This condition is rare, so once we are here it is |
2648 |
+ * reasonable to perform an expensive computation to |
2649 |
+ * determine if we should continue or fail. |
2650 |
+ */ |
2651 |
+ if (TraversingDown == direction) |
2652 |
+ { |
2653 |
+#ifdef STAT_MOUNTPOINTS |
2654 |
+ isfatal = dirchange_is_fatal(specific_what,isfatal,silent,newinfo); |
2655 |
+#else |
2656 |
+ isfatal = RETRY_IF_SANITY_CHECK_FAILS; |
2657 |
+#endif |
2658 |
+ } |
2659 |
+ |
2660 |
+ switch (isfatal) |
2661 |
+ { |
2662 |
+ case FATAL_IF_SANITY_CHECK_FAILS: |
2663 |
+ { |
2664 |
+ fstype = filesystem_type(newinfo, current_dir); |
2665 |
+ error (1, 0, |
2666 |
+ _("%1$s%2$s changed during execution of %3$s " |
2667 |
+ "(old device number %4$ld, new device number %5$ld, file system type is %6$s) [ref %7$ld]"), |
2668 |
+ safely_quote_err_filename(0, specific_what), |
2669 |
+ parent ? "/.." : "", |
2670 |
+ safely_quote_err_filename(1, progname), |
2671 |
+ (long) old_dev, |
2672 |
+ (long) newinfo->st_dev, |
2673 |
+ fstype, |
2674 |
+ (long)line_no); |
2675 |
+ /*NOTREACHED*/ |
2676 |
+ return false; |
2677 |
+ } |
2678 |
+ |
2679 |
+ case NON_FATAL_IF_SANITY_CHECK_FAILS: |
2680 |
+ { |
2681 |
+ /* Since the device has changed under us, the inode number |
2682 |
+ * will almost certainly also be different. However, we have |
2683 |
+ * already decided that this is not a problem. Hence we return |
2684 |
+ * without checking the inode number. |
2685 |
+ */ |
2686 |
+ free(specific_what); |
2687 |
+ return true; |
2688 |
+ } |
2689 |
+ |
2690 |
+ case RETRY_IF_SANITY_CHECK_FAILS: |
2691 |
+ return false; |
2692 |
+ } |
2693 |
+ } |
2694 |
+ |
2695 |
+ /* Device number was the same, check if the inode has changed. */ |
2696 |
+ if (old_ino != newinfo->st_ino) |
2697 |
+ { |
2698 |
+ *changed = true; |
2699 |
+ specific_what = specific_dirname(what); |
2700 |
+ fstype = filesystem_type(newinfo, current_dir); |
2701 |
+ |
2702 |
+ error ((isfatal == FATAL_IF_SANITY_CHECK_FAILS) ? 1 : 0, |
2703 |
+ 0, /* no relevant errno value */ |
2704 |
+ _("%1$s%2$s changed during execution of %3$s " |
2705 |
+ "(old inode number %4$ld, new inode number %5$ld, file system type is %5$s) [ref %7$ld]"), |
2706 |
+ safely_quote_err_filename(0, specific_what), |
2707 |
+ parent ? "/.." : "", |
2708 |
+ safely_quote_err_filename(1, progname), |
2709 |
+ (long) old_ino, |
2710 |
+ (long) newinfo->st_ino, |
2711 |
+ fstype, |
2712 |
+ (long)line_no); |
2713 |
+ free(specific_what); |
2714 |
+ return false; |
2715 |
+ } |
2716 |
+ |
2717 |
+ return true; |
2718 |
+} |
2719 |
+ |
2720 |
+enum SafeChdirStatus |
2721 |
+ { |
2722 |
+ SafeChdirOK, |
2723 |
+ SafeChdirFailSymlink, |
2724 |
+ SafeChdirFailNotDir, |
2725 |
+ SafeChdirFailStat, |
2726 |
+ SafeChdirFailWouldBeUnableToReturn, |
2727 |
+ SafeChdirFailChdirFailed, |
2728 |
+ SafeChdirFailNonexistent, |
2729 |
+ SafeChdirFailDestUnreadable |
2730 |
+ }; |
2731 |
+ |
2732 |
+/* Safely perform a change in directory. We do this by calling |
2733 |
+ * lstat() on the subdirectory, using chdir() to move into it, and |
2734 |
+ * then lstat()ing ".". We compare the results of the two stat calls |
2735 |
+ * to see if they are consistent. If not, we sound the alarm. |
2736 |
+ * |
2737 |
+ * If following_links() is true, we do follow symbolic links. |
2738 |
+ */ |
2739 |
+static enum SafeChdirStatus |
2740 |
+safely_chdir_lstat(const char *dest, |
2741 |
+ enum TraversalDirection direction, |
2742 |
+ struct stat *statbuf_dest, |
2743 |
+ enum ChdirSymlinkHandling symlink_follow_option, |
2744 |
+ boolean *did_stat) |
2745 |
+{ |
2746 |
+ struct stat statbuf_arrived; |
2747 |
+ int rv, dotfd=-1; |
2748 |
+ int saved_errno; /* specific_dirname() changes errno. */ |
2749 |
+ boolean rv_set = false; |
2750 |
+ boolean statflag = false; |
2751 |
+ int tries = 0; |
2752 |
+ enum WdSanityCheckFatality isfatal = RETRY_IF_SANITY_CHECK_FAILS; |
2753 |
+ |
2754 |
+ saved_errno = errno = 0; |
2755 |
+ |
2756 |
+ dotfd = open(".", O_RDONLY |
2757 |
+#if defined O_LARGEFILE |
2758 |
+ |O_LARGEFILE |
2759 |
+#endif |
2760 |
+ ); |
2761 |
+ |
2762 |
+ /* We jump back to here if wd_sanity_check() |
2763 |
+ * recoverably triggers an alert. |
2764 |
+ */ |
2765 |
+ retry: |
2766 |
+ ++tries; |
2767 |
+ |
2768 |
+ if (dotfd >= 0) |
2769 |
+ { |
2770 |
+ /* Stat the directory we're going to. */ |
2771 |
+ set_stat_placeholders(statbuf_dest); |
2772 |
+ if (0 == options.xstat(dest, statbuf_dest)) |
2773 |
+ { |
2774 |
+ statflag = true; |
2775 |
+ |
2776 |
+#ifdef S_ISLNK |
2777 |
+ /* symlink_follow_option might be set to SymlinkFollowOk, which |
2778 |
+ * would allow us to chdir() into a symbolic link. This is |
2779 |
+ * only useful for the case where the directory we're |
2780 |
+ * chdir()ing into is the basename of a command line |
2781 |
+ * argument, for example where "foo/bar/baz" is specified on |
2782 |
+ * the command line. When -P is in effect (the default), |
2783 |
+ * baz will not be followed if it is a symlink, but if bar |
2784 |
+ * is a symlink, it _should_ be followed. Hence we need the |
2785 |
+ * ability to override the policy set by following_links(). |
2786 |
+ */ |
2787 |
+ if (!following_links() && S_ISLNK(statbuf_dest->st_mode)) |
2788 |
+ { |
2789 |
+ /* We're not supposed to be following links, but this is |
2790 |
+ * a link. Check symlink_follow_option to see if we should |
2791 |
+ * make a special exception. |
2792 |
+ */ |
2793 |
+ if (symlink_follow_option == SymlinkFollowOk) |
2794 |
+ { |
2795 |
+ /* We need to re-stat() the file so that the |
2796 |
+ * sanity check can pass. |
2797 |
+ */ |
2798 |
+ if (0 != stat(dest, statbuf_dest)) |
2799 |
+ { |
2800 |
+ rv = SafeChdirFailNonexistent; |
2801 |
+ rv_set = true; |
2802 |
+ saved_errno = errno; |
2803 |
+ goto fail; |
2804 |
+ } |
2805 |
+ statflag = true; |
2806 |
+ } |
2807 |
+ else |
2808 |
+ { |
2809 |
+ /* Not following symlinks, so the attempt to |
2810 |
+ * chdir() into a symlink should be prevented. |
2811 |
+ */ |
2812 |
+ rv = SafeChdirFailSymlink; |
2813 |
+ rv_set = true; |
2814 |
+ saved_errno = 0; /* silence the error message */ |
2815 |
+ goto fail; |
2816 |
+ } |
2817 |
+ } |
2818 |
+#endif |
2819 |
+#ifdef S_ISDIR |
2820 |
+ /* Although the immediately following chdir() would detect |
2821 |
+ * the fact that this is not a directory for us, this would |
2822 |
+ * result in an extra system call that fails. Anybody |
2823 |
+ * examining the system-call trace should ideally not be |
2824 |
+ * concerned that something is actually failing. |
2825 |
+ */ |
2826 |
+ if (!S_ISDIR(statbuf_dest->st_mode)) |
2827 |
+ { |
2828 |
+ rv = SafeChdirFailNotDir; |
2829 |
+ rv_set = true; |
2830 |
+ saved_errno = 0; /* silence the error message */ |
2831 |
+ goto fail; |
2832 |
+ } |
2833 |
+#endif |
2834 |
+ |
2835 |
+ if (options.debug_options & DebugSearch) |
2836 |
+ fprintf(stderr, "safely_chdir(): chdir(\"%s\")\n", dest); |
2837 |
+ |
2838 |
+ if (0 == chdir(dest)) |
2839 |
+ { |
2840 |
+ /* check we ended up where we wanted to go */ |
2841 |
+ boolean changed = false; |
2842 |
+ if (!wd_sanity_check(".", program_name, ".", |
2843 |
+ statbuf_dest->st_dev, |
2844 |
+ statbuf_dest->st_ino, |
2845 |
+ &statbuf_arrived, |
2846 |
+ 0, __LINE__, direction, |
2847 |
+ isfatal, |
2848 |
+ &changed)) |
2849 |
+ { |
2850 |
+ /* Only allow one failure. */ |
2851 |
+ if (RETRY_IF_SANITY_CHECK_FAILS == isfatal) |
2852 |
+ { |
2853 |
+ if (0 == fchdir(dotfd)) |
2854 |
+ { |
2855 |
+ isfatal = FATAL_IF_SANITY_CHECK_FAILS; |
2856 |
+ goto retry; |
2857 |
+ } |
2858 |
+ else |
2859 |
+ { |
2860 |
+ /* Failed to return to original directory, |
2861 |
+ * but we know that the current working |
2862 |
+ * directory is not the one that we intend |
2863 |
+ * to be in. Since fchdir() failed, we |
2864 |
+ * can't recover from this and so this error |
2865 |
+ * is fatal. |
2866 |
+ */ |
2867 |
+ error(1, errno, |
2868 |
+ "failed to return to parent directory"); |
2869 |
+ } |
2870 |
+ } |
2871 |
+ else |
2872 |
+ { |
2873 |
+ /* XXX: not sure what to use as an excuse here. */ |
2874 |
+ rv = SafeChdirFailNonexistent; |
2875 |
+ rv_set = true; |
2876 |
+ saved_errno = 0; |
2877 |
+ goto fail; |
2878 |
+ } |
2879 |
+ } |
2880 |
+ |
2881 |
+ close(dotfd); |
2882 |
+ return SafeChdirOK; |
2883 |
+ } |
2884 |
+ else |
2885 |
+ { |
2886 |
+ saved_errno = errno; |
2887 |
+ if (ENOENT == saved_errno) |
2888 |
+ { |
2889 |
+ rv = SafeChdirFailNonexistent; |
2890 |
+ rv_set = true; |
2891 |
+ if (options.ignore_readdir_race) |
2892 |
+ errno = 0; /* don't issue err msg */ |
2893 |
+ } |
2894 |
+ else if (ENOTDIR == saved_errno) |
2895 |
+ { |
2896 |
+ /* This can happen if the we stat a directory, |
2897 |
+ * and then file system activity changes it into |
2898 |
+ * a non-directory. |
2899 |
+ */ |
2900 |
+ saved_errno = 0; /* don't issue err msg */ |
2901 |
+ rv = SafeChdirFailNotDir; |
2902 |
+ rv_set = true; |
2903 |
+ } |
2904 |
+ else |
2905 |
+ { |
2906 |
+ rv = SafeChdirFailChdirFailed; |
2907 |
+ rv_set = true; |
2908 |
+ } |
2909 |
+ goto fail; |
2910 |
+ } |
2911 |
+ } |
2912 |
+ else |
2913 |
+ { |
2914 |
+ saved_errno = errno; |
2915 |
+ rv = SafeChdirFailStat; |
2916 |
+ rv_set = true; |
2917 |
+ |
2918 |
+ if ( (ENOENT == saved_errno) || (0 == state.curdepth)) |
2919 |
+ saved_errno = 0; /* don't issue err msg */ |
2920 |
+ goto fail; |
2921 |
+ } |
2922 |
+ } |
2923 |
+ else |
2924 |
+ { |
2925 |
+ /* We do not have read permissions on "." */ |
2926 |
+ rv = SafeChdirFailWouldBeUnableToReturn; |
2927 |
+ rv_set = true; |
2928 |
+ goto fail; |
2929 |
+ } |
2930 |
+ |
2931 |
+ /* This is the success path, so we clear errno. The caller probably |
2932 |
+ * won't be calling error() anyway. |
2933 |
+ */ |
2934 |
+ saved_errno = 0; |
2935 |
+ |
2936 |
+ /* We use the same exit path for success or failure. |
2937 |
+ * which has occurred is recorded in RV. |
2938 |
+ */ |
2939 |
+ fail: |
2940 |
+ /* We do not call error() as this would result in a duplicate error |
2941 |
+ * message when the caller does the same thing. |
2942 |
+ */ |
2943 |
+ if (saved_errno) |
2944 |
+ errno = saved_errno; |
2945 |
+ |
2946 |
+ if (dotfd >= 0) |
2947 |
+ { |
2948 |
+ close(dotfd); |
2949 |
+ dotfd = -1; |
2950 |
+ } |
2951 |
+ |
2952 |
+ *did_stat = statflag; |
2953 |
+ assert (rv_set); |
2954 |
+ return rv; |
2955 |
+} |
2956 |
+ |
2957 |
+#if defined O_NOFOLLOW |
2958 |
+/* Safely change working directory to the specified subdirectory. If |
2959 |
+ * we are not allowed to follow symbolic links, we use open() with |
2960 |
+ * O_NOFOLLOW, followed by fchdir(). This ensures that we don't |
2961 |
+ * follow symbolic links (of course, we do follow them if the -L |
2962 |
+ * option is in effect). |
2963 |
+ */ |
2964 |
+static enum SafeChdirStatus |
2965 |
+safely_chdir_nofollow(const char *dest, |
2966 |
+ enum TraversalDirection direction, |
2967 |
+ struct stat *statbuf_dest, |
2968 |
+ enum ChdirSymlinkHandling symlink_follow_option, |
2969 |
+ boolean *did_stat) |
2970 |
+{ |
2971 |
+ int extraflags, fd; |
2972 |
+ |
2973 |
+ (void) direction; |
2974 |
+ (void) statbuf_dest; |
2975 |
+ |
2976 |
+ extraflags = 0; |
2977 |
+ *did_stat = false; |
2978 |
+ |
2979 |
+ switch (symlink_follow_option) |
2980 |
+ { |
2981 |
+ case SymlinkFollowOk: |
2982 |
+ extraflags = 0; |
2983 |
+ break; |
2984 |
+ |
2985 |
+ case SymlinkHandleDefault: |
2986 |
+ if (following_links()) |
2987 |
+ extraflags = 0; |
2988 |
+ else |
2989 |
+ extraflags = O_NOFOLLOW; |
2990 |
+ break; |
2991 |
+ } |
2992 |
+ |
2993 |
+ errno = 0; |
2994 |
+ fd = open(dest, O_RDONLY |
2995 |
+#if defined O_LARGEFILE |
2996 |
+ |O_LARGEFILE |
2997 |
+#endif |
2998 |
+ |extraflags); |
2999 |
+ if (fd < 0) |
3000 |
+ { |
3001 |
+ switch (errno) |
3002 |
+ { |
3003 |
+ case ELOOP: |
3004 |
+ return SafeChdirFailSymlink; /* This is why we use O_NOFOLLOW */ |
3005 |
+ case ENOENT: |
3006 |
+ return SafeChdirFailNonexistent; |
3007 |
+ default: |
3008 |
+ return SafeChdirFailDestUnreadable; |
3009 |
+ } |
3010 |
+ } |
3011 |
+ |
3012 |
+ errno = 0; |
3013 |
+ if (0 == fchdir(fd)) |
3014 |
+ { |
3015 |
+ close(fd); |
3016 |
+ return SafeChdirOK; |
3017 |
+ } |
3018 |
+ else |
3019 |
+ { |
3020 |
+ int saved_errno = errno; |
3021 |
+ close(fd); |
3022 |
+ errno = saved_errno; |
3023 |
+ |
3024 |
+ switch (errno) |
3025 |
+ { |
3026 |
+ case ENOTDIR: |
3027 |
+ return SafeChdirFailNotDir; |
3028 |
+ |
3029 |
+ case EACCES: |
3030 |
+ case EBADF: /* Shouldn't happen */ |
3031 |
+ case EINTR: |
3032 |
+ case EIO: |
3033 |
+ default: |
3034 |
+ return SafeChdirFailChdirFailed; |
3035 |
+ } |
3036 |
+ } |
3037 |
+} |
3038 |
+#endif |
3039 |
+ |
3040 |
+static enum SafeChdirStatus |
3041 |
+safely_chdir(const char *dest, |
3042 |
+ enum TraversalDirection direction, |
3043 |
+ struct stat *statbuf_dest, |
3044 |
+ enum ChdirSymlinkHandling symlink_follow_option, |
3045 |
+ boolean *did_stat) |
3046 |
+{ |
3047 |
+ enum SafeChdirStatus result; |
3048 |
+ |
3049 |
+ /* We're about to leave a directory. If there are any -execdir |
3050 |
+ * argument lists which have been built but have not yet been |
3051 |
+ * processed, do them now because they must be done in the same |
3052 |
+ * directory. |
3053 |
+ */ |
3054 |
+ complete_pending_execdirs(get_current_dirfd()); |
3055 |
+ |
3056 |
+#if !defined(O_NOFOLLOW) |
3057 |
+ options.open_nofollow_available = false; |
3058 |
+#endif |
3059 |
+ if (options.open_nofollow_available) |
3060 |
+ { |
3061 |
+ result = safely_chdir_nofollow(dest, direction, statbuf_dest, |
3062 |
+ symlink_follow_option, did_stat); |
3063 |
+ if (SafeChdirFailDestUnreadable != result) |
3064 |
+ { |
3065 |
+ return result; |
3066 |
+ } |
3067 |
+ else |
3068 |
+ { |
3069 |
+ /* Savannah bug #15384: fall through to use safely_chdir_lstat |
3070 |
+ * if the directory is not readable. |
3071 |
+ */ |
3072 |
+ /* Do nothing. */ |
3073 |
+ } |
3074 |
+ } |
3075 |
+ /* Even if O_NOFOLLOW is available, we may need to use the alternative |
3076 |
+ * method, since parent of the start point may be executable but not |
3077 |
+ * readable. |
3078 |
+ */ |
3079 |
+ return safely_chdir_lstat(dest, direction, statbuf_dest, |
3080 |
+ symlink_follow_option, did_stat); |
3081 |
+} |
3082 |
+ |
3083 |
+ |
3084 |
+ |
3085 |
+/* Safely go back to the starting directory. */ |
3086 |
+static void |
3087 |
+chdir_back (void) |
3088 |
+{ |
3089 |
+ struct stat stat_buf; |
3090 |
+ boolean dummy; |
3091 |
+ |
3092 |
+ if (starting_desc < 0) |
3093 |
+ { |
3094 |
+ if (options.debug_options & DebugSearch) |
3095 |
+ fprintf(stderr, "chdir_back(): chdir(\"%s\")\n", starting_dir); |
3096 |
+ |
3097 |
+#ifdef STAT_MOUNTPOINTS |
3098 |
+ /* We will need the mounted device list. Get it now if we don't |
3099 |
+ * already have it. |
3100 |
+ */ |
3101 |
+ if (NULL == mounted_devices) |
3102 |
+ init_mounted_dev_list(1); |
3103 |
+#endif |
3104 |
+ |
3105 |
+ if (chdir (starting_dir) != 0) |
3106 |
+ fatal_file_error(starting_dir); |
3107 |
+ |
3108 |
+ wd_sanity_check(starting_dir, |
3109 |
+ program_name, |
3110 |
+ starting_dir, |
3111 |
+ starting_stat_buf.st_dev, |
3112 |
+ starting_stat_buf.st_ino, |
3113 |
+ &stat_buf, 0, __LINE__, |
3114 |
+ TraversingUp, |
3115 |
+ FATAL_IF_SANITY_CHECK_FAILS, |
3116 |
+ &dummy); |
3117 |
+ } |
3118 |
+ else |
3119 |
+ { |
3120 |
+ if (options.debug_options & DebugSearch) |
3121 |
+ fprintf(stderr, "chdir_back(): chdir(<starting-point>)\n"); |
3122 |
+ |
3123 |
+ if (fchdir (starting_desc) != 0) |
3124 |
+ { |
3125 |
+ fatal_file_error(starting_dir); |
3126 |
+ } |
3127 |
+ } |
3128 |
+} |
3129 |
+ |
3130 |
+/* Move to the parent of a given directory and then call a function, |
3131 |
+ * restoring the cwd. Don't bother changing directory if the |
3132 |
+ * specified directory is a child of "." or is the root directory. |
3133 |
+ */ |
3134 |
+static void |
3135 |
+at_top (char *pathname, |
3136 |
+ mode_t mode, |
3137 |
+ struct stat *pstat, |
3138 |
+ void (*action)(char *pathname, |
3139 |
+ char *basename, |
3140 |
+ int mode, |
3141 |
+ struct stat *pstat)) |
3142 |
+{ |
3143 |
+ int dirchange; |
3144 |
+ char *parent_dir = dir_name (pathname); |
3145 |
+ char *base = last_component (pathname); |
3146 |
+ |
3147 |
+ state.curdepth = 0; |
3148 |
+ state.starting_path_length = strlen (pathname); |
3149 |
+ |
3150 |
+ if (0 == *base |
3151 |
+ || 0 == strcmp(parent_dir, ".")) |
3152 |
+ { |
3153 |
+ dirchange = 0; |
3154 |
+ base = pathname; |
3155 |
+ } |
3156 |
+ else |
3157 |
+ { |
3158 |
+ enum TraversalDirection direction; |
3159 |
+ enum SafeChdirStatus chdir_status; |
3160 |
+ struct stat st; |
3161 |
+ boolean did_stat = false; |
3162 |
+ |
3163 |
+ dirchange = 1; |
3164 |
+ if (0 == strcmp(base, "..")) |
3165 |
+ direction = TraversingUp; |
3166 |
+ else |
3167 |
+ direction = TraversingDown; |
3168 |
+ |
3169 |
+ /* We pass SymlinkFollowOk to safely_chdir(), which allows it to |
3170 |
+ * chdir() into a symbolic link. This is only useful for the |
3171 |
+ * case where the directory we're chdir()ing into is the |
3172 |
+ * basename of a command line argument, for example where |
3173 |
+ * "foo/bar/baz" is specified on the command line. When -P is |
3174 |
+ * in effect (the default), baz will not be followed if it is a |
3175 |
+ * symlink, but if bar is a symlink, it _should_ be followed. |
3176 |
+ * Hence we need the ability to override the policy set by |
3177 |
+ * following_links(). |
3178 |
+ */ |
3179 |
+ chdir_status = safely_chdir(parent_dir, direction, &st, SymlinkFollowOk, &did_stat); |
3180 |
+ if (SafeChdirOK != chdir_status) |
3181 |
+ { |
3182 |
+ const char *what = (SafeChdirFailWouldBeUnableToReturn == chdir_status) ? "." : parent_dir; |
3183 |
+ if (errno) |
3184 |
+ error (0, errno, "%s", |
3185 |
+ safely_quote_err_filename(0, what)); |
3186 |
+ else |
3187 |
+ error (0, 0, _("Failed to safely change directory into %s"), |
3188 |
+ safely_quote_err_filename(0, parent_dir)); |
3189 |
+ |
3190 |
+ /* We can't process this command-line argument. */ |
3191 |
+ state.exit_status = 1; |
3192 |
+ return; |
3193 |
+ } |
3194 |
+ } |
3195 |
+ |
3196 |
+ free (parent_dir); |
3197 |
+ parent_dir = NULL; |
3198 |
+ |
3199 |
+ action(pathname, base, mode, pstat); |
3200 |
+ |
3201 |
+ if (dirchange) |
3202 |
+ { |
3203 |
+ chdir_back(); |
3204 |
+ } |
3205 |
+} |
3206 |
+ |
3207 |
+ |
3208 |
+static void do_process_top_dir(char *pathname, |
3209 |
+ char *base, |
3210 |
+ int mode, |
3211 |
+ struct stat *pstat) |
3212 |
+{ |
3213 |
+ (void) pstat; |
3214 |
+ |
3215 |
+ process_path (pathname, base, false, ".", mode); |
3216 |
+ complete_pending_execdirs(get_current_dirfd()); |
3217 |
+} |
3218 |
+ |
3219 |
+static void do_process_predicate(char *pathname, |
3220 |
+ char *base, |
3221 |
+ int mode, |
3222 |
+ struct stat *pstat) |
3223 |
+{ |
3224 |
+ (void) mode; |
3225 |
+ |
3226 |
+ state.rel_pathname = base; /* cwd_dir_fd was already set by safely_chdir */ |
3227 |
+ apply_predicate (pathname, pstat, get_eval_tree()); |
3228 |
+} |
3229 |
+ |
3230 |
+ |
3231 |
+ |
3232 |
+ |
3233 |
+/* Descend PATHNAME, which is a command-line argument. |
3234 |
+ |
3235 |
+ Actions like -execdir assume that we are in the |
3236 |
+ parent directory of the file we're examining, |
3237 |
+ and on entry to this function our working directory |
3238 |
+ is whatever it was when find was invoked. Therefore |
3239 |
+ If PATHNAME is "." we just leave things as they are. |
3240 |
+ Otherwise, we figure out what the parent directory is, |
3241 |
+ and move to that. |
3242 |
+*/ |
3243 |
+static void |
3244 |
+process_top_path (char *pathname, mode_t mode) |
3245 |
+{ |
3246 |
+ at_top(pathname, mode, NULL, do_process_top_dir); |
3247 |
+} |
3248 |
+ |
3249 |
+ |
3250 |
+/* Info on each directory in the current tree branch, to avoid |
3251 |
+ getting stuck in symbolic link loops. */ |
3252 |
+static struct dir_id *dir_ids = NULL; |
3253 |
+/* Entries allocated in `dir_ids'. */ |
3254 |
+static int dir_alloc = 0; |
3255 |
+/* Index in `dir_ids' of directory currently being searched. |
3256 |
+ This is always the last valid entry. */ |
3257 |
+static int dir_curr = -1; |
3258 |
+/* (Arbitrary) number of entries to grow `dir_ids' by. */ |
3259 |
+#define DIR_ALLOC_STEP 32 |
3260 |
+ |
3261 |
+ |
3262 |
+ |
3263 |
+/* We've detected a file system loop. This is caused by one of |
3264 |
+ * two things: |
3265 |
+ * |
3266 |
+ * 1. Option -L is in effect and we've hit a symbolic link that |
3267 |
+ * points to an ancestor. This is harmless. We won't traverse the |
3268 |
+ * symbolic link. |
3269 |
+ * |
3270 |
+ * 2. We have hit a real cycle in the directory hierarchy. In this |
3271 |
+ * case, we issue a diagnostic message (POSIX requires this) and we |
3272 |
+ * skip that directory entry. |
3273 |
+ */ |
3274 |
+static void |
3275 |
+issue_loop_warning(const char *name, const char *pathname, int level) |
3276 |
+{ |
3277 |
+ struct stat stbuf_link; |
3278 |
+ if (lstat(name, &stbuf_link) != 0) |
3279 |
+ stbuf_link.st_mode = S_IFREG; |
3280 |
+ |
3281 |
+ if (S_ISLNK(stbuf_link.st_mode)) |
3282 |
+ { |
3283 |
+ error(0, 0, |
3284 |
+ _("Symbolic link %s is part of a loop in the directory hierarchy; we have already visited the directory to which it points."), |
3285 |
+ safely_quote_err_filename(0, pathname)); |
3286 |
+ /* XXX: POSIX appears to require that the exit status be non-zero if a |
3287 |
+ * diagnostic is issued. |
3288 |
+ */ |
3289 |
+ } |
3290 |
+ else |
3291 |
+ { |
3292 |
+ int distance = 1 + (dir_curr-level); |
3293 |
+ /* We have found an infinite loop. POSIX requires us to |
3294 |
+ * issue a diagnostic. Usually we won't get to here |
3295 |
+ * because when the leaf optimisation is on, it will cause |
3296 |
+ * the subdirectory to be skipped. If /a/b/c/d is a hard |
3297 |
+ * link to /a/b, then the link count of /a/b/c is 2, |
3298 |
+ * because the ".." entry of /b/b/c/d points to /a, not |
3299 |
+ * to /a/b/c. |
3300 |
+ */ |
3301 |
+ error(0, 0, |
3302 |
+ ngettext( |
3303 |
+ "Filesystem loop detected; %1$s has the same device number and inode as " |
3304 |
+ "a directory which is %2$d level higher in the file system hierarchy", |
3305 |
+ "Filesystem loop detected; %1$s has the same device number and inode as " |
3306 |
+ "a directory which is %2$d levels higher in the file system hierarchy", |
3307 |
+ (long)distance), |
3308 |
+ safely_quote_err_filename(0, pathname), |
3309 |
+ distance); |
3310 |
+ } |
3311 |
+} |
3312 |
+ |
3313 |
+ |
3314 |
+ |
3315 |
+/* Recursively descend path PATHNAME, applying the predicates. |
3316 |
+ LEAF is true if PATHNAME is known to be in a directory that has no |
3317 |
+ more unexamined subdirectories, and therefore it is not a directory. |
3318 |
+ Knowing this allows us to avoid calling stat as long as possible for |
3319 |
+ leaf files. |
3320 |
+ |
3321 |
+ NAME is PATHNAME relative to the current directory. We access NAME |
3322 |
+ but print PATHNAME. |
3323 |
+ |
3324 |
+ PARENT is the path of the parent of NAME, relative to find's |
3325 |
+ starting directory. |
3326 |
+ |
3327 |
+ Return nonzero iff PATHNAME is a directory. */ |
3328 |
+ |
3329 |
+static int |
3330 |
+process_path (char *pathname, char *name, boolean leaf, char *parent, |
3331 |
+ mode_t mode) |
3332 |
+{ |
3333 |
+ struct stat stat_buf; |
3334 |
+ static dev_t root_dev; /* Device ID of current argument pathname. */ |
3335 |
+ int i; |
3336 |
+ struct predicate *eval_tree; |
3337 |
+ |
3338 |
+ eval_tree = get_eval_tree(); |
3339 |
+ /* Assume it is a non-directory initially. */ |
3340 |
+ stat_buf.st_mode = 0; |
3341 |
+ state.rel_pathname = name; |
3342 |
+ state.type = 0; |
3343 |
+ state.have_stat = false; |
3344 |
+ state.have_type = false; |
3345 |
+ |
3346 |
+ if (!digest_mode(mode, pathname, name, &stat_buf, leaf)) |
3347 |
+ return 0; |
3348 |
+ |
3349 |
+ if (!S_ISDIR (state.type)) |
3350 |
+ { |
3351 |
+ if (state.curdepth >= options.mindepth) |
3352 |
+ apply_predicate (pathname, &stat_buf, eval_tree); |
3353 |
+ return 0; |
3354 |
+ } |
3355 |
+ |
3356 |
+ /* From here on, we're working on a directory. */ |
3357 |
+ |
3358 |
+ |
3359 |
+ /* Now we really need to stat the directory, even if we know the |
3360 |
+ * type, because we need information like struct stat.st_rdev. |
3361 |
+ */ |
3362 |
+ if (get_statinfo(pathname, name, &stat_buf) != 0) |
3363 |
+ return 0; |
3364 |
+ |
3365 |
+ state.have_stat = true; |
3366 |
+ mode = state.type = stat_buf.st_mode; /* use full info now that we have it. */ |
3367 |
+ state.stop_at_current_level = |
3368 |
+ options.maxdepth >= 0 |
3369 |
+ && state.curdepth >= options.maxdepth; |
3370 |
+ |
3371 |
+ /* If we've already seen this directory on this branch, |
3372 |
+ don't descend it again. */ |
3373 |
+ for (i = 0; i <= dir_curr; i++) |
3374 |
+ if (stat_buf.st_ino == dir_ids[i].ino && |
3375 |
+ stat_buf.st_dev == dir_ids[i].dev) |
3376 |
+ { |
3377 |
+ state.stop_at_current_level = true; |
3378 |
+ issue_loop_warning(name, pathname, i); |
3379 |
+ } |
3380 |
+ |
3381 |
+ if (dir_alloc <= ++dir_curr) |
3382 |
+ { |
3383 |
+ dir_alloc += DIR_ALLOC_STEP; |
3384 |
+ dir_ids = (struct dir_id *) |
3385 |
+ xrealloc ((char *) dir_ids, dir_alloc * sizeof (struct dir_id)); |
3386 |
+ } |
3387 |
+ dir_ids[dir_curr].ino = stat_buf.st_ino; |
3388 |
+ dir_ids[dir_curr].dev = stat_buf.st_dev; |
3389 |
+ |
3390 |
+ if (options.stay_on_filesystem) |
3391 |
+ { |
3392 |
+ if (state.curdepth == 0) |
3393 |
+ root_dev = stat_buf.st_dev; |
3394 |
+ else if (stat_buf.st_dev != root_dev) |
3395 |
+ state.stop_at_current_level = true; |
3396 |
+ } |
3397 |
+ |
3398 |
+ if (options.do_dir_first && state.curdepth >= options.mindepth) |
3399 |
+ apply_predicate (pathname, &stat_buf, eval_tree); |
3400 |
+ |
3401 |
+ if (options.debug_options & DebugSearch) |
3402 |
+ fprintf(stderr, "pathname = %s, stop_at_current_level = %d\n", |
3403 |
+ pathname, state.stop_at_current_level); |
3404 |
+ |
3405 |
+ if (state.stop_at_current_level == false) |
3406 |
+ { |
3407 |
+ /* Scan directory on disk. */ |
3408 |
+ process_dir (pathname, name, strlen (pathname), &stat_buf, parent); |
3409 |
+ } |
3410 |
+ |
3411 |
+ if (options.do_dir_first == false && state.curdepth >= options.mindepth) |
3412 |
+ { |
3413 |
+ /* The fields in 'state' are now out of date. Correct them. |
3414 |
+ */ |
3415 |
+ if (!digest_mode(mode, pathname, name, &stat_buf, leaf)) |
3416 |
+ return 0; |
3417 |
+ |
3418 |
+ if (0 == dir_curr) |
3419 |
+ { |
3420 |
+ at_top(pathname, mode, &stat_buf, do_process_predicate); |
3421 |
+ } |
3422 |
+ else |
3423 |
+ { |
3424 |
+ do_process_predicate(pathname, name, mode, &stat_buf); |
3425 |
+ } |
3426 |
+ } |
3427 |
+ |
3428 |
+ dir_curr--; |
3429 |
+ |
3430 |
+ return 1; |
3431 |
+} |
3432 |
+ |
3433 |
+ |
3434 |
+/* Scan directory PATHNAME and recurse through process_path for each entry. |
3435 |
+ |
3436 |
+ PATHLEN is the length of PATHNAME. |
3437 |
+ |
3438 |
+ NAME is PATHNAME relative to the current directory. |
3439 |
+ |
3440 |
+ STATP is the results of *options.xstat on it. |
3441 |
+ |
3442 |
+ PARENT is the path of the parent of NAME, relative to find's |
3443 |
+ starting directory. */ |
3444 |
+ |
3445 |
+static void |
3446 |
+process_dir (char *pathname, char *name, int pathlen, const struct stat *statp, char *parent) |
3447 |
+{ |
3448 |
+ int subdirs_left; /* Number of unexamined subdirs in PATHNAME. */ |
3449 |
+ boolean subdirs_unreliable; /* if true, cannot use dir link count as subdir limif (if false, it may STILL be unreliable) */ |
3450 |
+ unsigned int idx; /* Which entry are we on? */ |
3451 |
+ struct stat stat_buf; |
3452 |
+ size_t dircount = 0u; |
3453 |
+ struct savedir_dirinfo *dirinfo; |
3454 |
+#if 0 |
3455 |
+ printf("process_dir: pathname=%s name=%s statp->st_nlink=%d st_ino=%d\n", |
3456 |
+ pathname, |
3457 |
+ name, |
3458 |
+ (int)statp->st_nlink, |
3459 |
+ (int)statp->st_ino); |
3460 |
+#endif |
3461 |
+ if (statp->st_nlink < 2) |
3462 |
+ { |
3463 |
+ subdirs_unreliable = true; |
3464 |
+ subdirs_left = 0; |
3465 |
+ } |
3466 |
+ else |
3467 |
+ { |
3468 |
+ subdirs_unreliable = false; /* not necessarily right */ |
3469 |
+ subdirs_left = statp->st_nlink - 2; /* Account for name and ".". */ |
3470 |
+ } |
3471 |
+ |
3472 |
+ errno = 0; |
3473 |
+ dirinfo = xsavedir(name, 0); |
3474 |
+ |
3475 |
+ |
3476 |
+ if (dirinfo == NULL) |
3477 |
+ { |
3478 |
+ assert (errno != 0); |
3479 |
+ error (0, errno, "%s", safely_quote_err_filename(0, pathname)); |
3480 |
+ state.exit_status = 1; |
3481 |
+ } |
3482 |
+ else |
3483 |
+ { |
3484 |
+ register char *namep; /* Current point in `name_space'. */ |
3485 |
+ char *cur_path; /* Full path of each file to process. */ |
3486 |
+ char *cur_name; /* Base name of each file to process. */ |
3487 |
+ unsigned cur_path_size; /* Bytes allocated for `cur_path'. */ |
3488 |
+ register unsigned file_len; /* Length of each path to process. */ |
3489 |
+ register unsigned pathname_len; /* PATHLEN plus trailing '/'. */ |
3490 |
+ boolean did_stat = false; |
3491 |
+ |
3492 |
+ if (pathname[pathlen - 1] == '/') |
3493 |
+ pathname_len = pathlen + 1; /* For '\0'; already have '/'. */ |
3494 |
+ else |
3495 |
+ pathname_len = pathlen + 2; /* For '/' and '\0'. */ |
3496 |
+ cur_path_size = 0; |
3497 |
+ cur_path = NULL; |
3498 |
+ |
3499 |
+ /* We're about to leave the directory. If there are any |
3500 |
+ * -execdir argument lists which have been built but have not |
3501 |
+ * yet been processed, do them now because they must be done in |
3502 |
+ * the same directory. |
3503 |
+ */ |
3504 |
+ complete_pending_execdirs(get_current_dirfd()); |
3505 |
+ |
3506 |
+ if (strcmp (name, ".")) |
3507 |
+ { |
3508 |
+ enum SafeChdirStatus status = safely_chdir (name, TraversingDown, &stat_buf, SymlinkHandleDefault, &did_stat); |
3509 |
+ switch (status) |
3510 |
+ { |
3511 |
+ case SafeChdirOK: |
3512 |
+ /* If there had been a change but wd_sanity_check() |
3513 |
+ * accepted it, we need to accept that on the |
3514 |
+ * way back up as well, so modify our record |
3515 |
+ * of what we think we should see later. |
3516 |
+ * If there was no change, the assignments are a no-op. |
3517 |
+ * |
3518 |
+ * However, before performing the assignment, we need to |
3519 |
+ * check that we have the stat information. If O_NOFOLLOW |
3520 |
+ * is available, safely_chdir() will not have needed to use |
3521 |
+ * stat(), and so stat_buf will just contain random data. |
3522 |
+ */ |
3523 |
+ if (!did_stat) |
3524 |
+ { |
3525 |
+ /* If there is a link we need to follow it. Hence |
3526 |
+ * the direct call to stat() not through (options.xstat) |
3527 |
+ */ |
3528 |
+ set_stat_placeholders(&stat_buf); |
3529 |
+ if (0 != stat(".", &stat_buf)) |
3530 |
+ break; /* skip the assignment. */ |
3531 |
+ } |
3532 |
+ dir_ids[dir_curr].dev = stat_buf.st_dev; |
3533 |
+ dir_ids[dir_curr].ino = stat_buf.st_ino; |
3534 |
+ |
3535 |
+ break; |
3536 |
+ |
3537 |
+ case SafeChdirFailWouldBeUnableToReturn: |
3538 |
+ error (0, errno, "."); |
3539 |
+ state.exit_status = 1; |
3540 |
+ break; |
3541 |
+ |
3542 |
+ case SafeChdirFailNonexistent: |
3543 |
+ case SafeChdirFailDestUnreadable: |
3544 |
+ case SafeChdirFailStat: |
3545 |
+ case SafeChdirFailNotDir: |
3546 |
+ case SafeChdirFailChdirFailed: |
3547 |
+ error (0, errno, "%s", |
3548 |
+ safely_quote_err_filename(0, pathname)); |
3549 |
+ state.exit_status = 1; |
3550 |
+ return; |
3551 |
+ |
3552 |
+ case SafeChdirFailSymlink: |
3553 |
+ error (0, 0, |
3554 |
+ _("warning: not following the symbolic link %s"), |
3555 |
+ safely_quote_err_filename(0, pathname)); |
3556 |
+ state.exit_status = 1; |
3557 |
+ return; |
3558 |
+ } |
3559 |
+ } |
3560 |
+ |
3561 |
+ for (idx=0; idx < dirinfo->size; ++idx) |
3562 |
+ { |
3563 |
+ /* savedirinfo() may return dirinfo=NULL if extended information |
3564 |
+ * is not available. |
3565 |
+ */ |
3566 |
+ mode_t mode = (dirinfo->entries[idx].flags & SavedirHaveFileType) ? |
3567 |
+ dirinfo->entries[idx].type_info : 0; |
3568 |
+ namep = dirinfo->entries[idx].name; |
3569 |
+ |
3570 |
+ /* Append this directory entry's name to the path being searched. */ |
3571 |
+ file_len = pathname_len + strlen (namep); |
3572 |
+ if (file_len > cur_path_size) |
3573 |
+ { |
3574 |
+ while (file_len > cur_path_size) |
3575 |
+ cur_path_size += 1024; |
3576 |
+ if (cur_path) |
3577 |
+ free (cur_path); |
3578 |
+ cur_path = xmalloc (cur_path_size); |
3579 |
+ strcpy (cur_path, pathname); |
3580 |
+ cur_path[pathname_len - 2] = '/'; |
3581 |
+ } |
3582 |
+ cur_name = cur_path + pathname_len - 1; |
3583 |
+ strcpy (cur_name, namep); |
3584 |
+ |
3585 |
+ state.curdepth++; |
3586 |
+ if (!options.no_leaf_check && !subdirs_unreliable) |
3587 |
+ { |
3588 |
+ if (mode && S_ISDIR(mode) && (subdirs_left == 0)) |
3589 |
+ { |
3590 |
+ /* This is a subdirectory, but the number of directories we |
3591 |
+ * have found now exceeds the number we would expect given |
3592 |
+ * the hard link count on the parent. This is likely to be |
3593 |
+ * a bug in the file system driver (e.g. Linux's |
3594 |
+ * /proc file system) or may just be a fact that the OS |
3595 |
+ * doesn't really handle hard links with Unix semantics. |
3596 |
+ * In the latter case, -noleaf should be used routinely. |
3597 |
+ */ |
3598 |
+ error(0, 0, _("WARNING: Hard link count is wrong for %1$s " |
3599 |
+ "(saw only st_nlink=%2$d but we already saw %3$d subdirectories): " |
3600 |
+ "this may be a bug in your file system driver. " |
3601 |
+ "Automatically turning on find's -noleaf option. " |
3602 |
+ "Earlier results may have failed to include directories " |
3603 |
+ "that should have been searched."), |
3604 |
+ safely_quote_err_filename(0, pathname), |
3605 |
+ statp->st_nlink, |
3606 |
+ dircount); |
3607 |
+ state.exit_status = 1; /* We know the result is wrong, now */ |
3608 |
+ options.no_leaf_check = true; /* Don't make same |
3609 |
+ mistake again */ |
3610 |
+ subdirs_unreliable = 1; |
3611 |
+ subdirs_left = 1; /* band-aid for this iteration. */ |
3612 |
+ } |
3613 |
+ |
3614 |
+ /* Normal case optimization. On normal Unix |
3615 |
+ file systems, a directory that has no subdirectories |
3616 |
+ has two links: its name, and ".". Any additional |
3617 |
+ links are to the ".." entries of its subdirectories. |
3618 |
+ Once we have processed as many subdirectories as |
3619 |
+ there are additional links, we know that the rest of |
3620 |
+ the entries are non-directories -- in other words, |
3621 |
+ leaf files. */ |
3622 |
+ { |
3623 |
+ int count; |
3624 |
+ count = process_path (cur_path, cur_name, |
3625 |
+ subdirs_left == 0, pathname, |
3626 |
+ mode); |
3627 |
+ subdirs_left -= count; |
3628 |
+ dircount += count; |
3629 |
+ } |
3630 |
+ } |
3631 |
+ else |
3632 |
+ { |
3633 |
+ /* There might be weird (e.g., CD-ROM or MS-DOS) file systems |
3634 |
+ mounted, which don't have Unix-like directory link counts. */ |
3635 |
+ process_path (cur_path, cur_name, false, pathname, mode); |
3636 |
+ } |
3637 |
+ |
3638 |
+ state.curdepth--; |
3639 |
+ } |
3640 |
+ |
3641 |
+ |
3642 |
+ /* We're about to leave the directory. If there are any |
3643 |
+ * -execdir argument lists which have been built but have not |
3644 |
+ * yet been processed, do them now because they must be done in |
3645 |
+ * the same directory. |
3646 |
+ */ |
3647 |
+ complete_pending_execdirs(get_current_dirfd()); |
3648 |
+ |
3649 |
+ if (strcmp (name, ".")) |
3650 |
+ { |
3651 |
+ enum SafeChdirStatus status; |
3652 |
+ struct dir_id did; |
3653 |
+ |
3654 |
+ /* We could go back and do the next command-line arg |
3655 |
+ instead, maybe using longjmp. */ |
3656 |
+ char const *dir; |
3657 |
+ boolean deref = following_links() ? true : false; |
3658 |
+ |
3659 |
+ if ( (state.curdepth>0) && !deref) |
3660 |
+ dir = ".."; |
3661 |
+ else |
3662 |
+ { |
3663 |
+ chdir_back (); |
3664 |
+ dir = parent; |
3665 |
+ } |
3666 |
+ |
3667 |
+ did_stat = false; |
3668 |
+ status = safely_chdir (dir, TraversingUp, &stat_buf, SymlinkHandleDefault, &did_stat); |
3669 |
+ switch (status) |
3670 |
+ { |
3671 |
+ case SafeChdirOK: |
3672 |
+ break; |
3673 |
+ |
3674 |
+ case SafeChdirFailWouldBeUnableToReturn: |
3675 |
+ error (1, errno, "."); |
3676 |
+ return; |
3677 |
+ |
3678 |
+ case SafeChdirFailNonexistent: |
3679 |
+ case SafeChdirFailDestUnreadable: |
3680 |
+ case SafeChdirFailStat: |
3681 |
+ case SafeChdirFailSymlink: |
3682 |
+ case SafeChdirFailNotDir: |
3683 |
+ case SafeChdirFailChdirFailed: |
3684 |
+ error (1, errno, "%s", safely_quote_err_filename(0, pathname)); |
3685 |
+ return; |
3686 |
+ } |
3687 |
+ |
3688 |
+ if (dir_curr > 0) |
3689 |
+ { |
3690 |
+ did.dev = dir_ids[dir_curr-1].dev; |
3691 |
+ did.ino = dir_ids[dir_curr-1].ino; |
3692 |
+ } |
3693 |
+ else |
3694 |
+ { |
3695 |
+ did.dev = starting_stat_buf.st_dev; |
3696 |
+ did.ino = starting_stat_buf.st_ino; |
3697 |
+ } |
3698 |
+ } |
3699 |
+ |
3700 |
+ if (cur_path) |
3701 |
+ free (cur_path); |
3702 |
+ free_dirinfo(dirinfo); |
3703 |
+ } |
3704 |
+ |
3705 |
+ if (subdirs_unreliable) |
3706 |
+ { |
3707 |
+ /* Make sure we hasn't used the variable subdirs_left if we knew |
3708 |
+ * we shouldn't do so. |
3709 |
+ */ |
3710 |
+ assert (0 == subdirs_left || options.no_leaf_check); |
3711 |
+ } |
3712 |
+} |
3713 |
diff -purN findutils-4.3.12.orig/find/parser.c findutils-4.3.12/find/parser.c |
3714 |
--- findutils-4.3.12.orig/find/parser.c 2007-12-19 16:12:34.000000000 -0500 |
3715 |
+++ findutils-4.3.12/find/parser.c 2008-01-30 08:46:05.754843619 -0500 |
3716 |
@@ -53,6 +53,13 @@ |
3717 |
#include <unistd.h> |
3718 |
#include <sys/stat.h> |
3719 |
|
3720 |
+#ifdef WITH_SELINUX |
3721 |
+#include <selinux/selinux.h> |
3722 |
+int optionh_getfilecon(const char *name, security_context_t *p); |
3723 |
+int optionl_getfilecon(const char *name, security_context_t *p); |
3724 |
+int optionp_getfilecon(const char *name, security_context_t *p); |
3725 |
+#endif /*WITH_SELINUX*/ |
3726 |
+ |
3727 |
#if ENABLE_NLS |
3728 |
# include <libintl.h> |
3729 |
# define _(Text) gettext (Text) |
3730 |
@@ -156,6 +163,9 @@ static boolean parse_noignore_race PARAM |
3731 |
static boolean parse_warn PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
3732 |
static boolean parse_xtype PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
3733 |
static boolean parse_quit PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
3734 |
+#ifdef WITH_SELINUX |
3735 |
+static boolean parse_scontext PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
3736 |
+#endif /*WITH_SELINUX*/ |
3737 |
|
3738 |
boolean parse_print PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
3739 |
|
3740 |
@@ -341,6 +351,8 @@ static struct parser_table const parse_t |
3741 |
{ARG_TEST, "-help", parse_help, NULL}, /* GNU */ |
3742 |
{ARG_TEST, "version", parse_version, NULL}, /* GNU */ |
3743 |
{ARG_TEST, "-version", parse_version, NULL}, /* GNU */ |
3744 |
+ {ARG_TEST, "context", parse_scontext, pred_scontext}, /* SELinux */ |
3745 |
+ {ARG_TEST, "-context", parse_scontext, pred_scontext}, /* SELinux */ |
3746 |
{0, 0, 0, 0} |
3747 |
}; |
3748 |
|
3749 |
@@ -452,10 +464,16 @@ set_follow_state(enum SymlinkOption opt) |
3750 |
case SYMLINK_ALWAYS_DEREF: /* -L */ |
3751 |
options.xstat = optionl_stat; |
3752 |
options.no_leaf_check = true; |
3753 |
+#ifdef WITH_SELINUX |
3754 |
+ options.x_getfilecon = optionl_getfilecon; |
3755 |
+#endif /* WITH_SELINUX */ |
3756 |
break; |
3757 |
|
3758 |
case SYMLINK_NEVER_DEREF: /* -P (default) */ |
3759 |
options.xstat = optionp_stat; |
3760 |
+#ifdef WITH_SELINUX |
3761 |
+ options.x_getfilecon = optionp_getfilecon; |
3762 |
+#endif /* WITH_SELINUX */ |
3763 |
/* Can't turn no_leaf_check off because the user might have specified |
3764 |
* -noleaf anyway |
3765 |
*/ |
3766 |
@@ -464,6 +482,9 @@ set_follow_state(enum SymlinkOption opt) |
3767 |
case SYMLINK_DEREF_ARGSONLY: /* -H */ |
3768 |
options.xstat = optionh_stat; |
3769 |
options.no_leaf_check = true; |
3770 |
+#ifdef WITH_SELINUX |
3771 |
+ options.x_getfilecon = optionh_getfilecon; |
3772 |
+#endif /* WITH_SELINUX */ |
3773 |
} |
3774 |
} |
3775 |
options.symlink_handling = opt; |
3776 |
@@ -667,6 +688,94 @@ collect_arg_stat_info(char **argv, int * |
3777 |
|
3778 |
The predicate structure is updated with the new information. */ |
3779 |
|
3780 |
+#ifdef WITH_SELINUX |
3781 |
+ |
3782 |
+static int |
3783 |
+fallback_getfilecon(const char *name, security_context_t *p, int prev_rv) |
3784 |
+{ |
3785 |
+ /* Our original getfilecon() call failed. Perhaps we can't follow a |
3786 |
+ * symbolic link. If that might be the problem, lgetfilecon() the link. |
3787 |
+ * Otherwise, admit defeat. |
3788 |
+ */ |
3789 |
+ switch (errno) |
3790 |
+ { |
3791 |
+ case ENOENT: |
3792 |
+ case ENOTDIR: |
3793 |
+#ifdef DEBUG_STAT |
3794 |
+ fprintf(stderr, "fallback_getfilecon(): getfilecon(%s) failed; falling back on lgetfilecon()\n", name); |
3795 |
+#endif |
3796 |
+ return lgetfilecon(name, p); |
3797 |
+ |
3798 |
+ case EACCES: |
3799 |
+ case EIO: |
3800 |
+ case ELOOP: |
3801 |
+ case ENAMETOOLONG: |
3802 |
+#ifdef EOVERFLOW |
3803 |
+ case EOVERFLOW: /* EOVERFLOW is not #defined on UNICOS. */ |
3804 |
+#endif |
3805 |
+ default: |
3806 |
+ return prev_rv; |
3807 |
+ } |
3808 |
+} |
3809 |
+ |
3810 |
+ |
3811 |
+/* optionh_getfilecon() implements the getfilecon operation when the |
3812 |
+ * -H option is in effect. |
3813 |
+ * |
3814 |
+ * If the item to be examined is a command-line argument, we follow |
3815 |
+ * symbolic links. If the getfilecon() call fails on the command-line |
3816 |
+ * item, we fall back on the properties of the symbolic link. |
3817 |
+ * |
3818 |
+ * If the item to be examined is not a command-line argument, we |
3819 |
+ * examine the link itself. |
3820 |
+ */ |
3821 |
+int |
3822 |
+optionh_getfilecon(const char *name, security_context_t *p) |
3823 |
+{ |
3824 |
+ if (0 == state.curdepth) |
3825 |
+ { |
3826 |
+ /* This file is from the command line; deference the link (if it |
3827 |
+ * is a link). |
3828 |
+ */ |
3829 |
+ int rv = getfilecon(name, p); |
3830 |
+ if (0 == rv) |
3831 |
+ return 0; /* success */ |
3832 |
+ else |
3833 |
+ return fallback_getfilecon(name, p, rv); |
3834 |
+ } |
3835 |
+ else |
3836 |
+ { |
3837 |
+ /* Not a file on the command line; do not derefernce the link. |
3838 |
+ */ |
3839 |
+ return lgetfilecon(name, p); |
3840 |
+ } |
3841 |
+} |
3842 |
+ |
3843 |
+/* optionl_getfilecon() implements the getfilecon operation when the |
3844 |
+ * -L option is in effect. That option makes us examine the thing the |
3845 |
+ * symbolic link points to, not the symbolic link itself. |
3846 |
+ */ |
3847 |
+int |
3848 |
+optionl_getfilecon(const char *name, security_context_t *p) |
3849 |
+{ |
3850 |
+ int rv = getfilecon(name, p); |
3851 |
+ if (0 == rv) |
3852 |
+ return 0; /* normal case. */ |
3853 |
+ else |
3854 |
+ return fallback_getfilecon(name, p, rv); |
3855 |
+} |
3856 |
+ |
3857 |
+/* optionp_getfilecon() implements the stat operation when the -P |
3858 |
+ * option is in effect (this is also the default). That option makes |
3859 |
+ * us examine the symbolic link itself, not the thing it points to. |
3860 |
+ */ |
3861 |
+int |
3862 |
+optionp_getfilecon(const char *name, security_context_t *p) |
3863 |
+{ |
3864 |
+ return lgetfilecon(name, p); |
3865 |
+} |
3866 |
+#endif /* WITH_SELINUX */ |
3867 |
+ |
3868 |
|
3869 |
static boolean |
3870 |
parse_and (const struct parser_table* entry, char **argv, int *arg_ptr) |
3871 |
@@ -1124,6 +1233,10 @@ tests (N can be +N or -N or N): -amin N |
3872 |
-readable -writable -executable\n\ |
3873 |
-wholename PATTERN -size N[bcwkMG] -true -type [bcdpflsD] -uid N\n\ |
3874 |
-used N -user NAME -xtype [bcdpfls]\n")); |
3875 |
+#ifdef WITH_SELINUX |
3876 |
+ puts (_("\ |
3877 |
+ -context CONTEXT\n")); |
3878 |
+#endif /*WITH_SELINUX*/ |
3879 |
puts (_("\ |
3880 |
actions: -delete -print0 -printf FORMAT -fprintf FILE FORMAT -print \n\ |
3881 |
-fprint0 FILE -fprint FILE -ls -fls FILE -prune -quit\n\ |
3882 |
@@ -2522,6 +2635,29 @@ parse_version (const struct parser_table |
3883 |
exit (0); |
3884 |
} |
3885 |
|
3886 |
+#ifdef WITH_SELINUX |
3887 |
+ |
3888 |
+static boolean |
3889 |
+parse_scontext ( const struct parser_table* entry, char **argv, int *arg_ptr) |
3890 |
+{ |
3891 |
+ struct predicate *our_pred; |
3892 |
+ |
3893 |
+ if ( (argv == NULL) || (argv[*arg_ptr] == NULL) ) |
3894 |
+ return( false ); |
3895 |
+ |
3896 |
+ our_pred = insert_primary(entry); |
3897 |
+ our_pred->need_stat = false; |
3898 |
+#ifdef DEBUG |
3899 |
+ our_pred->p_name = find_pred_name (pred_scontext); |
3900 |
+#endif /*DEBUG*/ |
3901 |
+ our_pred->args.scontext = argv[*arg_ptr];; |
3902 |
+ |
3903 |
+ (*arg_ptr)++; |
3904 |
+ return( true ); |
3905 |
+} |
3906 |
+ |
3907 |
+#endif /*WITH_SELINUX*/ |
3908 |
+ |
3909 |
static boolean |
3910 |
parse_xdev (const struct parser_table* entry, char **argv, int *arg_ptr) |
3911 |
{ |
3912 |
@@ -2773,7 +2909,11 @@ insert_fprintf (struct format_val *vec, |
3913 |
if (*scan2 == '.') |
3914 |
for (scan2++; ISDIGIT (*scan2); scan2++) |
3915 |
/* Do nothing. */ ; |
3916 |
+#ifdef WITH_SELINUX |
3917 |
+ if (strchr ("abcdDfFgGhHiklmMnpPsStuUyYZ", *scan2)) |
3918 |
+#else |
3919 |
if (strchr ("abcdDfFgGhHiklmMnpPsStuUyY", *scan2)) |
3920 |
+#endif |
3921 |
{ |
3922 |
segmentp = make_segment (segmentp, format, scan2 - format, |
3923 |
KIND_FORMAT, *scan2, 0, |
3924 |
diff -purN findutils-4.3.12.orig/find/parser.c.orig findutils-4.3.12/find/parser.c.orig |
3925 |
--- findutils-4.3.12.orig/find/parser.c.orig 1969-12-31 19:00:00.000000000 -0500 |
3926 |
+++ findutils-4.3.12/find/parser.c.orig 2007-12-19 16:12:34.000000000 -0500 |
3927 |
@@ -0,0 +1,3502 @@ |
3928 |
+/* parser.c -- convert the command line args into an expression tree. |
3929 |
+ Copyright (C) 1990, 1991, 1992, 1993, 1994, 2000, 2001, 2003, |
3930 |
+ 2004, 2005, 2006, 2007 Free Software Foundation, Inc. |
3931 |
+ |
3932 |
+ This program is free software: you can redistribute it and/or modify |
3933 |
+ it under the terms of the GNU General Public License as published by |
3934 |
+ the Free Software Foundation, either version 3 of the License, or |
3935 |
+ (at your option) any later version. |
3936 |
+ |
3937 |
+ This program is distributed in the hope that it will be useful, |
3938 |
+ but WITHOUT ANY WARRANTY; without even the implied warranty of |
3939 |
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3940 |
+ GNU General Public License for more details. |
3941 |
+ |
3942 |
+ You should have received a copy of the GNU General Public License |
3943 |
+ along with this program. If not, see <http://www.gnu.org/licenses/>. |
3944 |
+*/ |
3945 |
+ |
3946 |
+#include <config.h> |
3947 |
+ |
3948 |
+#include "defs.h" |
3949 |
+#include <ctype.h> |
3950 |
+#include <math.h> |
3951 |
+#include <assert.h> |
3952 |
+#include <pwd.h> |
3953 |
+#include <errno.h> |
3954 |
+#include <grp.h> |
3955 |
+#include <fnmatch.h> |
3956 |
+#include "modechange.h" |
3957 |
+#include "modetype.h" |
3958 |
+#include "xstrtol.h" |
3959 |
+#include "xalloc.h" |
3960 |
+#include "quote.h" |
3961 |
+#include "quotearg.h" |
3962 |
+#include "buildcmd.h" |
3963 |
+#include "nextelem.h" |
3964 |
+#include "stdio-safer.h" |
3965 |
+#include "regextype.h" |
3966 |
+#include "stat-time.h" |
3967 |
+#include "xstrtod.h" |
3968 |
+#include "fts_.h" |
3969 |
+#include "getdate.h" |
3970 |
+#include "error.h" |
3971 |
+#include "findutils-version.h" |
3972 |
+ |
3973 |
+#include <fcntl.h> |
3974 |
+ |
3975 |
+ |
3976 |
+/* The presence of unistd.h is assumed by gnulib these days, so we |
3977 |
+ * might as well assume it too. |
3978 |
+ */ |
3979 |
+/* We need <unistd.h> for isatty(). */ |
3980 |
+#include <unistd.h> |
3981 |
+#include <sys/stat.h> |
3982 |
+ |
3983 |
+#if ENABLE_NLS |
3984 |
+# include <libintl.h> |
3985 |
+# define _(Text) gettext (Text) |
3986 |
+#else |
3987 |
+# define _(Text) Text |
3988 |
+#endif |
3989 |
+#ifdef gettext_noop |
3990 |
+# define N_(String) gettext_noop (String) |
3991 |
+#else |
3992 |
+/* See locate.c for explanation as to why not use (String) */ |
3993 |
+# define N_(String) String |
3994 |
+#endif |
3995 |
+ |
3996 |
+#if !defined (isascii) || defined (STDC_HEADERS) |
3997 |
+#ifdef isascii |
3998 |
+#undef isascii |
3999 |
+#endif |
4000 |
+#define isascii(c) 1 |
4001 |
+#endif |
4002 |
+ |
4003 |
+#define ISDIGIT(c) (isascii ((unsigned char)c) && isdigit ((unsigned char)c)) |
4004 |
+#define ISUPPER(c) (isascii ((unsigned char)c) && isupper ((unsigned char)c)) |
4005 |
+ |
4006 |
+#ifndef HAVE_ENDGRENT |
4007 |
+#define endgrent() |
4008 |
+#endif |
4009 |
+#ifndef HAVE_ENDPWENT |
4010 |
+#define endpwent() |
4011 |
+#endif |
4012 |
+ |
4013 |
+static boolean parse_accesscheck PARAMS((const struct parser_table* entry, char **argv, int *arg_ptr)); |
4014 |
+static boolean parse_amin PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4015 |
+static boolean parse_and PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4016 |
+static boolean parse_anewer PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4017 |
+static boolean parse_cmin PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4018 |
+static boolean parse_cnewer PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4019 |
+static boolean parse_comma PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4020 |
+static boolean parse_daystart PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4021 |
+static boolean parse_delete PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4022 |
+static boolean parse_d PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4023 |
+static boolean parse_depth PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4024 |
+static boolean parse_empty PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4025 |
+static boolean parse_exec PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4026 |
+static boolean parse_execdir PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4027 |
+static boolean parse_false PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4028 |
+static boolean parse_fls PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4029 |
+static boolean parse_fprintf PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4030 |
+static boolean parse_follow PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4031 |
+static boolean parse_fprint PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4032 |
+static boolean parse_fprint0 PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4033 |
+static boolean parse_fstype PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4034 |
+static boolean parse_gid PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4035 |
+static boolean parse_group PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4036 |
+static boolean parse_help PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4037 |
+static boolean parse_ilname PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4038 |
+static boolean parse_iname PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4039 |
+static boolean parse_inum PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4040 |
+static boolean parse_ipath PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4041 |
+static boolean parse_iregex PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4042 |
+static boolean parse_iwholename PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4043 |
+static boolean parse_links PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4044 |
+static boolean parse_lname PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4045 |
+static boolean parse_ls PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4046 |
+static boolean parse_maxdepth PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4047 |
+static boolean parse_mindepth PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4048 |
+static boolean parse_mmin PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4049 |
+static boolean parse_name PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4050 |
+static boolean parse_negate PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4051 |
+static boolean parse_newer PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4052 |
+static boolean parse_newerXY PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4053 |
+static boolean parse_noleaf PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4054 |
+static boolean parse_nogroup PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4055 |
+static boolean parse_nouser PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4056 |
+static boolean parse_nowarn PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4057 |
+static boolean parse_ok PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4058 |
+static boolean parse_okdir PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4059 |
+static boolean parse_or PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4060 |
+static boolean parse_path PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4061 |
+static boolean parse_perm PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4062 |
+static boolean parse_print0 PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4063 |
+static boolean parse_printf PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4064 |
+static boolean parse_prune PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4065 |
+static boolean parse_regex PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4066 |
+static boolean parse_regextype PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4067 |
+static boolean parse_samefile PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4068 |
+#if 0 |
4069 |
+static boolean parse_show_control_chars PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4070 |
+#endif |
4071 |
+static boolean parse_size PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4072 |
+static boolean parse_time PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4073 |
+static boolean parse_true PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4074 |
+static boolean parse_type PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4075 |
+static boolean parse_uid PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4076 |
+static boolean parse_used PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4077 |
+static boolean parse_user PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4078 |
+static boolean parse_version PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4079 |
+static boolean parse_wholename PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4080 |
+static boolean parse_xdev PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4081 |
+static boolean parse_ignore_race PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4082 |
+static boolean parse_noignore_race PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4083 |
+static boolean parse_warn PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4084 |
+static boolean parse_xtype PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4085 |
+static boolean parse_quit PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4086 |
+ |
4087 |
+boolean parse_print PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); |
4088 |
+ |
4089 |
+ |
4090 |
+static boolean insert_type PARAMS((char **argv, int *arg_ptr, |
4091 |
+ const struct parser_table *entry, |
4092 |
+ PRED_FUNC which_pred)); |
4093 |
+static boolean insert_regex PARAMS((char *argv[], int *arg_ptr, |
4094 |
+ const struct parser_table *entry, |
4095 |
+ int regex_options)); |
4096 |
+static boolean insert_fprintf (struct format_val *vec, |
4097 |
+ const struct parser_table *entry, |
4098 |
+ PRED_FUNC func, |
4099 |
+ const char *format); |
4100 |
+ |
4101 |
+static struct segment **make_segment PARAMS((struct segment **segment, |
4102 |
+ char *format, int len, |
4103 |
+ int kind, char format_char, |
4104 |
+ char aux_format_char, |
4105 |
+ struct predicate *pred)); |
4106 |
+static boolean insert_exec_ok PARAMS((const char *action, |
4107 |
+ const struct parser_table *entry, |
4108 |
+ int dirfd, |
4109 |
+ char *argv[], |
4110 |
+ int *arg_ptr)); |
4111 |
+static boolean get_comp_type PARAMS((const char **str, |
4112 |
+ enum comparison_type *comp_type)); |
4113 |
+static boolean get_relative_timestamp PARAMS((const char *str, |
4114 |
+ struct time_val *tval, |
4115 |
+ time_t origin, |
4116 |
+ double sec_per_unit, |
4117 |
+ const char *overflowmessage)); |
4118 |
+static boolean get_num PARAMS((const char *str, |
4119 |
+ uintmax_t *num, |
4120 |
+ enum comparison_type *comp_type)); |
4121 |
+static struct predicate* insert_num PARAMS((char *argv[], int *arg_ptr, |
4122 |
+ const struct parser_table *entry)); |
4123 |
+static void open_output_file (const char *path, struct format_val *p); |
4124 |
+static void open_stdout (struct format_val *p); |
4125 |
+static boolean stream_is_tty(FILE *fp); |
4126 |
+static boolean parse_noop PARAMS((const struct parser_table* entry, |
4127 |
+ char **argv, int *arg_ptr)); |
4128 |
+ |
4129 |
+#define PASTE(x,y) x##y |
4130 |
+#define STRINGIFY(s) #s |
4131 |
+ |
4132 |
+#define PARSE_OPTION(what,suffix) \ |
4133 |
+ { (ARG_OPTION), (what), PASTE(parse_,suffix), NULL } |
4134 |
+ |
4135 |
+#define PARSE_POSOPT(what,suffix) \ |
4136 |
+ { (ARG_POSITIONAL_OPTION), (what), PASTE(parse_,suffix), NULL } |
4137 |
+ |
4138 |
+#define PARSE_TEST(what,suffix) \ |
4139 |
+ { (ARG_TEST), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) } |
4140 |
+ |
4141 |
+#define PARSE_TEST_NP(what,suffix) \ |
4142 |
+ { (ARG_TEST), (what), PASTE(parse_,suffix), NULL } |
4143 |
+ |
4144 |
+#define PARSE_ACTION(what,suffix) \ |
4145 |
+ { (ARG_ACTION), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) } |
4146 |
+ |
4147 |
+#define PARSE_ACTION_NP(what,suffix) \ |
4148 |
+ { (ARG_ACTION), (what), PASTE(parse_,suffix), NULL } |
4149 |
+ |
4150 |
+#define PARSE_PUNCTUATION(what,suffix) \ |
4151 |
+ { (ARG_PUNCTUATION), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) } |
4152 |
+ |
4153 |
+ |
4154 |
+/* Predicates we cannot handle in the usual way. If you add an entry |
4155 |
+ * to this table, double-check the switch statement in |
4156 |
+ * pred_sanity_check() to make sure that the new case is being |
4157 |
+ * correctly handled. |
4158 |
+ */ |
4159 |
+static struct parser_table const parse_entry_newerXY = |
4160 |
+ { |
4161 |
+ ARG_SPECIAL_PARSE, "newerXY", parse_newerXY, pred_newerXY /* BSD */ |
4162 |
+ }; |
4163 |
+ |
4164 |
+/* GNU find predicates that are not mentioned in POSIX.2 are marked `GNU'. |
4165 |
+ If they are in some Unix versions of find, they are marked `Unix'. */ |
4166 |
+ |
4167 |
+static struct parser_table const parse_table[] = |
4168 |
+{ |
4169 |
+ PARSE_PUNCTUATION("!", negate), /* POSIX */ |
4170 |
+ PARSE_PUNCTUATION("not", negate), /* GNU */ |
4171 |
+ PARSE_PUNCTUATION("(", openparen), /* POSIX */ |
4172 |
+ PARSE_PUNCTUATION(")", closeparen), /* POSIX */ |
4173 |
+ PARSE_PUNCTUATION(",", comma), /* GNU */ |
4174 |
+ PARSE_PUNCTUATION("a", and), /* POSIX */ |
4175 |
+ PARSE_TEST ("amin", amin), /* GNU */ |
4176 |
+ PARSE_PUNCTUATION("and", and), /* GNU */ |
4177 |
+ PARSE_TEST ("anewer", anewer), /* GNU */ |
4178 |
+ {ARG_TEST, "atime", parse_time, pred_atime}, /* POSIX */ |
4179 |
+ PARSE_TEST ("cmin", cmin), /* GNU */ |
4180 |
+ PARSE_TEST ("cnewer", cnewer), /* GNU */ |
4181 |
+ {ARG_TEST, "ctime", parse_time, pred_ctime}, /* POSIX */ |
4182 |
+ PARSE_POSOPT ("daystart", daystart), /* GNU */ |
4183 |
+ PARSE_ACTION ("delete", delete), /* GNU, Mac OS, FreeBSD */ |
4184 |
+ PARSE_OPTION ("d", d), /* Mac OS X, FreeBSD, NetBSD, OpenBSD, but deprecated in favour of -depth */ |
4185 |
+ PARSE_OPTION ("depth", depth), /* POSIX */ |
4186 |
+ PARSE_TEST ("empty", empty), /* GNU */ |
4187 |
+ {ARG_ACTION, "exec", parse_exec, pred_exec}, /* POSIX */ |
4188 |
+ {ARG_TEST, "executable", parse_accesscheck, pred_executable}, /* GNU, 4.3.0+ */ |
4189 |
+ PARSE_ACTION ("execdir", execdir), /* *BSD, GNU */ |
4190 |
+ PARSE_ACTION ("fls", fls), /* GNU */ |
4191 |
+ PARSE_POSOPT ("follow", follow), /* GNU, Unix */ |
4192 |
+ PARSE_ACTION ("fprint", fprint), /* GNU */ |
4193 |
+ PARSE_ACTION ("fprint0", fprint0), /* GNU */ |
4194 |
+ {ARG_ACTION, "fprintf", parse_fprintf, pred_fprintf}, /* GNU */ |
4195 |
+ PARSE_TEST ("fstype", fstype), /* GNU, Unix */ |
4196 |
+ PARSE_TEST ("gid", gid), /* GNU */ |
4197 |
+ PARSE_TEST ("group", group), /* POSIX */ |
4198 |
+ PARSE_OPTION ("ignore_readdir_race", ignore_race), /* GNU */ |
4199 |
+ PARSE_TEST ("ilname", ilname), /* GNU */ |
4200 |
+ PARSE_TEST ("iname", iname), /* GNU */ |
4201 |
+ PARSE_TEST ("inum", inum), /* GNU, Unix */ |
4202 |
+ PARSE_TEST ("ipath", ipath), /* GNU, deprecated in favour of iwholename */ |
4203 |
+ PARSE_TEST_NP ("iregex", iregex), /* GNU */ |
4204 |
+ PARSE_TEST_NP ("iwholename", iwholename), /* GNU */ |
4205 |
+ PARSE_TEST ("links", links), /* POSIX */ |
4206 |
+ PARSE_TEST ("lname", lname), /* GNU */ |
4207 |
+ PARSE_ACTION ("ls", ls), /* GNU, Unix */ |
4208 |
+ PARSE_OPTION ("maxdepth", maxdepth), /* GNU */ |
4209 |
+ PARSE_OPTION ("mindepth", mindepth), /* GNU */ |
4210 |
+ PARSE_TEST ("mmin", mmin), /* GNU */ |
4211 |
+ PARSE_OPTION ("mount", xdev), /* Unix */ |
4212 |
+ {ARG_TEST, "mtime", parse_time, pred_mtime}, /* POSIX */ |
4213 |
+ PARSE_TEST ("name", name), |
4214 |
+#ifdef UNIMPLEMENTED_UNIX |
4215 |
+ PARSE(ARG_UNIMPLEMENTED, "ncpio", ncpio), /* Unix */ |
4216 |
+#endif |
4217 |
+ PARSE_TEST ("newer", newer), /* POSIX */ |
4218 |
+ {ARG_TEST, "atime", parse_time, pred_atime}, /* POSIX */ |
4219 |
+ PARSE_OPTION ("noleaf", noleaf), /* GNU */ |
4220 |
+ PARSE_TEST ("nogroup", nogroup), /* POSIX */ |
4221 |
+ PARSE_TEST ("nouser", nouser), /* POSIX */ |
4222 |
+ PARSE_OPTION ("noignore_readdir_race", noignore_race), /* GNU */ |
4223 |
+ PARSE_POSOPT ("nowarn", nowarn), /* GNU */ |
4224 |
+ PARSE_PUNCTUATION("o", or), /* POSIX */ |
4225 |
+ PARSE_PUNCTUATION("or", or), /* GNU */ |
4226 |
+ PARSE_ACTION ("ok", ok), /* POSIX */ |
4227 |
+ PARSE_ACTION ("okdir", okdir), /* GNU (-execdir is BSD) */ |
4228 |
+ PARSE_TEST ("path", path), /* GNU, HP-UX, RMS prefers wholename, but anyway soon POSIX */ |
4229 |
+ PARSE_TEST ("perm", perm), /* POSIX */ |
4230 |
+ PARSE_ACTION ("print", print), /* POSIX */ |
4231 |
+ PARSE_ACTION ("print0", print0), /* GNU */ |
4232 |
+ {ARG_ACTION, "printf", parse_printf, NULL}, /* GNU */ |
4233 |
+ PARSE_ACTION ("prune", prune), /* POSIX */ |
4234 |
+ PARSE_ACTION ("quit", quit), /* GNU */ |
4235 |
+ {ARG_TEST, "readable", parse_accesscheck, pred_readable}, /* GNU, 4.3.0+ */ |
4236 |
+ PARSE_TEST ("regex", regex), /* GNU */ |
4237 |
+ PARSE_OPTION ("regextype", regextype), /* GNU */ |
4238 |
+ PARSE_TEST ("samefile", samefile), /* GNU */ |
4239 |
+#if 0 |
4240 |
+ PARSE_OPTION ("show-control-chars", show_control_chars), /* GNU, 4.3.0+ */ |
4241 |
+#endif |
4242 |
+ PARSE_TEST ("size", size), /* POSIX */ |
4243 |
+ PARSE_TEST ("type", type), /* POSIX */ |
4244 |
+ PARSE_TEST ("uid", uid), /* GNU */ |
4245 |
+ PARSE_TEST ("used", used), /* GNU */ |
4246 |
+ PARSE_TEST ("user", user), /* POSIX */ |
4247 |
+ PARSE_OPTION ("warn", warn), /* GNU */ |
4248 |
+ PARSE_TEST_NP ("wholename", wholename), /* GNU, replaced -path, but anyway -path will soon be in POSIX */ |
4249 |
+ {ARG_TEST, "writable", parse_accesscheck, pred_writable}, /* GNU, 4.3.0+ */ |
4250 |
+ PARSE_OPTION ("xdev", xdev), /* POSIX */ |
4251 |
+ PARSE_TEST ("xtype", xtype), /* GNU */ |
4252 |
+#ifdef UNIMPLEMENTED_UNIX |
4253 |
+ /* It's pretty ugly for find to know about archive formats. |
4254 |
+ Plus what it could do with cpio archives is very limited. |
4255 |
+ Better to leave it out. */ |
4256 |
+ PARSE(ARG_UNIMPLEMENTED, "cpio", cpio), /* Unix */ |
4257 |
+#endif |
4258 |
+ /* gnulib's stdbool.h might have made true and false into macros, |
4259 |
+ * so we can't leave named 'true' and 'false' tokens, so we have |
4260 |
+ * to expeant the relevant entries longhand. |
4261 |
+ */ |
4262 |
+ {ARG_TEST, "false", parse_false, pred_false}, /* GNU */ |
4263 |
+ {ARG_TEST, "true", parse_true, pred_true }, /* GNU */ |
4264 |
+ {ARG_NOOP, "noop", NULL, pred_true }, /* GNU, internal use only */ |
4265 |
+ |
4266 |
+ /* Various other cases that don't fit neatly into our macro scheme. */ |
4267 |
+ {ARG_TEST, "help", parse_help, NULL}, /* GNU */ |
4268 |
+ {ARG_TEST, "-help", parse_help, NULL}, /* GNU */ |
4269 |
+ {ARG_TEST, "version", parse_version, NULL}, /* GNU */ |
4270 |
+ {ARG_TEST, "-version", parse_version, NULL}, /* GNU */ |
4271 |
+ {0, 0, 0, 0} |
4272 |
+}; |
4273 |
+ |
4274 |
+ |
4275 |
+static const char *first_nonoption_arg = NULL; |
4276 |
+static const struct parser_table *noop = NULL; |
4277 |
+ |
4278 |
+ |
4279 |
+void |
4280 |
+check_option_combinations(const struct predicate *p) |
4281 |
+{ |
4282 |
+ enum { seen_delete=1u, seen_prune=2u }; |
4283 |
+ unsigned int predicates = 0u; |
4284 |
+ |
4285 |
+ while (p) |
4286 |
+ { |
4287 |
+ if (p->pred_func == pred_delete) |
4288 |
+ predicates |= seen_delete; |
4289 |
+ else if (p->pred_func == pred_prune) |
4290 |
+ predicates |= seen_prune; |
4291 |
+ p = p->pred_next; |
4292 |
+ } |
4293 |
+ |
4294 |
+ if ((predicates & seen_prune) && (predicates & seen_delete)) |
4295 |
+ { |
4296 |
+ /* The user specified both -delete and -prune. One might test |
4297 |
+ * this by first doing |
4298 |
+ * find dirs .... -prune ..... -print |
4299 |
+ * to fnd out what's going to get deleted, and then switch to |
4300 |
+ * find dirs .... -prune ..... -delete |
4301 |
+ * once we are happy. Unfortunately, the -delete action also |
4302 |
+ * implicitly turns on -depth, which will affect the behaviour |
4303 |
+ * of -prune (in fact, it makes it a no-op). In this case we |
4304 |
+ * would like to prevent unfortunate accidents, so we require |
4305 |
+ * the user to have explicitly used -depth. |
4306 |
+ * |
4307 |
+ * We only get away with this because the -delete predicate is not |
4308 |
+ * in POSIX. If it was, we couldn't issue a fatal error here. |
4309 |
+ */ |
4310 |
+ if (!options.explicit_depth) |
4311 |
+ { |
4312 |
+ /* This fixes Savannah bug #20865. */ |
4313 |
+ error (1, 0, _("The -delete action atomatically turns on -depth, " |
4314 |
+ "but -prune does nothing when -depth is in effect. " |
4315 |
+ "If you want to carry on anyway, just explicitly use " |
4316 |
+ "the -depth option.")); |
4317 |
+ } |
4318 |
+ } |
4319 |
+} |
4320 |
+ |
4321 |
+ |
4322 |
+static const struct parser_table* |
4323 |
+get_noop(void) |
4324 |
+{ |
4325 |
+ int i; |
4326 |
+ if (NULL == noop) |
4327 |
+ { |
4328 |
+ for (i = 0; parse_table[i].parser_name != 0; i++) |
4329 |
+ { |
4330 |
+ if (ARG_NOOP ==parse_table[i].type) |
4331 |
+ { |
4332 |
+ noop = &(parse_table[i]); |
4333 |
+ break; |
4334 |
+ } |
4335 |
+ } |
4336 |
+ } |
4337 |
+ return noop; |
4338 |
+} |
4339 |
+ |
4340 |
+static int |
4341 |
+get_stat_Ytime(const struct stat *p, |
4342 |
+ char what, |
4343 |
+ struct timespec *ret) |
4344 |
+{ |
4345 |
+ switch (what) |
4346 |
+ { |
4347 |
+ case 'a': |
4348 |
+ *ret = get_stat_atime(p); |
4349 |
+ return 1; |
4350 |
+ case 'B': |
4351 |
+ *ret = get_stat_birthtime(p); |
4352 |
+ return (ret->tv_nsec >= 0); |
4353 |
+ case 'c': |
4354 |
+ *ret = get_stat_ctime(p); |
4355 |
+ return 1; |
4356 |
+ case 'm': |
4357 |
+ *ret = get_stat_mtime(p); |
4358 |
+ return 1; |
4359 |
+ default: |
4360 |
+ assert (0); |
4361 |
+ abort(); |
4362 |
+ } |
4363 |
+} |
4364 |
+ |
4365 |
+void |
4366 |
+set_follow_state(enum SymlinkOption opt) |
4367 |
+{ |
4368 |
+ if (options.debug_options & DebugStat) |
4369 |
+ { |
4370 |
+ /* For DebugStat, the choice is made at runtime within debug_stat() |
4371 |
+ * by checking the contents of the symlink_handling variable. |
4372 |
+ */ |
4373 |
+ options.xstat = debug_stat; |
4374 |
+ } |
4375 |
+ else |
4376 |
+ { |
4377 |
+ switch (opt) |
4378 |
+ { |
4379 |
+ case SYMLINK_ALWAYS_DEREF: /* -L */ |
4380 |
+ options.xstat = optionl_stat; |
4381 |
+ options.no_leaf_check = true; |
4382 |
+ break; |
4383 |
+ |
4384 |
+ case SYMLINK_NEVER_DEREF: /* -P (default) */ |
4385 |
+ options.xstat = optionp_stat; |
4386 |
+ /* Can't turn no_leaf_check off because the user might have specified |
4387 |
+ * -noleaf anyway |
4388 |
+ */ |
4389 |
+ break; |
4390 |
+ |
4391 |
+ case SYMLINK_DEREF_ARGSONLY: /* -H */ |
4392 |
+ options.xstat = optionh_stat; |
4393 |
+ options.no_leaf_check = true; |
4394 |
+ } |
4395 |
+ } |
4396 |
+ options.symlink_handling = opt; |
4397 |
+} |
4398 |
+ |
4399 |
+ |
4400 |
+void |
4401 |
+parse_begin_user_args (char **args, int argno, |
4402 |
+ const struct predicate *last, |
4403 |
+ const struct predicate *predicates) |
4404 |
+{ |
4405 |
+ (void) args; |
4406 |
+ (void) argno; |
4407 |
+ (void) last; |
4408 |
+ (void) predicates; |
4409 |
+ first_nonoption_arg = NULL; |
4410 |
+} |
4411 |
+ |
4412 |
+void |
4413 |
+parse_end_user_args (char **args, int argno, |
4414 |
+ const struct predicate *last, |
4415 |
+ const struct predicate *predicates) |
4416 |
+{ |
4417 |
+ /* does nothing */ |
4418 |
+ (void) args; |
4419 |
+ (void) argno; |
4420 |
+ (void) last; |
4421 |
+ (void) predicates; |
4422 |
+} |
4423 |
+ |
4424 |
+ |
4425 |
+/* Check that it is legal to fid the given primary in its |
4426 |
+ * position and return it. |
4427 |
+ */ |
4428 |
+const struct parser_table* |
4429 |
+found_parser(const char *original_arg, const struct parser_table *entry) |
4430 |
+{ |
4431 |
+ /* If this is an option, but we have already had a |
4432 |
+ * non-option argument, the user may be under the |
4433 |
+ * impression that the behaviour of the option |
4434 |
+ * argument is conditional on some preceding |
4435 |
+ * tests. This might typically be the case with, |
4436 |
+ * for example, -maxdepth. |
4437 |
+ * |
4438 |
+ * The options -daystart and -follow are exempt |
4439 |
+ * from this treatment, since their positioning |
4440 |
+ * in the command line does have an effect on |
4441 |
+ * subsequent tests but not previous ones. That |
4442 |
+ * might be intentional on the part of the user. |
4443 |
+ */ |
4444 |
+ if (entry->type != ARG_POSITIONAL_OPTION) |
4445 |
+ { |
4446 |
+ /* Something other than -follow/-daystart. |
4447 |
+ * If this is an option, check if it followed |
4448 |
+ * a non-option and if so, issue a warning. |
4449 |
+ */ |
4450 |
+ if (entry->type == ARG_OPTION) |
4451 |
+ { |
4452 |
+ if ((first_nonoption_arg != NULL) |
4453 |
+ && options.warnings ) |
4454 |
+ { |
4455 |
+ /* option which follows a non-option */ |
4456 |
+ error (0, 0, |
4457 |
+ _("warning: you have specified the %1$s " |
4458 |
+ "option after a non-option argument %2$s, " |
4459 |
+ "but options are not positional (%3$s affects " |
4460 |
+ "tests specified before it as well as those " |
4461 |
+ "specified after it). Please specify options " |
4462 |
+ "before other arguments.\n"), |
4463 |
+ original_arg, |
4464 |
+ first_nonoption_arg, |
4465 |
+ original_arg); |
4466 |
+ } |
4467 |
+ } |
4468 |
+ else |
4469 |
+ { |
4470 |
+ /* Not an option or a positional option, |
4471 |
+ * so remember we've seen it in order to |
4472 |
+ * use it in a possible future warning message. |
4473 |
+ */ |
4474 |
+ if (first_nonoption_arg == NULL) |
4475 |
+ { |
4476 |
+ first_nonoption_arg = original_arg; |
4477 |
+ } |
4478 |
+ } |
4479 |
+ } |
4480 |
+ |
4481 |
+ return entry; |
4482 |
+} |
4483 |
+ |
4484 |
+ |
4485 |
+/* Return a pointer to the parser function to invoke for predicate |
4486 |
+ SEARCH_NAME. |
4487 |
+ Return NULL if SEARCH_NAME is not a valid predicate name. */ |
4488 |
+ |
4489 |
+const struct parser_table* |
4490 |
+find_parser (char *search_name) |
4491 |
+{ |
4492 |
+ int i; |
4493 |
+ const char *original_arg = search_name; |
4494 |
+ |
4495 |
+ /* Ugh. Special case -newerXY. */ |
4496 |
+ if (0 == strncmp("-newer", search_name, 6) |
4497 |
+ && (8 == strlen(search_name))) |
4498 |
+ { |
4499 |
+ return found_parser(original_arg, &parse_entry_newerXY); |
4500 |
+ } |
4501 |
+ |
4502 |
+ if (*search_name == '-') |
4503 |
+ search_name++; |
4504 |
+ |
4505 |
+ for (i = 0; parse_table[i].parser_name != 0; i++) |
4506 |
+ { |
4507 |
+ if (strcmp (parse_table[i].parser_name, search_name) == 0) |
4508 |
+ { |
4509 |
+ return found_parser(original_arg, &parse_table[i]); |
4510 |
+ } |
4511 |
+ } |
4512 |
+ return NULL; |
4513 |
+} |
4514 |
+ |
4515 |
+static float |
4516 |
+estimate_file_age_success_rate(float num_days) |
4517 |
+{ |
4518 |
+ if (num_days < 0.1) |
4519 |
+ { |
4520 |
+ /* Assume 1% of files have timestamps in the future */ |
4521 |
+ return 0.01f; |
4522 |
+ } |
4523 |
+ else if (num_days < 1) |
4524 |
+ { |
4525 |
+ /* Assume 30% of files have timestamps today */ |
4526 |
+ return 0.3f; |
4527 |
+ } |
4528 |
+ else if (num_days > 100) |
4529 |
+ { |
4530 |
+ /* Assume 30% of files are very old */ |
4531 |
+ return 0.3f; |
4532 |
+ } |
4533 |
+ else |
4534 |
+ { |
4535 |
+ /* Assume 39% of files are between 1 and 100 days old. */ |
4536 |
+ return 0.39f; |
4537 |
+ } |
4538 |
+} |
4539 |
+ |
4540 |
+static float |
4541 |
+estimate_timestamp_success_rate(time_t when) |
4542 |
+{ |
4543 |
+ int num_days = (options.cur_day_start - when) / 86400; |
4544 |
+ return estimate_file_age_success_rate(num_days); |
4545 |
+} |
4546 |
+ |
4547 |
+/* Collect an argument from the argument list, or |
4548 |
+ * return false. |
4549 |
+ */ |
4550 |
+static boolean |
4551 |
+collect_arg(char **argv, int *arg_ptr, const char **collected_arg) |
4552 |
+{ |
4553 |
+ if ((argv == NULL) || (argv[*arg_ptr] == NULL)) |
4554 |
+ { |
4555 |
+ *collected_arg = NULL; |
4556 |
+ return false; |
4557 |
+ } |
4558 |
+ else |
4559 |
+ { |
4560 |
+ *collected_arg = argv[*arg_ptr]; |
4561 |
+ (*arg_ptr)++; |
4562 |
+ return true; |
4563 |
+ } |
4564 |
+} |
4565 |
+ |
4566 |
+static boolean |
4567 |
+collect_arg_stat_info(char **argv, int *arg_ptr, struct stat *p) |
4568 |
+{ |
4569 |
+ const char *filename; |
4570 |
+ if (collect_arg(argv, arg_ptr, &filename)) |
4571 |
+ { |
4572 |
+ if (0 == (options.xstat)(filename, p)) |
4573 |
+ { |
4574 |
+ return true; |
4575 |
+ } |
4576 |
+ else |
4577 |
+ { |
4578 |
+ fatal_file_error(filename); |
4579 |
+ } |
4580 |
+ } |
4581 |
+ else |
4582 |
+ { |
4583 |
+ return false; |
4584 |
+ } |
4585 |
+} |
4586 |
+ |
4587 |
+/* The parsers are responsible to continue scanning ARGV for |
4588 |
+ their arguments. Each parser knows what is and isn't |
4589 |
+ allowed for itself. |
4590 |
+ |
4591 |
+ ARGV is the argument array. |
4592 |
+ *ARG_PTR is the index to start at in ARGV, |
4593 |
+ updated to point beyond the last element consumed. |
4594 |
+ |
4595 |
+ The predicate structure is updated with the new information. */ |
4596 |
+ |
4597 |
+ |
4598 |
+static boolean |
4599 |
+parse_and (const struct parser_table* entry, char **argv, int *arg_ptr) |
4600 |
+{ |
4601 |
+ struct predicate *our_pred; |
4602 |
+ |
4603 |
+ (void) argv; |
4604 |
+ (void) arg_ptr; |
4605 |
+ |
4606 |
+ our_pred = get_new_pred (entry); |
4607 |
+ our_pred->pred_func = pred_and; |
4608 |
+ our_pred->p_type = BI_OP; |
4609 |
+ our_pred->p_prec = AND_PREC; |
4610 |
+ our_pred->need_stat = our_pred->need_type = false; |
4611 |
+ return true; |
4612 |
+} |
4613 |
+ |
4614 |
+static boolean |
4615 |
+parse_anewer (const struct parser_table* entry, char **argv, int *arg_ptr) |
4616 |
+{ |
4617 |
+ struct stat stat_newer; |
4618 |
+ |
4619 |
+ set_stat_placeholders(&stat_newer); |
4620 |
+ if (collect_arg_stat_info(argv, arg_ptr, &stat_newer)) |
4621 |
+ { |
4622 |
+ struct predicate *our_pred = insert_primary (entry); |
4623 |
+ our_pred->args.reftime.xval = XVAL_ATIME; |
4624 |
+ our_pred->args.reftime.ts = get_stat_mtime(&stat_newer); |
4625 |
+ our_pred->args.reftime.kind = COMP_GT; |
4626 |
+ our_pred->est_success_rate = estimate_timestamp_success_rate(stat_newer.st_mtime); |
4627 |
+ return true; |
4628 |
+ } |
4629 |
+ return false; |
4630 |
+} |
4631 |
+ |
4632 |
+boolean |
4633 |
+parse_closeparen (const struct parser_table* entry, char **argv, int *arg_ptr) |
4634 |
+{ |
4635 |
+ struct predicate *our_pred; |
4636 |
+ |
4637 |
+ (void) argv; |
4638 |
+ (void) arg_ptr; |
4639 |
+ |
4640 |
+ our_pred = get_new_pred (entry); |
4641 |
+ our_pred->pred_func = pred_closeparen; |
4642 |
+ our_pred->p_type = CLOSE_PAREN; |
4643 |
+ our_pred->p_prec = NO_PREC; |
4644 |
+ our_pred->need_stat = our_pred->need_type = false; |
4645 |
+ return true; |
4646 |
+} |
4647 |
+ |
4648 |
+static boolean |
4649 |
+parse_cnewer (const struct parser_table* entry, char **argv, int *arg_ptr) |
4650 |
+{ |
4651 |
+ struct stat stat_newer; |
4652 |
+ |
4653 |
+ set_stat_placeholders(&stat_newer); |
4654 |
+ if (collect_arg_stat_info(argv, arg_ptr, &stat_newer)) |
4655 |
+ { |
4656 |
+ struct predicate *our_pred = insert_primary (entry); |
4657 |
+ our_pred->args.reftime.xval = XVAL_CTIME; /* like -newercm */ |
4658 |
+ our_pred->args.reftime.ts = get_stat_mtime(&stat_newer); |
4659 |
+ our_pred->args.reftime.kind = COMP_GT; |
4660 |
+ our_pred->est_success_rate = estimate_timestamp_success_rate(stat_newer.st_mtime); |
4661 |
+ return true; |
4662 |
+ } |
4663 |
+ return false; |
4664 |
+} |
4665 |
+ |
4666 |
+static boolean |
4667 |
+parse_comma (const struct parser_table* entry, char **argv, int *arg_ptr) |
4668 |
+{ |
4669 |
+ struct predicate *our_pred; |
4670 |
+ |
4671 |
+ (void) argv; |
4672 |
+ (void) arg_ptr; |
4673 |
+ |
4674 |
+ our_pred = get_new_pred (entry); |
4675 |
+ our_pred->pred_func = pred_comma; |
4676 |
+ our_pred->p_type = BI_OP; |
4677 |
+ our_pred->p_prec = COMMA_PREC; |
4678 |
+ our_pred->need_stat = our_pred->need_type = false; |
4679 |
+ our_pred->est_success_rate = 1.0f; |
4680 |
+ return true; |
4681 |
+} |
4682 |
+ |
4683 |
+static boolean |
4684 |
+parse_daystart (const struct parser_table* entry, char **argv, int *arg_ptr) |
4685 |
+{ |
4686 |
+ struct tm *local; |
4687 |
+ |
4688 |
+ (void) entry; |
4689 |
+ (void) argv; |
4690 |
+ (void) arg_ptr; |
4691 |
+ |
4692 |
+ if (options.full_days == false) |
4693 |
+ { |
4694 |
+ options.cur_day_start += DAYSECS; |
4695 |
+ local = localtime (&options.cur_day_start); |
4696 |
+ options.cur_day_start -= (local |
4697 |
+ ? (local->tm_sec + local->tm_min * 60 |
4698 |
+ + local->tm_hour * 3600) |
4699 |
+ : options.cur_day_start % DAYSECS); |
4700 |
+ options.full_days = true; |
4701 |
+ } |
4702 |
+ return true; |
4703 |
+} |
4704 |
+ |
4705 |
+static boolean |
4706 |
+parse_delete (const struct parser_table* entry, char *argv[], int *arg_ptr) |
4707 |
+{ |
4708 |
+ struct predicate *our_pred; |
4709 |
+ (void) argv; |
4710 |
+ (void) arg_ptr; |
4711 |
+ |
4712 |
+ our_pred = insert_primary (entry); |
4713 |
+ our_pred->side_effects = our_pred->no_default_print = true; |
4714 |
+ /* -delete implies -depth */ |
4715 |
+ options.do_dir_first = false; |
4716 |
+ |
4717 |
+ /* We do not need stat information because we check for the case |
4718 |
+ * (errno==EISDIR) in pred_delete. |
4719 |
+ */ |
4720 |
+ our_pred->need_stat = our_pred->need_type = false; |
4721 |
+ |
4722 |
+ our_pred->est_success_rate = 1.0f; |
4723 |
+ return true; |
4724 |
+} |
4725 |
+ |
4726 |
+static boolean |
4727 |
+parse_depth (const struct parser_table* entry, char **argv, int *arg_ptr) |
4728 |
+{ |
4729 |
+ (void) entry; |
4730 |
+ (void) argv; |
4731 |
+ |
4732 |
+ options.do_dir_first = false; |
4733 |
+ options.explicit_depth = true; |
4734 |
+ return parse_noop(entry, argv, arg_ptr); |
4735 |
+} |
4736 |
+ |
4737 |
+static boolean |
4738 |
+parse_d (const struct parser_table* entry, char **argv, int *arg_ptr) |
4739 |
+{ |
4740 |
+ if (options.warnings) |
4741 |
+ { |
4742 |
+ error (0, 0, |
4743 |
+ _("warning: the -d option is deprecated; please use " |
4744 |
+ "-depth instead, because the latter is a " |
4745 |
+ "POSIX-compliant feature.")); |
4746 |
+ } |
4747 |
+ return parse_depth(entry, argv, arg_ptr); |
4748 |
+} |
4749 |
+ |
4750 |
+static boolean |
4751 |
+parse_empty (const struct parser_table* entry, char **argv, int *arg_ptr) |
4752 |
+{ |
4753 |
+ struct predicate *our_pred; |
4754 |
+ (void) argv; |
4755 |
+ (void) arg_ptr; |
4756 |
+ |
4757 |
+ our_pred = insert_primary (entry); |
4758 |
+ our_pred->est_success_rate = 0.01f; /* assume 1% of files are empty. */ |
4759 |
+ return true; |
4760 |
+} |
4761 |
+ |
4762 |
+static boolean |
4763 |
+parse_exec (const struct parser_table* entry, char **argv, int *arg_ptr) |
4764 |
+{ |
4765 |
+ return insert_exec_ok ("-exec", entry, get_start_dirfd(), argv, arg_ptr); |
4766 |
+} |
4767 |
+ |
4768 |
+static boolean |
4769 |
+parse_execdir (const struct parser_table* entry, char **argv, int *arg_ptr) |
4770 |
+{ |
4771 |
+ return insert_exec_ok ("-execdir", entry, -1, argv, arg_ptr); |
4772 |
+} |
4773 |
+ |
4774 |
+static boolean |
4775 |
+parse_false (const struct parser_table* entry, char **argv, int *arg_ptr) |
4776 |
+{ |
4777 |
+ struct predicate *our_pred; |
4778 |
+ |
4779 |
+ (void) argv; |
4780 |
+ (void) arg_ptr; |
4781 |
+ |
4782 |
+ our_pred = insert_primary (entry); |
4783 |
+ our_pred->need_stat = our_pred->need_type = false; |
4784 |
+ our_pred->side_effects = our_pred->no_default_print = false; |
4785 |
+ our_pred->est_success_rate = 0.0f; |
4786 |
+ return true; |
4787 |
+} |
4788 |
+ |
4789 |
+static boolean |
4790 |
+insert_fls (const struct parser_table* entry, const char *filename) |
4791 |
+{ |
4792 |
+ struct predicate *our_pred = insert_primary (entry); |
4793 |
+ if (filename) |
4794 |
+ open_output_file (filename, &our_pred->args.printf_vec); |
4795 |
+ else |
4796 |
+ open_stdout (&our_pred->args.printf_vec); |
4797 |
+ our_pred->side_effects = our_pred->no_default_print = true; |
4798 |
+ our_pred->est_success_rate = 1.0f; |
4799 |
+ return true; |
4800 |
+} |
4801 |
+ |
4802 |
+ |
4803 |
+static boolean |
4804 |
+parse_fls (const struct parser_table* entry, char **argv, int *arg_ptr) |
4805 |
+{ |
4806 |
+ const char *filename; |
4807 |
+ return collect_arg(argv, arg_ptr, &filename) |
4808 |
+ && insert_fls(entry, filename); |
4809 |
+} |
4810 |
+ |
4811 |
+static boolean |
4812 |
+parse_follow (const struct parser_table* entry, char **argv, int *arg_ptr) |
4813 |
+{ |
4814 |
+ set_follow_state(SYMLINK_ALWAYS_DEREF); |
4815 |
+ return parse_noop(entry, argv, arg_ptr); |
4816 |
+} |
4817 |
+ |
4818 |
+static boolean |
4819 |
+parse_fprint (const struct parser_table* entry, char **argv, int *arg_ptr) |
4820 |
+{ |
4821 |
+ struct predicate *our_pred; |
4822 |
+ const char *filename; |
4823 |
+ if (collect_arg(argv, arg_ptr, &filename)) |
4824 |
+ { |
4825 |
+ our_pred = insert_primary (entry); |
4826 |
+ open_output_file (filename, &our_pred->args.printf_vec); |
4827 |
+ our_pred->side_effects = our_pred->no_default_print = true; |
4828 |
+ our_pred->need_stat = our_pred->need_type = false; |
4829 |
+ our_pred->est_success_rate = 1.0f; |
4830 |
+ return true; |
4831 |
+ } |
4832 |
+ else |
4833 |
+ { |
4834 |
+ return false; |
4835 |
+ } |
4836 |
+} |
4837 |
+ |
4838 |
+static boolean |
4839 |
+insert_fprint(const struct parser_table* entry, const char *filename) |
4840 |
+{ |
4841 |
+ struct predicate *our_pred = insert_primary (entry); |
4842 |
+ if (filename) |
4843 |
+ open_output_file (filename, &our_pred->args.printf_vec); |
4844 |
+ else |
4845 |
+ open_stdout (&our_pred->args.printf_vec); |
4846 |
+ our_pred->side_effects = our_pred->no_default_print = true; |
4847 |
+ our_pred->need_stat = our_pred->need_type = false; |
4848 |
+ our_pred->est_success_rate = 1.0f; |
4849 |
+ return true; |
4850 |
+} |
4851 |
+ |
4852 |
+ |
4853 |
+static boolean |
4854 |
+parse_fprint0 (const struct parser_table* entry, char **argv, int *arg_ptr) |
4855 |
+{ |
4856 |
+ const char *filename; |
4857 |
+ if (collect_arg(argv, arg_ptr, &filename)) |
4858 |
+ return insert_fprint(entry, filename); |
4859 |
+ else |
4860 |
+ return false; |
4861 |
+} |
4862 |
+ |
4863 |
+static float estimate_fstype_success_rate(const char *fsname) |
4864 |
+{ |
4865 |
+ struct stat dir_stat; |
4866 |
+ const char *dir = "/"; |
4867 |
+ if (0 == stat(dir, &dir_stat)) |
4868 |
+ { |
4869 |
+ const char *fstype = filesystem_type(&dir_stat, dir); |
4870 |
+ /* Assume most files are on the same file system type as the root fs. */ |
4871 |
+ if (0 == strcmp(fsname, fstype)) |
4872 |
+ return 0.7f; |
4873 |
+ else |
4874 |
+ return 0.3f; |
4875 |
+ } |
4876 |
+ return 1.0f; |
4877 |
+} |
4878 |
+ |
4879 |
+ |
4880 |
+static boolean |
4881 |
+parse_fstype (const struct parser_table* entry, char **argv, int *arg_ptr) |
4882 |
+{ |
4883 |
+ const char *typename; |
4884 |
+ if (collect_arg(argv, arg_ptr, &typename)) |
4885 |
+ { |
4886 |
+ struct predicate *our_pred = insert_primary (entry); |
4887 |
+ our_pred->args.str = typename; |
4888 |
+ |
4889 |
+ /* This is an expensive operation, so although there are |
4890 |
+ * circumstances where it is selective, we ignore this fact |
4891 |
+ * because we probably don't want to promote this test to the |
4892 |
+ * front anyway. |
4893 |
+ */ |
4894 |
+ our_pred->est_success_rate = estimate_fstype_success_rate(typename); |
4895 |
+ return true; |
4896 |
+ } |
4897 |
+ else |
4898 |
+ { |
4899 |
+ return false; |
4900 |
+ } |
4901 |
+} |
4902 |
+ |
4903 |
+static boolean |
4904 |
+parse_gid (const struct parser_table* entry, char **argv, int *arg_ptr) |
4905 |
+{ |
4906 |
+ struct predicate *p = insert_num (argv, arg_ptr, entry); |
4907 |
+ if (p) |
4908 |
+ { |
4909 |
+ p->est_success_rate = (p->args.numinfo.l_val < 100) ? 0.99 : 0.2; |
4910 |
+ return true; |
4911 |
+ } |
4912 |
+ else |
4913 |
+ { |
4914 |
+ return false; |
4915 |
+ } |
4916 |
+} |
4917 |
+ |
4918 |
+ |
4919 |
+static int |
4920 |
+safe_atoi (const char *s) |
4921 |
+{ |
4922 |
+ long lval; |
4923 |
+ char *end; |
4924 |
+ |
4925 |
+ errno = 0; |
4926 |
+ lval = strtol(s, &end, 10); |
4927 |
+ if ( (LONG_MAX == lval) || (LONG_MIN == lval) ) |
4928 |
+ { |
4929 |
+ /* max/min possible value, or an error. */ |
4930 |
+ if (errno == ERANGE) |
4931 |
+ { |
4932 |
+ /* too big, or too small. */ |
4933 |
+ error(1, errno, "%s", s); |
4934 |
+ } |
4935 |
+ else |
4936 |
+ { |
4937 |
+ /* not a valid number */ |
4938 |
+ error(1, errno, "%s", s); |
4939 |
+ } |
4940 |
+ /* Otherwise, we do a range chack against INT_MAX and INT_MIN |
4941 |
+ * below. |
4942 |
+ */ |
4943 |
+ } |
4944 |
+ |
4945 |
+ if (lval > INT_MAX || lval < INT_MIN) |
4946 |
+ { |
4947 |
+ /* The number was in range for long, but not int. */ |
4948 |
+ errno = ERANGE; |
4949 |
+ error(1, errno, "%s", s); |
4950 |
+ } |
4951 |
+ else if (*end) |
4952 |
+ { |
4953 |
+ error(1, errno, "Unexpected suffix %s on %s", |
4954 |
+ quotearg_n_style(0, options.err_quoting_style, end), |
4955 |
+ quotearg_n_style(1, options.err_quoting_style, s)); |
4956 |
+ } |
4957 |
+ else if (end == s) |
4958 |
+ { |
4959 |
+ error(1, errno, "Expected an integer: %s", |
4960 |
+ quotearg_n_style(0, options.err_quoting_style, s)); |
4961 |
+ } |
4962 |
+ return (int)lval; |
4963 |
+} |
4964 |
+ |
4965 |
+ |
4966 |
+static boolean |
4967 |
+parse_group (const struct parser_table* entry, char **argv, int *arg_ptr) |
4968 |
+{ |
4969 |
+ const char *groupname; |
4970 |
+ |
4971 |
+ if (collect_arg(argv, arg_ptr, &groupname)) |
4972 |
+ { |
4973 |
+ gid_t gid; |
4974 |
+ struct predicate *our_pred; |
4975 |
+ struct group *cur_gr = getgrnam(groupname); |
4976 |
+ endgrent(); |
4977 |
+ if (cur_gr) |
4978 |
+ { |
4979 |
+ gid = cur_gr->gr_gid; |
4980 |
+ } |
4981 |
+ else |
4982 |
+ { |
4983 |
+ const int gid_len = strspn (groupname, "0123456789"); |
4984 |
+ if (gid_len) |
4985 |
+ { |
4986 |
+ if (groupname[gid_len] == 0) |
4987 |
+ { |
4988 |
+ gid = safe_atoi (groupname); |
4989 |
+ } |
4990 |
+ else |
4991 |
+ { |
4992 |
+ /* XXX: no test in test suite for this */ |
4993 |
+ error(1, 0, _("%1$s is not the name of an existing group and" |
4994 |
+ " it does not look like a numeric group ID " |
4995 |
+ "because it has the unexpected suffix %2$s"), |
4996 |
+ quotearg_n_style(0, options.err_quoting_style, groupname), |
4997 |
+ quotearg_n_style(1, options.err_quoting_style, groupname+gid_len)); |
4998 |
+ return false; |
4999 |
+ } |
5000 |
+ } |
5001 |
+ else |
5002 |
+ { |
5003 |
+ if (*groupname) |
5004 |
+ { |
5005 |
+ /* XXX: no test in test suite for this */ |
5006 |
+ error(1, 0, _("%s is not the name of an existing group"), |
5007 |
+ quotearg_n_style(0, options.err_quoting_style, groupname)); |
5008 |
+ } |
5009 |
+ else |
5010 |
+ { |
5011 |
+ error(1, 0, _("argument to -group is empty, but should be a group name")); |
5012 |
+ } |
5013 |
+ return false; |
5014 |
+ } |
5015 |
+ } |
5016 |
+ our_pred = insert_primary (entry); |
5017 |
+ our_pred->args.gid = gid; |
5018 |
+ our_pred->est_success_rate = (our_pred->args.numinfo.l_val < 100) ? 0.99 : 0.2; |
5019 |
+ return true; |
5020 |
+ } |
5021 |
+ return false; |
5022 |
+} |
5023 |
+ |
5024 |
+static boolean |
5025 |
+parse_help (const struct parser_table* entry, char **argv, int *arg_ptr) |
5026 |
+{ |
5027 |
+ (void) entry; |
5028 |
+ (void) argv; |
5029 |
+ (void) arg_ptr; |
5030 |
+ |
5031 |
+ usage(stdout, 0, NULL); |
5032 |
+ puts (_("\n\ |
5033 |
+default path is the current directory; default expression is -print\n\ |
5034 |
+expression may consist of: operators, options, tests, and actions:\n")); |
5035 |
+ puts (_("\ |
5036 |
+operators (decreasing precedence; -and is implicit where no others are given):\n\ |
5037 |
+ ( EXPR ) ! EXPR -not EXPR EXPR1 -a EXPR2 EXPR1 -and EXPR2\n\ |
5038 |
+ EXPR1 -o EXPR2 EXPR1 -or EXPR2 EXPR1 , EXPR2\n")); |
5039 |
+ puts (_("\ |
5040 |
+positional options (always true): -daystart -follow -regextype\n\n\ |
5041 |
+normal options (always true, specified before other expressions):\n\ |
5042 |
+ -depth --help -maxdepth LEVELS -mindepth LEVELS -mount -noleaf\n\ |
5043 |
+ --version -xdev -ignore_readdir_race -noignore_readdir_race\n")); |
5044 |
+ puts (_("\ |
5045 |
+tests (N can be +N or -N or N): -amin N -anewer FILE -atime N -cmin N\n\ |
5046 |
+ -cnewer FILE -ctime N -empty -false -fstype TYPE -gid N -group NAME\n\ |
5047 |
+ -ilname PATTERN -iname PATTERN -inum N -iwholename PATTERN -iregex PATTERN\n\ |
5048 |
+ -links N -lname PATTERN -mmin N -mtime N -name PATTERN -newer FILE")); |
5049 |
+ puts (_("\ |
5050 |
+ -nouser -nogroup -path PATTERN -perm [+-]MODE -regex PATTERN\n\ |
5051 |
+ -readable -writable -executable\n\ |
5052 |
+ -wholename PATTERN -size N[bcwkMG] -true -type [bcdpflsD] -uid N\n\ |
5053 |
+ -used N -user NAME -xtype [bcdpfls]\n")); |
5054 |
+ puts (_("\ |
5055 |
+actions: -delete -print0 -printf FORMAT -fprintf FILE FORMAT -print \n\ |
5056 |
+ -fprint0 FILE -fprint FILE -ls -fls FILE -prune -quit\n\ |
5057 |
+ -exec COMMAND ; -exec COMMAND {} + -ok COMMAND ;\n\ |
5058 |
+ -execdir COMMAND ; -execdir COMMAND {} + -okdir COMMAND ;\n\ |
5059 |
+")); |
5060 |
+ puts (_("Report (and track progress on fixing) bugs via the findutils bug-reporting\n\ |
5061 |
+page at http://savannah.gnu.org/ or, if you have no web access, by sending\n\ |
5062 |
+email to <bug-findutils@×××.org>.")); |
5063 |
+ exit (0); |
5064 |
+} |
5065 |
+ |
5066 |
+static float |
5067 |
+estimate_pattern_match_rate(const char *pattern, int is_regex) |
5068 |
+{ |
5069 |
+ if (strpbrk(pattern, "*?[") || (is_regex && strpbrk(pattern, "."))) |
5070 |
+ { |
5071 |
+ /* A wildcard; assume the pattern matches most files. */ |
5072 |
+ return 0.8f; |
5073 |
+ } |
5074 |
+ else |
5075 |
+ { |
5076 |
+ return 0.1f; |
5077 |
+ } |
5078 |
+} |
5079 |
+ |
5080 |
+static boolean |
5081 |
+parse_ilname (const struct parser_table* entry, char **argv, int *arg_ptr) |
5082 |
+{ |
5083 |
+ const char *name; |
5084 |
+ if (collect_arg(argv, arg_ptr, &name)) |
5085 |
+ { |
5086 |
+ struct predicate *our_pred = insert_primary (entry); |
5087 |
+ our_pred->args.str = name; |
5088 |
+ /* Use the generic glob pattern estimator to figure out how many |
5089 |
+ * links will match, but bear in mind that most files won't be links. |
5090 |
+ */ |
5091 |
+ our_pred->est_success_rate = 0.1 * estimate_pattern_match_rate(name, 0); |
5092 |
+ return true; |
5093 |
+ } |
5094 |
+ else |
5095 |
+ { |
5096 |
+ return false; |
5097 |
+ } |
5098 |
+} |
5099 |
+ |
5100 |
+ |
5101 |
+/* sanity check the fnmatch() function to make sure that case folding |
5102 |
+ * is supported (as opposed to just having the flag ignored). |
5103 |
+ */ |
5104 |
+static boolean |
5105 |
+fnmatch_sanitycheck(void) |
5106 |
+{ |
5107 |
+ static boolean checked = false; |
5108 |
+ if (!checked) |
5109 |
+ { |
5110 |
+ if (0 != fnmatch("foo", "foo", 0) |
5111 |
+ || 0 == fnmatch("Foo", "foo", 0) |
5112 |
+ || 0 != fnmatch("Foo", "foo", FNM_CASEFOLD)) |
5113 |
+ { |
5114 |
+ error (1, 0, _("sanity check of the fnmatch() library function failed.")); |
5115 |
+ return false; |
5116 |
+ } |
5117 |
+ checked = true; |
5118 |
+ } |
5119 |
+ return checked; |
5120 |
+} |
5121 |
+ |
5122 |
+ |
5123 |
+static boolean |
5124 |
+check_name_arg(const char *pred, const char *arg) |
5125 |
+{ |
5126 |
+ if (options.warnings && strchr(arg, '/')) |
5127 |
+ { |
5128 |
+ error(0, 0,_("warning: Unix filenames usually don't contain slashes " |
5129 |
+ "(though pathnames do). That means that '%s %s' will " |
5130 |
+ "probably evaluate to false all the time on this system. " |
5131 |
+ "You might find the '-wholename' test more useful, or " |
5132 |
+ "perhaps '-samefile'. Alternatively, if you are using " |
5133 |
+ "GNU grep, you could " |
5134 |
+ "use 'find ... -print0 | grep -FzZ %s'."), |
5135 |
+ pred, |
5136 |
+ safely_quote_err_filename(0, arg), |
5137 |
+ safely_quote_err_filename(1, arg)); |
5138 |
+ } |
5139 |
+ return true; /* allow it anyway */ |
5140 |
+} |
5141 |
+ |
5142 |
+ |
5143 |
+ |
5144 |
+static boolean |
5145 |
+parse_iname (const struct parser_table* entry, char **argv, int *arg_ptr) |
5146 |
+{ |
5147 |
+ const char *name; |
5148 |
+ fnmatch_sanitycheck(); |
5149 |
+ if (collect_arg(argv, arg_ptr, &name)) |
5150 |
+ { |
5151 |
+ if (check_name_arg("-iname", name)) |
5152 |
+ { |
5153 |
+ struct predicate *our_pred = insert_primary (entry); |
5154 |
+ our_pred->need_stat = our_pred->need_type = false; |
5155 |
+ our_pred->args.str = name; |
5156 |
+ our_pred->est_success_rate = estimate_pattern_match_rate(name, 0); |
5157 |
+ return true; |
5158 |
+ } |
5159 |
+ } |
5160 |
+ return false; |
5161 |
+} |
5162 |
+ |
5163 |
+static boolean |
5164 |
+parse_inum (const struct parser_table* entry, char **argv, int *arg_ptr) |
5165 |
+{ |
5166 |
+ struct predicate *p = insert_num (argv, arg_ptr, entry); |
5167 |
+ if (p) |
5168 |
+ { |
5169 |
+ /* inode number is exact match only, so very low proportions of |
5170 |
+ * files match |
5171 |
+ */ |
5172 |
+ p->est_success_rate = 1e-6; |
5173 |
+ return true; |
5174 |
+ } |
5175 |
+ else |
5176 |
+ { |
5177 |
+ return false; |
5178 |
+ } |
5179 |
+} |
5180 |
+ |
5181 |
+/* -ipath is deprecated (at RMS's request) in favour of |
5182 |
+ * -iwholename. See the node "GNU Manuals" in standards.texi |
5183 |
+ * for the rationale for this (basically, GNU prefers the use |
5184 |
+ * of the phrase "file name" to "path name" |
5185 |
+ */ |
5186 |
+static boolean |
5187 |
+parse_ipath (const struct parser_table* entry, char **argv, int *arg_ptr) |
5188 |
+{ |
5189 |
+ const char *name; |
5190 |
+ |
5191 |
+ fnmatch_sanitycheck (); |
5192 |
+ if (collect_arg (argv, arg_ptr, &name)) |
5193 |
+ { |
5194 |
+ struct predicate *our_pred = insert_primary_withpred (entry, pred_ipath); |
5195 |
+ our_pred->need_stat = our_pred->need_type = false; |
5196 |
+ our_pred->args.str = name; |
5197 |
+ our_pred->est_success_rate = estimate_pattern_match_rate (name, 0); |
5198 |
+ return true; |
5199 |
+ } |
5200 |
+ return false; |
5201 |
+} |
5202 |
+ |
5203 |
+static boolean |
5204 |
+parse_iwholename (const struct parser_table* entry, char **argv, int *arg_ptr) |
5205 |
+{ |
5206 |
+ return parse_ipath (entry, argv, arg_ptr); |
5207 |
+} |
5208 |
+ |
5209 |
+static boolean |
5210 |
+parse_iregex (const struct parser_table* entry, char **argv, int *arg_ptr) |
5211 |
+{ |
5212 |
+ return insert_regex (argv, arg_ptr, entry, RE_ICASE|options.regex_options); |
5213 |
+} |
5214 |
+ |
5215 |
+static boolean |
5216 |
+parse_links (const struct parser_table* entry, char **argv, int *arg_ptr) |
5217 |
+{ |
5218 |
+ struct predicate *p = insert_num (argv, arg_ptr, entry); |
5219 |
+ if (p) |
5220 |
+ { |
5221 |
+ if (p->args.numinfo.l_val == 1) |
5222 |
+ p->est_success_rate = 0.99; |
5223 |
+ else if (p->args.numinfo.l_val == 2) |
5224 |
+ p->est_success_rate = 0.01; |
5225 |
+ else |
5226 |
+ p->est_success_rate = 1e-3; |
5227 |
+ return true; |
5228 |
+ } |
5229 |
+ else |
5230 |
+ { |
5231 |
+ return false; |
5232 |
+ } |
5233 |
+} |
5234 |
+ |
5235 |
+static boolean |
5236 |
+parse_lname (const struct parser_table* entry, char **argv, int *arg_ptr) |
5237 |
+{ |
5238 |
+ const char *name; |
5239 |
+ fnmatch_sanitycheck(); |
5240 |
+ if (collect_arg(argv, arg_ptr, &name)) |
5241 |
+ { |
5242 |
+ struct predicate *our_pred = insert_primary (entry); |
5243 |
+ our_pred->args.str = name; |
5244 |
+ our_pred->est_success_rate = 0.1 * estimate_pattern_match_rate(name, 0); |
5245 |
+ return true; |
5246 |
+ } |
5247 |
+ return false; |
5248 |
+} |
5249 |
+ |
5250 |
+static boolean |
5251 |
+parse_ls (const struct parser_table* entry, char **argv, int *arg_ptr) |
5252 |
+{ |
5253 |
+ (void) &argv; |
5254 |
+ (void) &arg_ptr; |
5255 |
+ return insert_fls(entry, NULL); |
5256 |
+} |
5257 |
+ |
5258 |
+static boolean |
5259 |
+insert_depthspec(const struct parser_table* entry, char **argv, int *arg_ptr, |
5260 |
+ int *limitptr) |
5261 |
+{ |
5262 |
+ const char *depthstr; |
5263 |
+ int depth_len; |
5264 |
+ const char *predicate = argv[(*arg_ptr)-1]; |
5265 |
+ if (collect_arg(argv, arg_ptr, &depthstr)) |
5266 |
+ { |
5267 |
+ depth_len = strspn (depthstr, "0123456789"); |
5268 |
+ if ((depth_len > 0) && (depthstr[depth_len] == 0)) |
5269 |
+ { |
5270 |
+ (*limitptr) = safe_atoi (depthstr); |
5271 |
+ if (*limitptr >= 0) |
5272 |
+ { |
5273 |
+ return parse_noop(entry, argv, arg_ptr); |
5274 |
+ } |
5275 |
+ } |
5276 |
+ error(1, 0, _("Expected a positive decimal integer argument to %1$s, but got %2$s"), |
5277 |
+ predicate, |
5278 |
+ quotearg_n_style(0, options.err_quoting_style, depthstr)); |
5279 |
+ return false; |
5280 |
+ } |
5281 |
+ /* missing argument */ |
5282 |
+ return false; |
5283 |
+} |
5284 |
+ |
5285 |
+ |
5286 |
+static boolean |
5287 |
+parse_maxdepth (const struct parser_table* entry, char **argv, int *arg_ptr) |
5288 |
+{ |
5289 |
+ return insert_depthspec(entry, argv, arg_ptr, &options.maxdepth); |
5290 |
+} |
5291 |
+ |
5292 |
+static boolean |
5293 |
+parse_mindepth (const struct parser_table* entry, char **argv, int *arg_ptr) |
5294 |
+{ |
5295 |
+ return insert_depthspec(entry, argv, arg_ptr, &options.mindepth); |
5296 |
+} |
5297 |
+ |
5298 |
+ |
5299 |
+static boolean |
5300 |
+do_parse_xmin (const struct parser_table* entry, |
5301 |
+ char **argv, |
5302 |
+ int *arg_ptr, |
5303 |
+ enum xval xv) |
5304 |
+{ |
5305 |
+ const char *minutes; |
5306 |
+ |
5307 |
+ if (collect_arg(argv, arg_ptr, &minutes)) |
5308 |
+ { |
5309 |
+ struct time_val tval; |
5310 |
+ tval.xval = xv; |
5311 |
+ if (get_relative_timestamp(minutes, &tval, |
5312 |
+ options.cur_day_start + DAYSECS, 60, |
5313 |
+ "arithmetic overflow while converting %s " |
5314 |
+ "minutes to a number of seconds")) |
5315 |
+ { |
5316 |
+ struct predicate *our_pred = insert_primary (entry); |
5317 |
+ our_pred->args.reftime = tval; |
5318 |
+ our_pred->est_success_rate = estimate_timestamp_success_rate(tval.ts.tv_sec); |
5319 |
+ return true; |
5320 |
+ } |
5321 |
+ } |
5322 |
+ return false; |
5323 |
+} |
5324 |
+static boolean |
5325 |
+parse_amin (const struct parser_table* entry, char **argv, int *arg_ptr) |
5326 |
+{ |
5327 |
+ return do_parse_xmin(entry, argv, arg_ptr, XVAL_ATIME); |
5328 |
+} |
5329 |
+ |
5330 |
+static boolean |
5331 |
+parse_cmin (const struct parser_table* entry, char **argv, int *arg_ptr) |
5332 |
+{ |
5333 |
+ return do_parse_xmin(entry, argv, arg_ptr, XVAL_CTIME); |
5334 |
+} |
5335 |
+ |
5336 |
+ |
5337 |
+static boolean |
5338 |
+parse_mmin (const struct parser_table* entry, char **argv, int *arg_ptr) |
5339 |
+{ |
5340 |
+ return do_parse_xmin(entry, argv, arg_ptr, XVAL_MTIME); |
5341 |
+} |
5342 |
+ |
5343 |
+static boolean |
5344 |
+parse_name (const struct parser_table* entry, char **argv, int *arg_ptr) |
5345 |
+{ |
5346 |
+ const char *name; |
5347 |
+ if (collect_arg(argv, arg_ptr, &name)) |
5348 |
+ { |
5349 |
+ fnmatch_sanitycheck(); |
5350 |
+ if (check_name_arg("-name", name)) |
5351 |
+ { |
5352 |
+ struct predicate *our_pred = insert_primary (entry); |
5353 |
+ our_pred->need_stat = our_pred->need_type = false; |
5354 |
+ our_pred->args.str = name; |
5355 |
+ our_pred->est_success_rate = estimate_pattern_match_rate(name, 0); |
5356 |
+ return true; |
5357 |
+ } |
5358 |
+ } |
5359 |
+ return false; |
5360 |
+} |
5361 |
+ |
5362 |
+static boolean |
5363 |
+parse_negate (const struct parser_table* entry, char **argv, int *arg_ptr) |
5364 |
+{ |
5365 |
+ struct predicate *our_pred; |
5366 |
+ |
5367 |
+ (void) &argv; |
5368 |
+ (void) &arg_ptr; |
5369 |
+ |
5370 |
+ our_pred = get_new_pred_chk_op (entry); |
5371 |
+ our_pred->pred_func = pred_negate; |
5372 |
+ our_pred->p_type = UNI_OP; |
5373 |
+ our_pred->p_prec = NEGATE_PREC; |
5374 |
+ our_pred->need_stat = our_pred->need_type = false; |
5375 |
+ return true; |
5376 |
+} |
5377 |
+ |
5378 |
+static boolean |
5379 |
+parse_newer (const struct parser_table* entry, char **argv, int *arg_ptr) |
5380 |
+{ |
5381 |
+ struct predicate *our_pred; |
5382 |
+ struct stat stat_newer; |
5383 |
+ |
5384 |
+ set_stat_placeholders(&stat_newer); |
5385 |
+ if (collect_arg_stat_info(argv, arg_ptr, &stat_newer)) |
5386 |
+ { |
5387 |
+ our_pred = insert_primary (entry); |
5388 |
+ our_pred->args.reftime.ts = get_stat_mtime(&stat_newer); |
5389 |
+ our_pred->args.reftime.xval = XVAL_MTIME; |
5390 |
+ our_pred->args.reftime.kind = COMP_GT; |
5391 |
+ our_pred->est_success_rate = estimate_timestamp_success_rate(stat_newer.st_mtime); |
5392 |
+ return true; |
5393 |
+ } |
5394 |
+ return false; |
5395 |
+} |
5396 |
+ |
5397 |
+ |
5398 |
+static boolean |
5399 |
+parse_newerXY (const struct parser_table* entry, char **argv, int *arg_ptr) |
5400 |
+{ |
5401 |
+ (void) argv; |
5402 |
+ (void) arg_ptr; |
5403 |
+ |
5404 |
+ if ((argv == NULL) || (argv[*arg_ptr] == NULL)) |
5405 |
+ { |
5406 |
+ return false; |
5407 |
+ } |
5408 |
+ else if (8u != strlen(argv[*arg_ptr])) |
5409 |
+ { |
5410 |
+ return false; |
5411 |
+ } |
5412 |
+ else |
5413 |
+ { |
5414 |
+ char x, y; |
5415 |
+ const char validchars[] = "aBcmt"; |
5416 |
+ |
5417 |
+ assert (0 == strncmp("-newer", argv[*arg_ptr], 6)); |
5418 |
+ x = argv[*arg_ptr][6]; |
5419 |
+ y = argv[*arg_ptr][7]; |
5420 |
+ |
5421 |
+ |
5422 |
+#if !defined(HAVE_STRUCT_STAT_ST_BIRTHTIME) && !defined(HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC) && !defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC) |
5423 |
+ if ('B' == x || 'B' == y) |
5424 |
+ { |
5425 |
+ error(0, 0, |
5426 |
+ _("This system does not provide a way to find the birth time of a file.")); |
5427 |
+ return 0; |
5428 |
+ } |
5429 |
+#endif |
5430 |
+ |
5431 |
+ /* -newertY (for any Y) is invalid. */ |
5432 |
+ if (x == 't' |
5433 |
+ || 0 == strchr(validchars, x) |
5434 |
+ || 0 == strchr( validchars, y)) |
5435 |
+ { |
5436 |
+ return false; |
5437 |
+ } |
5438 |
+ else |
5439 |
+ { |
5440 |
+ struct predicate *our_pred; |
5441 |
+ |
5442 |
+ /* Because this item is ARG_SPECIAL_PARSE, we have to advance arg_ptr |
5443 |
+ * past the test name (for most other tests, this is already done) |
5444 |
+ */ |
5445 |
+ (*arg_ptr)++; |
5446 |
+ |
5447 |
+ our_pred = insert_primary (entry); |
5448 |
+ |
5449 |
+ |
5450 |
+ switch (x) |
5451 |
+ { |
5452 |
+ case 'a': |
5453 |
+ our_pred->args.reftime.xval = XVAL_ATIME; |
5454 |
+ break; |
5455 |
+ case 'B': |
5456 |
+ our_pred->args.reftime.xval = XVAL_BIRTHTIME; |
5457 |
+ break; |
5458 |
+ case 'c': |
5459 |
+ our_pred->args.reftime.xval = XVAL_CTIME; |
5460 |
+ break; |
5461 |
+ case 'm': |
5462 |
+ our_pred->args.reftime.xval = XVAL_MTIME; |
5463 |
+ break; |
5464 |
+ default: |
5465 |
+ assert (strchr(validchars, x)); |
5466 |
+ assert (0); |
5467 |
+ } |
5468 |
+ |
5469 |
+ if ('t' == y) |
5470 |
+ { |
5471 |
+ if (!get_date(&our_pred->args.reftime.ts, |
5472 |
+ argv[*arg_ptr], |
5473 |
+ &options.start_time)) |
5474 |
+ { |
5475 |
+ error(1, 0, |
5476 |
+ _("I cannot figure out how to interpret %s as a date or time"), |
5477 |
+ quotearg_n_style(0, options.err_quoting_style, argv[*arg_ptr])); |
5478 |
+ } |
5479 |
+ } |
5480 |
+ else |
5481 |
+ { |
5482 |
+ struct stat stat_newer; |
5483 |
+ |
5484 |
+ /* Stat the named file. */ |
5485 |
+ set_stat_placeholders(&stat_newer); |
5486 |
+ if ((*options.xstat) (argv[*arg_ptr], &stat_newer)) |
5487 |
+ fatal_file_error(argv[*arg_ptr]); |
5488 |
+ |
5489 |
+ if (!get_stat_Ytime(&stat_newer, y, &our_pred->args.reftime.ts)) |
5490 |
+ { |
5491 |
+ /* We cannot extract a timestamp from the struct stat. */ |
5492 |
+ error(1, 0, _("Cannot obtain birth time of file %s"), |
5493 |
+ safely_quote_err_filename(0, argv[*arg_ptr])); |
5494 |
+ } |
5495 |
+ } |
5496 |
+ our_pred->args.reftime.kind = COMP_GT; |
5497 |
+ our_pred->est_success_rate = estimate_timestamp_success_rate(our_pred->args.reftime.ts.tv_sec); |
5498 |
+ (*arg_ptr)++; |
5499 |
+ |
5500 |
+ assert (our_pred->pred_func != NULL); |
5501 |
+ assert (our_pred->pred_func == pred_newerXY); |
5502 |
+ assert (our_pred->need_stat); |
5503 |
+ return true; |
5504 |
+ } |
5505 |
+ } |
5506 |
+} |
5507 |
+ |
5508 |
+ |
5509 |
+static boolean |
5510 |
+parse_noleaf (const struct parser_table* entry, char **argv, int *arg_ptr) |
5511 |
+{ |
5512 |
+ options.no_leaf_check = true; |
5513 |
+ return parse_noop(entry, argv, arg_ptr); |
5514 |
+} |
5515 |
+ |
5516 |
+#ifdef CACHE_IDS |
5517 |
+/* Arbitrary amount by which to increase size |
5518 |
+ of `uid_unused' and `gid_unused'. */ |
5519 |
+#define ALLOC_STEP 2048 |
5520 |
+ |
5521 |
+/* Boolean: if uid_unused[n] is nonzero, then UID n has no passwd entry. */ |
5522 |
+char *uid_unused = NULL; |
5523 |
+ |
5524 |
+/* Number of elements in `uid_unused'. */ |
5525 |
+unsigned uid_allocated; |
5526 |
+ |
5527 |
+/* Similar for GIDs and group entries. */ |
5528 |
+char *gid_unused = NULL; |
5529 |
+unsigned gid_allocated; |
5530 |
+#endif |
5531 |
+ |
5532 |
+static boolean |
5533 |
+parse_nogroup (const struct parser_table* entry, char **argv, int *arg_ptr) |
5534 |
+{ |
5535 |
+ struct predicate *our_pred; |
5536 |
+ |
5537 |
+ (void) &argv; |
5538 |
+ (void) &arg_ptr; |
5539 |
+ |
5540 |
+ our_pred = insert_primary (entry); |
5541 |
+ our_pred->est_success_rate = 1e-4; |
5542 |
+#ifdef CACHE_IDS |
5543 |
+ if (gid_unused == NULL) |
5544 |
+ { |
5545 |
+ struct group *gr; |
5546 |
+ |
5547 |
+ gid_allocated = ALLOC_STEP; |
5548 |
+ gid_unused = xmalloc (gid_allocated); |
5549 |
+ memset (gid_unused, 1, gid_allocated); |
5550 |
+ setgrent (); |
5551 |
+ while ((gr = getgrent ()) != NULL) |
5552 |
+ { |
5553 |
+ if ((unsigned) gr->gr_gid >= gid_allocated) |
5554 |
+ { |
5555 |
+ unsigned new_allocated = (unsigned) gr->gr_gid + ALLOC_STEP; |
5556 |
+ gid_unused = xrealloc (gid_unused, new_allocated); |
5557 |
+ memset (gid_unused + gid_allocated, 1, |
5558 |
+ new_allocated - gid_allocated); |
5559 |
+ gid_allocated = new_allocated; |
5560 |
+ } |
5561 |
+ gid_unused[(unsigned) gr->gr_gid] = 0; |
5562 |
+ } |
5563 |
+ endgrent (); |
5564 |
+ } |
5565 |
+#endif |
5566 |
+ return true; |
5567 |
+} |
5568 |
+ |
5569 |
+static boolean |
5570 |
+parse_nouser (const struct parser_table* entry, char **argv, int *arg_ptr) |
5571 |
+{ |
5572 |
+ struct predicate *our_pred; |
5573 |
+ (void) argv; |
5574 |
+ (void) arg_ptr; |
5575 |
+ |
5576 |
+ |
5577 |
+ our_pred = insert_primary (entry); |
5578 |
+ our_pred->est_success_rate = 1e-3; |
5579 |
+#ifdef CACHE_IDS |
5580 |
+ if (uid_unused == NULL) |
5581 |
+ { |
5582 |
+ struct passwd *pw; |
5583 |
+ |
5584 |
+ uid_allocated = ALLOC_STEP; |
5585 |
+ uid_unused = xmalloc (uid_allocated); |
5586 |
+ memset (uid_unused, 1, uid_allocated); |
5587 |
+ setpwent (); |
5588 |
+ while ((pw = getpwent ()) != NULL) |
5589 |
+ { |
5590 |
+ if ((unsigned) pw->pw_uid >= uid_allocated) |
5591 |
+ { |
5592 |
+ unsigned new_allocated = (unsigned) pw->pw_uid + ALLOC_STEP; |
5593 |
+ uid_unused = xrealloc (uid_unused, new_allocated); |
5594 |
+ memset (uid_unused + uid_allocated, 1, |
5595 |
+ new_allocated - uid_allocated); |
5596 |
+ uid_allocated = new_allocated; |
5597 |
+ } |
5598 |
+ uid_unused[(unsigned) pw->pw_uid] = 0; |
5599 |
+ } |
5600 |
+ endpwent (); |
5601 |
+ } |
5602 |
+#endif |
5603 |
+ return true; |
5604 |
+} |
5605 |
+ |
5606 |
+static boolean |
5607 |
+parse_nowarn (const struct parser_table* entry, char **argv, int *arg_ptr) |
5608 |
+{ |
5609 |
+ options.warnings = false; |
5610 |
+ return parse_noop(entry, argv, arg_ptr); |
5611 |
+} |
5612 |
+ |
5613 |
+static boolean |
5614 |
+parse_ok (const struct parser_table* entry, char **argv, int *arg_ptr) |
5615 |
+{ |
5616 |
+ return insert_exec_ok ("-ok", entry, get_start_dirfd(), argv, arg_ptr); |
5617 |
+} |
5618 |
+ |
5619 |
+static boolean |
5620 |
+parse_okdir (const struct parser_table* entry, char **argv, int *arg_ptr) |
5621 |
+{ |
5622 |
+ return insert_exec_ok ("-okdir", entry, -1, argv, arg_ptr); |
5623 |
+} |
5624 |
+ |
5625 |
+boolean |
5626 |
+parse_openparen (const struct parser_table* entry, char **argv, int *arg_ptr) |
5627 |
+{ |
5628 |
+ struct predicate *our_pred; |
5629 |
+ |
5630 |
+ (void) argv; |
5631 |
+ (void) arg_ptr; |
5632 |
+ |
5633 |
+ our_pred = get_new_pred_chk_op (entry); |
5634 |
+ our_pred->pred_func = pred_openparen; |
5635 |
+ our_pred->p_type = OPEN_PAREN; |
5636 |
+ our_pred->p_prec = NO_PREC; |
5637 |
+ our_pred->need_stat = our_pred->need_type = false; |
5638 |
+ return true; |
5639 |
+} |
5640 |
+ |
5641 |
+static boolean |
5642 |
+parse_or (const struct parser_table* entry, char **argv, int *arg_ptr) |
5643 |
+{ |
5644 |
+ struct predicate *our_pred; |
5645 |
+ |
5646 |
+ (void) argv; |
5647 |
+ (void) arg_ptr; |
5648 |
+ |
5649 |
+ our_pred = get_new_pred (entry); |
5650 |
+ our_pred->pred_func = pred_or; |
5651 |
+ our_pred->p_type = BI_OP; |
5652 |
+ our_pred->p_prec = OR_PREC; |
5653 |
+ our_pred->need_stat = our_pred->need_type = false; |
5654 |
+ return true; |
5655 |
+} |
5656 |
+ |
5657 |
+/* For some time, -path was deprecated (at RMS's request) in favour of |
5658 |
+ * -iwholename. See the node "GNU Manuals" in standards.texi for the |
5659 |
+ * rationale for this (basically, GNU prefers the use of the phrase |
5660 |
+ * "file name" to "path name". |
5661 |
+ * |
5662 |
+ * We do not issue a warning that this usage is deprecated |
5663 |
+ * since |
5664 |
+ * (a) HPUX find supports this predicate also and |
5665 |
+ * (b) it will soon be in POSIX anyway. |
5666 |
+ */ |
5667 |
+static boolean |
5668 |
+parse_path (const struct parser_table* entry, char **argv, int *arg_ptr) |
5669 |
+{ |
5670 |
+ const char *name; |
5671 |
+ if (collect_arg(argv, arg_ptr, &name)) |
5672 |
+ { |
5673 |
+ struct predicate *our_pred = insert_primary_withpred (entry, pred_path); |
5674 |
+ our_pred->need_stat = our_pred->need_type = false; |
5675 |
+ our_pred->args.str = name; |
5676 |
+ our_pred->est_success_rate = estimate_pattern_match_rate (name, 0); |
5677 |
+ return true; |
5678 |
+ } |
5679 |
+ return false; |
5680 |
+} |
5681 |
+ |
5682 |
+static boolean |
5683 |
+parse_wholename (const struct parser_table* entry, char **argv, int *arg_ptr) |
5684 |
+{ |
5685 |
+ return parse_path (entry, argv, arg_ptr); |
5686 |
+} |
5687 |
+ |
5688 |
+static void |
5689 |
+non_posix_mode(const char *mode) |
5690 |
+{ |
5691 |
+ if (options.posixly_correct) |
5692 |
+ { |
5693 |
+ error (1, 0, _("Mode %s is not valid when POSIXLY_CORRECT is on."), |
5694 |
+ quotearg_n_style(0, options.err_quoting_style, mode)); |
5695 |
+ } |
5696 |
+} |
5697 |
+ |
5698 |
+ |
5699 |
+static boolean |
5700 |
+parse_perm (const struct parser_table* entry, char **argv, int *arg_ptr) |
5701 |
+{ |
5702 |
+ mode_t perm_val[2]; |
5703 |
+ float rate; |
5704 |
+ int mode_start = 0; |
5705 |
+ boolean havekind = false; |
5706 |
+ enum permissions_type kind = PERM_EXACT; |
5707 |
+ struct mode_change *change = NULL; |
5708 |
+ struct predicate *our_pred; |
5709 |
+ const char *perm_expr; |
5710 |
+ |
5711 |
+ if (!collect_arg(argv, arg_ptr, &perm_expr)) |
5712 |
+ return false; |
5713 |
+ |
5714 |
+ switch (perm_expr[0]) |
5715 |
+ { |
5716 |
+ case '-': |
5717 |
+ mode_start = 1; |
5718 |
+ kind = PERM_AT_LEAST; |
5719 |
+ havekind = true; |
5720 |
+ rate = 0.2; |
5721 |
+ break; |
5722 |
+ |
5723 |
+ case '+': |
5724 |
+ change = mode_compile (perm_expr); |
5725 |
+ if (NULL == change) |
5726 |
+ { |
5727 |
+ /* Most likely the caller is an old script that is still |
5728 |
+ * using the obsolete GNU syntax '-perm +MODE'. This old |
5729 |
+ * syntax was withdrawn in favor of '-perm /MODE' because |
5730 |
+ * it is incompatible with POSIX in some cases, but we |
5731 |
+ * still support uses of it that are not incompatible with |
5732 |
+ * POSIX. |
5733 |
+ * |
5734 |
+ * Example: POSIXLY_CORRECT=y find -perm +a+x |
5735 |
+ */ |
5736 |
+ non_posix_mode(perm_expr); |
5737 |
+ |
5738 |
+ /* support the previous behaviour. */ |
5739 |
+ mode_start = 1; |
5740 |
+ kind = PERM_ANY; |
5741 |
+ rate = 0.3; |
5742 |
+ } |
5743 |
+ else |
5744 |
+ { |
5745 |
+ /* This is a POSIX-compatible usage */ |
5746 |
+ mode_start = 0; |
5747 |
+ kind = PERM_EXACT; |
5748 |
+ rate = 0.1; |
5749 |
+ } |
5750 |
+ havekind = true; |
5751 |
+ break; |
5752 |
+ |
5753 |
+ case '/': /* GNU extension */ |
5754 |
+ non_posix_mode(perm_expr); |
5755 |
+ mode_start = 1; |
5756 |
+ kind = PERM_ANY; |
5757 |
+ havekind = true; |
5758 |
+ rate = 0.3; |
5759 |
+ break; |
5760 |
+ |
5761 |
+ default: |
5762 |
+ /* For example, '-perm 0644', which is valid and matches |
5763 |
+ * only files whose mode is exactly 0644. |
5764 |
+ */ |
5765 |
+ mode_start = 0; |
5766 |
+ kind = PERM_EXACT; |
5767 |
+ havekind = true; |
5768 |
+ rate = 0.01; |
5769 |
+ break; |
5770 |
+ } |
5771 |
+ |
5772 |
+ if (NULL == change) |
5773 |
+ { |
5774 |
+ change = mode_compile (perm_expr + mode_start); |
5775 |
+ if (NULL == change) |
5776 |
+ error (1, 0, _("invalid mode %s" |
5777 |
+ /* TRANSLATORS: the argument is a |
5778 |
+ * file permission string like 'u=rw,go=' |
5779 |
+ */), |
5780 |
+ quotearg_n_style(0, options.err_quoting_style, perm_expr)); |
5781 |
+ } |
5782 |
+ perm_val[0] = mode_adjust (0, false, 0, change, NULL); |
5783 |
+ perm_val[1] = mode_adjust (0, true, 0, change, NULL); |
5784 |
+ free (change); |
5785 |
+ |
5786 |
+ if (('/' == perm_expr[0]) && (0 == perm_val[0]) && (0 == perm_val[1])) |
5787 |
+ { |
5788 |
+ /* The meaning of -perm /000 will change in the future. It |
5789 |
+ * currently matches no files, but like -perm -000 it should |
5790 |
+ * match all files. |
5791 |
+ * |
5792 |
+ * Starting in 2005, we used to issue a warning message |
5793 |
+ * informing the user that the behaviour would change in the |
5794 |
+ * future. We have now changed the behaviour and issue a |
5795 |
+ * warning message that the behaviour recently changed. |
5796 |
+ */ |
5797 |
+ error (0, 0, |
5798 |
+ _("warning: you have specified a mode pattern %s (which is " |
5799 |
+ "equivalent to /000). The meaning of -perm /000 has now been " |
5800 |
+ "changed to be consistent with -perm -000; that is, while it " |
5801 |
+ "used to match no files, it now matches all files."), |
5802 |
+ perm_expr); |
5803 |
+ |
5804 |
+ kind = PERM_AT_LEAST; |
5805 |
+ havekind = true; |
5806 |
+ |
5807 |
+ /* The "magic" number below is just the fraction of files on my |
5808 |
+ * own system that "-type l -xtype l" fails for (i.e. unbroken symlinks). |
5809 |
+ * Actual totals are 1472 and 1073833. |
5810 |
+ */ |
5811 |
+ rate = 0.9986; /* probably matches anything but a broken symlink */ |
5812 |
+ } |
5813 |
+ |
5814 |
+ our_pred = insert_primary (entry); |
5815 |
+ our_pred->est_success_rate = rate; |
5816 |
+ if (havekind) |
5817 |
+ { |
5818 |
+ our_pred->args.perm.kind = kind; |
5819 |
+ } |
5820 |
+ else |
5821 |
+ { |
5822 |
+ |
5823 |
+ switch (perm_expr[0]) |
5824 |
+ { |
5825 |
+ case '-': |
5826 |
+ our_pred->args.perm.kind = PERM_AT_LEAST; |
5827 |
+ break; |
5828 |
+ case '+': |
5829 |
+ our_pred->args.perm.kind = PERM_ANY; |
5830 |
+ break; |
5831 |
+ default: |
5832 |
+ our_pred->args.perm.kind = PERM_EXACT; |
5833 |
+ break; |
5834 |
+ } |
5835 |
+ } |
5836 |
+ memcpy (our_pred->args.perm.val, perm_val, sizeof perm_val); |
5837 |
+ return true; |
5838 |
+} |
5839 |
+ |
5840 |
+boolean |
5841 |
+parse_print (const struct parser_table* entry, char **argv, int *arg_ptr) |
5842 |
+{ |
5843 |
+ struct predicate *our_pred; |
5844 |
+ |
5845 |
+ (void) argv; |
5846 |
+ (void) arg_ptr; |
5847 |
+ |
5848 |
+ our_pred = insert_primary (entry); |
5849 |
+ /* -print has the side effect of printing. This prevents us |
5850 |
+ from doing undesired multiple printing when the user has |
5851 |
+ already specified -print. */ |
5852 |
+ our_pred->side_effects = our_pred->no_default_print = true; |
5853 |
+ our_pred->need_stat = our_pred->need_type = false; |
5854 |
+ open_stdout(&our_pred->args.printf_vec); |
5855 |
+ return true; |
5856 |
+} |
5857 |
+ |
5858 |
+static boolean |
5859 |
+parse_print0 (const struct parser_table* entry, char **argv, int *arg_ptr) |
5860 |
+{ |
5861 |
+ return insert_fprint(entry, NULL); |
5862 |
+} |
5863 |
+ |
5864 |
+static boolean |
5865 |
+parse_printf (const struct parser_table* entry, char **argv, int *arg_ptr) |
5866 |
+{ |
5867 |
+ const char *format; |
5868 |
+ if (collect_arg(argv, arg_ptr, &format)) |
5869 |
+ { |
5870 |
+ struct format_val fmt; |
5871 |
+ open_stdout(&fmt); |
5872 |
+ return insert_fprintf (&fmt, entry, pred_fprintf, format); |
5873 |
+ } |
5874 |
+ return false; |
5875 |
+} |
5876 |
+ |
5877 |
+static boolean |
5878 |
+parse_fprintf (const struct parser_table* entry, char **argv, int *arg_ptr) |
5879 |
+{ |
5880 |
+ const char *format, *filename; |
5881 |
+ if (collect_arg(argv, arg_ptr, &filename)) |
5882 |
+ { |
5883 |
+ if (collect_arg(argv, arg_ptr, &format)) |
5884 |
+ { |
5885 |
+ struct format_val fmt; |
5886 |
+ open_output_file (filename, &fmt); |
5887 |
+ return insert_fprintf (&fmt, entry, pred_fprintf, format); |
5888 |
+ } |
5889 |
+ } |
5890 |
+ return false; |
5891 |
+} |
5892 |
+ |
5893 |
+static boolean |
5894 |
+parse_prune (const struct parser_table* entry, char **argv, int *arg_ptr) |
5895 |
+{ |
5896 |
+ struct predicate *our_pred; |
5897 |
+ |
5898 |
+ (void) argv; |
5899 |
+ (void) arg_ptr; |
5900 |
+ |
5901 |
+ our_pred = insert_primary (entry); |
5902 |
+ our_pred->need_stat = our_pred->need_type = false; |
5903 |
+ /* -prune has a side effect that it does not descend into |
5904 |
+ the current directory. */ |
5905 |
+ our_pred->side_effects = true; |
5906 |
+ our_pred->no_default_print = false; |
5907 |
+ return true; |
5908 |
+} |
5909 |
+ |
5910 |
+static boolean |
5911 |
+parse_quit (const struct parser_table* entry, char **argv, int *arg_ptr) |
5912 |
+{ |
5913 |
+ struct predicate *our_pred = insert_primary (entry); |
5914 |
+ (void) argv; |
5915 |
+ (void) arg_ptr; |
5916 |
+ our_pred->need_stat = our_pred->need_type = false; |
5917 |
+ our_pred->side_effects = true; /* Exiting is a side effect... */ |
5918 |
+ our_pred->no_default_print = false; /* Don't inhibit the default print, though. */ |
5919 |
+ our_pred->est_success_rate = 1.0f; |
5920 |
+ return true; |
5921 |
+} |
5922 |
+ |
5923 |
+ |
5924 |
+static boolean |
5925 |
+parse_regextype (const struct parser_table* entry, char **argv, int *arg_ptr) |
5926 |
+{ |
5927 |
+ const char *type_name; |
5928 |
+ if (collect_arg(argv, arg_ptr, &type_name)) |
5929 |
+ { |
5930 |
+ /* collect the regex type name */ |
5931 |
+ options.regex_options = get_regex_type(type_name); |
5932 |
+ return parse_noop(entry, argv, arg_ptr); |
5933 |
+ } |
5934 |
+ return false; |
5935 |
+} |
5936 |
+ |
5937 |
+ |
5938 |
+static boolean |
5939 |
+parse_regex (const struct parser_table* entry, char **argv, int *arg_ptr) |
5940 |
+{ |
5941 |
+ return insert_regex (argv, arg_ptr, entry, options.regex_options); |
5942 |
+} |
5943 |
+ |
5944 |
+static boolean |
5945 |
+insert_regex (char **argv, |
5946 |
+ int *arg_ptr, |
5947 |
+ const struct parser_table *entry, |
5948 |
+ int regex_options) |
5949 |
+{ |
5950 |
+ const char *rx; |
5951 |
+ if (collect_arg(argv, arg_ptr, &rx)) |
5952 |
+ { |
5953 |
+ struct re_pattern_buffer *re; |
5954 |
+ const char *error_message; |
5955 |
+ struct predicate *our_pred = insert_primary_withpred (entry, pred_regex); |
5956 |
+ our_pred->need_stat = our_pred->need_type = false; |
5957 |
+ re = xmalloc (sizeof (struct re_pattern_buffer)); |
5958 |
+ our_pred->args.regex = re; |
5959 |
+ re->allocated = 100; |
5960 |
+ re->buffer = xmalloc (re->allocated); |
5961 |
+ re->fastmap = NULL; |
5962 |
+ |
5963 |
+ re_set_syntax(regex_options); |
5964 |
+ re->syntax = regex_options; |
5965 |
+ re->translate = NULL; |
5966 |
+ |
5967 |
+ error_message = re_compile_pattern (rx, strlen(rx), re); |
5968 |
+ if (error_message) |
5969 |
+ error (1, 0, "%s", error_message); |
5970 |
+ our_pred->est_success_rate = estimate_pattern_match_rate(rx, 1); |
5971 |
+ return true; |
5972 |
+ } |
5973 |
+ return false; |
5974 |
+} |
5975 |
+ |
5976 |
+static boolean |
5977 |
+parse_size (const struct parser_table* entry, char **argv, int *arg_ptr) |
5978 |
+{ |
5979 |
+ struct predicate *our_pred; |
5980 |
+ uintmax_t num; |
5981 |
+ char suffix; |
5982 |
+ enum comparison_type c_type; |
5983 |
+ |
5984 |
+ int blksize = 512; |
5985 |
+ int len; |
5986 |
+ |
5987 |
+ /* XXX: cannot (yet) convert to ue collect_arg() as this |
5988 |
+ * function modifies the args in-place. |
5989 |
+ */ |
5990 |
+ if ((argv == NULL) || (argv[*arg_ptr] == NULL)) |
5991 |
+ return false; |
5992 |
+ |
5993 |
+ len = strlen (argv[*arg_ptr]); |
5994 |
+ if (len == 0) |
5995 |
+ error (1, 0, _("invalid null argument to -size")); |
5996 |
+ |
5997 |
+ suffix = argv[*arg_ptr][len - 1]; |
5998 |
+ switch (suffix) |
5999 |
+ { |
6000 |
+ case 'b': |
6001 |
+ blksize = 512; |
6002 |
+ argv[*arg_ptr][len - 1] = '\0'; |
6003 |
+ break; |
6004 |
+ |
6005 |
+ case 'c': |
6006 |
+ blksize = 1; |
6007 |
+ argv[*arg_ptr][len - 1] = '\0'; |
6008 |
+ break; |
6009 |
+ |
6010 |
+ case 'k': |
6011 |
+ blksize = 1024; |
6012 |
+ argv[*arg_ptr][len - 1] = '\0'; |
6013 |
+ break; |
6014 |
+ |
6015 |
+ case 'M': /* Megabytes */ |
6016 |
+ blksize = 1024*1024; |
6017 |
+ argv[*arg_ptr][len - 1] = '\0'; |
6018 |
+ break; |
6019 |
+ |
6020 |
+ case 'G': /* Gigabytes */ |
6021 |
+ blksize = 1024*1024*1024; |
6022 |
+ argv[*arg_ptr][len - 1] = '\0'; |
6023 |
+ break; |
6024 |
+ |
6025 |
+ case 'w': |
6026 |
+ blksize = 2; |
6027 |
+ argv[*arg_ptr][len - 1] = '\0'; |
6028 |
+ break; |
6029 |
+ |
6030 |
+ case '0': |
6031 |
+ case '1': |
6032 |
+ case '2': |
6033 |
+ case '3': |
6034 |
+ case '4': |
6035 |
+ case '5': |
6036 |
+ case '6': |
6037 |
+ case '7': |
6038 |
+ case '8': |
6039 |
+ case '9': |
6040 |
+ break; |
6041 |
+ |
6042 |
+ default: |
6043 |
+ error (1, 0, _("invalid -size type `%c'"), argv[*arg_ptr][len - 1]); |
6044 |
+ } |
6045 |
+ /* TODO: accept fractional megabytes etc. ? */ |
6046 |
+ if (!get_num (argv[*arg_ptr], &num, &c_type)) |
6047 |
+ { |
6048 |
+ error(1, 0, |
6049 |
+ _("Invalid argument `%s%c' to -size"), |
6050 |
+ argv[*arg_ptr], (int)suffix); |
6051 |
+ return false; |
6052 |
+ } |
6053 |
+ our_pred = insert_primary (entry); |
6054 |
+ our_pred->args.size.kind = c_type; |
6055 |
+ our_pred->args.size.blocksize = blksize; |
6056 |
+ our_pred->args.size.size = num; |
6057 |
+ our_pred->need_stat = true; |
6058 |
+ our_pred->need_type = false; |
6059 |
+ |
6060 |
+ if (COMP_GT == c_type) |
6061 |
+ our_pred->est_success_rate = (num*blksize > 20480) ? 0.1 : 0.9; |
6062 |
+ else if (COMP_LT == c_type) |
6063 |
+ our_pred->est_success_rate = (num*blksize > 20480) ? 0.9 : 0.1; |
6064 |
+ else |
6065 |
+ our_pred->est_success_rate = 0.01; |
6066 |
+ |
6067 |
+ (*arg_ptr)++; |
6068 |
+ return true; |
6069 |
+} |
6070 |
+ |
6071 |
+ |
6072 |
+static boolean |
6073 |
+parse_samefile (const struct parser_table* entry, char **argv, int *arg_ptr) |
6074 |
+{ |
6075 |
+ /* General idea: stat the file, remember device and inode numbers. |
6076 |
+ * If a candidate file matches those, it's the same file. |
6077 |
+ */ |
6078 |
+ struct predicate *our_pred; |
6079 |
+ struct stat st, fst; |
6080 |
+ int fd, openflags; |
6081 |
+ |
6082 |
+ set_stat_placeholders(&st); |
6083 |
+ if (!collect_arg_stat_info(argv, arg_ptr, &st)) |
6084 |
+ return false; |
6085 |
+ |
6086 |
+ set_stat_placeholders(&fst); |
6087 |
+ /* POSIX systems are free to re-use the inode number of a deleted |
6088 |
+ * file. To ensure that we are not fooled by inode reuse, we hold |
6089 |
+ * the file open if we can. This would prevent the system reusing |
6090 |
+ * the file. |
6091 |
+ */ |
6092 |
+ fd = -3; /* means, uninitialised */ |
6093 |
+ openflags = O_RDONLY; |
6094 |
+ |
6095 |
+ if (options.symlink_handling == SYMLINK_NEVER_DEREF) |
6096 |
+ { |
6097 |
+ if (options.open_nofollow_available) |
6098 |
+ { |
6099 |
+ assert (O_NOFOLLOW != 0); |
6100 |
+ openflags |= O_NOFOLLOW; |
6101 |
+ fd = -1; /* safe to open it. */ |
6102 |
+ } |
6103 |
+ else |
6104 |
+ { |
6105 |
+ if (S_ISLNK(st.st_mode)) |
6106 |
+ { |
6107 |
+ /* no way to ensure that a symlink will not be followed |
6108 |
+ * by open(2), so fall back on using lstat(). Accept |
6109 |
+ * the risk that the named file will be deleted and |
6110 |
+ * replaced with another having the same inode. |
6111 |
+ * |
6112 |
+ * Avoid opening the file. |
6113 |
+ */ |
6114 |
+ fd = -2; /* Do not open it */ |
6115 |
+ } |
6116 |
+ else |
6117 |
+ { |
6118 |
+ fd = -1; |
6119 |
+ /* Race condition here: the file might become a symlink here. */ |
6120 |
+ } |
6121 |
+ } |
6122 |
+ } |
6123 |
+ else |
6124 |
+ { |
6125 |
+ /* We want to dereference the symlink anyway */ |
6126 |
+ fd = -1; /* safe to open it without O_NOFOLLOW */ |
6127 |
+ } |
6128 |
+ |
6129 |
+ assert (fd != -3); /* check we made a decision */ |
6130 |
+ if (fd == -1) |
6131 |
+ { |
6132 |
+ /* Race condition here. The file might become a |
6133 |
+ * symbolic link in between out call to stat and |
6134 |
+ * the call to open. |
6135 |
+ */ |
6136 |
+ fd = open(argv[*arg_ptr], openflags); |
6137 |
+ |
6138 |
+ if (fd >= 0) |
6139 |
+ { |
6140 |
+ /* We stat the file again here to prevent a race condition |
6141 |
+ * between the first stat and the call to open(2). |
6142 |
+ */ |
6143 |
+ if (0 != fstat(fd, &fst)) |
6144 |
+ { |
6145 |
+ fatal_file_error(argv[*arg_ptr]); |
6146 |
+ } |
6147 |
+ else |
6148 |
+ { |
6149 |
+ /* Worry about the race condition. If the file became a |
6150 |
+ * symlink after our first stat and before our call to |
6151 |
+ * open, fst may contain the stat information for the |
6152 |
+ * destination of the link, not the link itself. |
6153 |
+ */ |
6154 |
+ if ((*options.xstat) (argv[*arg_ptr], &st)) |
6155 |
+ fatal_file_error(argv[*arg_ptr]); |
6156 |
+ |
6157 |
+ if ((options.symlink_handling == SYMLINK_NEVER_DEREF) |
6158 |
+ && (!options.open_nofollow_available)) |
6159 |
+ { |
6160 |
+ if (S_ISLNK(st.st_mode)) |
6161 |
+ { |
6162 |
+ /* We lost the race. Leave the data in st. The |
6163 |
+ * file descriptor points to the wrong thing. |
6164 |
+ */ |
6165 |
+ close(fd); |
6166 |
+ fd = -1; |
6167 |
+ } |
6168 |
+ else |
6169 |
+ { |
6170 |
+ /* Several possibilities here: |
6171 |
+ * 1. There was no race |
6172 |
+ * 2. The file changed into a symlink after the stat and |
6173 |
+ * before the open, and then back into a non-symlink |
6174 |
+ * before the second stat. |
6175 |
+ * |
6176 |
+ * In case (1) there is no problem. In case (2), |
6177 |
+ * the stat() and fstat() calls will have returned |
6178 |
+ * different data. O_NOFOLLOW was not available, |
6179 |
+ * so the open() call may have followed a symlink |
6180 |
+ * even if the -P option is in effect. |
6181 |
+ */ |
6182 |
+ if ((st.st_dev == fst.st_dev) |
6183 |
+ && (st.st_ino == fst.st_ino)) |
6184 |
+ { |
6185 |
+ /* No race. No need to copy fst to st, |
6186 |
+ * since they should be identical (modulo |
6187 |
+ * differences in padding bytes). |
6188 |
+ */ |
6189 |
+ } |
6190 |
+ else |
6191 |
+ { |
6192 |
+ /* We lost the race. Leave the data in st. The |
6193 |
+ * file descriptor points to the wrong thing. |
6194 |
+ */ |
6195 |
+ close(fd); |
6196 |
+ fd = -1; |
6197 |
+ } |
6198 |
+ } |
6199 |
+ } |
6200 |
+ else |
6201 |
+ { |
6202 |
+ st = fst; |
6203 |
+ } |
6204 |
+ } |
6205 |
+ } |
6206 |
+ } |
6207 |
+ |
6208 |
+ our_pred = insert_primary (entry); |
6209 |
+ our_pred->args.samefileid.ino = st.st_ino; |
6210 |
+ our_pred->args.samefileid.dev = st.st_dev; |
6211 |
+ our_pred->args.samefileid.fd = fd; |
6212 |
+ our_pred->need_type = false; |
6213 |
+ our_pred->need_stat = true; |
6214 |
+ our_pred->est_success_rate = 0.01f; |
6215 |
+ return true; |
6216 |
+} |
6217 |
+ |
6218 |
+#if 0 |
6219 |
+/* This function is commented out partly because support for it is |
6220 |
+ * uneven. |
6221 |
+ */ |
6222 |
+static boolean |
6223 |
+parse_show_control_chars (const struct parser_table* entry, |
6224 |
+ char **argv, |
6225 |
+ int *arg_ptr) |
6226 |
+{ |
6227 |
+ const char *arg; |
6228 |
+ const char *errmsg = _("The -show-control-chars option takes " |
6229 |
+ "a single argument which " |
6230 |
+ "must be 'literal' or 'safe'"); |
6231 |
+ |
6232 |
+ if ((argv == NULL) || (argv[*arg_ptr] == NULL)) |
6233 |
+ { |
6234 |
+ error (1, errno, "%s", errmsg); |
6235 |
+ return false; |
6236 |
+ } |
6237 |
+ else |
6238 |
+ { |
6239 |
+ arg = argv[*arg_ptr]; |
6240 |
+ |
6241 |
+ if (0 == strcmp("literal", arg)) |
6242 |
+ { |
6243 |
+ options.literal_control_chars = true; |
6244 |
+ } |
6245 |
+ else if (0 == strcmp("safe", arg)) |
6246 |
+ { |
6247 |
+ options.literal_control_chars = false; |
6248 |
+ } |
6249 |
+ else |
6250 |
+ { |
6251 |
+ error (1, errno, "%s", errmsg); |
6252 |
+ return false; |
6253 |
+ } |
6254 |
+ (*arg_ptr)++; /* consume the argument. */ |
6255 |
+ return true; |
6256 |
+ } |
6257 |
+} |
6258 |
+#endif |
6259 |
+ |
6260 |
+ |
6261 |
+static boolean |
6262 |
+parse_true (const struct parser_table* entry, char **argv, int *arg_ptr) |
6263 |
+{ |
6264 |
+ struct predicate *our_pred; |
6265 |
+ |
6266 |
+ (void) argv; |
6267 |
+ (void) arg_ptr; |
6268 |
+ |
6269 |
+ our_pred = insert_primary (entry); |
6270 |
+ our_pred->need_stat = our_pred->need_type = false; |
6271 |
+ our_pred->est_success_rate = 1.0f; |
6272 |
+ return true; |
6273 |
+} |
6274 |
+ |
6275 |
+static boolean |
6276 |
+parse_noop (const struct parser_table* entry, char **argv, int *arg_ptr) |
6277 |
+{ |
6278 |
+ (void) entry; |
6279 |
+ return parse_true(get_noop(), argv, arg_ptr); |
6280 |
+} |
6281 |
+ |
6282 |
+static boolean |
6283 |
+parse_accesscheck (const struct parser_table* entry, char **argv, int *arg_ptr) |
6284 |
+{ |
6285 |
+ struct predicate *our_pred; |
6286 |
+ (void) argv; |
6287 |
+ (void) arg_ptr; |
6288 |
+ our_pred = insert_primary (entry); |
6289 |
+ our_pred->need_stat = our_pred->need_type = false; |
6290 |
+ our_pred->side_effects = our_pred->no_default_print = false; |
6291 |
+ if (pred_is(our_pred, pred_executable)) |
6292 |
+ our_pred->est_success_rate = 0.2; |
6293 |
+ else |
6294 |
+ our_pred->est_success_rate = 0.9; |
6295 |
+ return true; |
6296 |
+} |
6297 |
+ |
6298 |
+static boolean |
6299 |
+parse_type (const struct parser_table* entry, char **argv, int *arg_ptr) |
6300 |
+{ |
6301 |
+ return insert_type (argv, arg_ptr, entry, pred_type); |
6302 |
+} |
6303 |
+ |
6304 |
+static boolean |
6305 |
+parse_uid (const struct parser_table* entry, char **argv, int *arg_ptr) |
6306 |
+{ |
6307 |
+ struct predicate *p = insert_num (argv, arg_ptr, entry); |
6308 |
+ if (p) |
6309 |
+ { |
6310 |
+ p->est_success_rate = (p->args.numinfo.l_val < 100) ? 0.99 : 0.2; |
6311 |
+ return true; |
6312 |
+ } |
6313 |
+ else |
6314 |
+ { |
6315 |
+ return false; |
6316 |
+ } |
6317 |
+} |
6318 |
+ |
6319 |
+static boolean |
6320 |
+parse_used (const struct parser_table* entry, char **argv, int *arg_ptr) |
6321 |
+{ |
6322 |
+ struct predicate *our_pred; |
6323 |
+ struct time_val tval; |
6324 |
+ const char *offset_str; |
6325 |
+ const char *errmsg = "arithmetic overflow while converting %s days to a number of seconds"; |
6326 |
+ |
6327 |
+ if (collect_arg(argv, arg_ptr, &offset_str)) |
6328 |
+ { |
6329 |
+ /* The timespec is actually a delta value, so we use an origin of 0. */ |
6330 |
+ if (get_relative_timestamp(offset_str, &tval, 0, DAYSECS, errmsg)) |
6331 |
+ { |
6332 |
+ our_pred = insert_primary (entry); |
6333 |
+ our_pred->args.reftime = tval; |
6334 |
+ our_pred->est_success_rate = estimate_file_age_success_rate(tval.ts.tv_sec / DAYSECS); |
6335 |
+ return true; |
6336 |
+ } |
6337 |
+ else |
6338 |
+ { |
6339 |
+ error(1, 0, _("Invalid argument %s to -used"), offset_str); |
6340 |
+ return false; |
6341 |
+ } |
6342 |
+ } |
6343 |
+ else |
6344 |
+ { |
6345 |
+ return false; /* missing argument */ |
6346 |
+ } |
6347 |
+} |
6348 |
+ |
6349 |
+static boolean |
6350 |
+parse_user (const struct parser_table* entry, char **argv, int *arg_ptr) |
6351 |
+{ |
6352 |
+ const char *username; |
6353 |
+ |
6354 |
+ if (collect_arg(argv, arg_ptr, &username)) |
6355 |
+ { |
6356 |
+ struct predicate *our_pred; |
6357 |
+ uid_t uid; |
6358 |
+ struct passwd *cur_pwd = getpwnam(username); |
6359 |
+ endpwent(); |
6360 |
+ if (cur_pwd != NULL) |
6361 |
+ { |
6362 |
+ uid = cur_pwd->pw_uid; |
6363 |
+ } |
6364 |
+ else |
6365 |
+ { |
6366 |
+ int uid_len = strspn (username, "0123456789"); |
6367 |
+ if (uid_len && (username[uid_len]==0)) |
6368 |
+ uid = safe_atoi (username); |
6369 |
+ else |
6370 |
+ return false; |
6371 |
+ } |
6372 |
+ our_pred = insert_primary (entry); |
6373 |
+ our_pred->args.uid = uid; |
6374 |
+ our_pred->est_success_rate = (our_pred->args.uid < 100) ? 0.99 : 0.2; |
6375 |
+ return true; |
6376 |
+ } |
6377 |
+ return false; |
6378 |
+} |
6379 |
+ |
6380 |
+static boolean |
6381 |
+parse_version (const struct parser_table* entry, char **argv, int *arg_ptr) |
6382 |
+{ |
6383 |
+ int features = 0; |
6384 |
+ int flags; |
6385 |
+ |
6386 |
+ (void) argv; |
6387 |
+ (void) arg_ptr; |
6388 |
+ (void) entry; |
6389 |
+ |
6390 |
+ display_findutils_version("find"); |
6391 |
+ printf (_("Features enabled: ")); |
6392 |
+ |
6393 |
+#if CACHE_IDS |
6394 |
+ printf("CACHE_IDS "); |
6395 |
+ ++features; |
6396 |
+#endif |
6397 |
+#if DEBUG |
6398 |
+ printf("DEBUG "); |
6399 |
+ ++features; |
6400 |
+#endif |
6401 |
+#if DEBUG_STAT |
6402 |
+ printf("DEBUG_STAT "); |
6403 |
+ ++features; |
6404 |
+#endif |
6405 |
+#if defined USE_STRUCT_DIRENT_D_TYPE && defined HAVE_STRUCT_DIRENT_D_TYPE |
6406 |
+ printf("D_TYPE "); |
6407 |
+ ++features; |
6408 |
+#endif |
6409 |
+#if defined O_NOFOLLOW |
6410 |
+ printf("O_NOFOLLOW(%s) ", |
6411 |
+ (options.open_nofollow_available ? "enabled" : "disabled")); |
6412 |
+ ++features; |
6413 |
+#endif |
6414 |
+#if defined LEAF_OPTIMISATION |
6415 |
+ printf("LEAF_OPTIMISATION "); |
6416 |
+ ++features; |
6417 |
+#endif |
6418 |
+ |
6419 |
+ flags = 0; |
6420 |
+ if (is_fts_enabled(&flags)) |
6421 |
+ { |
6422 |
+ int nflags = 0; |
6423 |
+ printf("FTS("); |
6424 |
+ ++features; |
6425 |
+ |
6426 |
+ if (flags & FTS_CWDFD) |
6427 |
+ { |
6428 |
+ if (nflags) |
6429 |
+ { |
6430 |
+ printf(","); |
6431 |
+ } |
6432 |
+ printf("FTS_CWDFD"); |
6433 |
+ ++nflags; |
6434 |
+ } |
6435 |
+ printf(") "); |
6436 |
+ } |
6437 |
+ |
6438 |
+ printf("CBO(level=%d) ", (int)(options.optimisation_level)); |
6439 |
+ ++features; |
6440 |
+ |
6441 |
+ if (0 == features) |
6442 |
+ { |
6443 |
+ /* For the moment, leave this as English in case someone wants |
6444 |
+ to parse these strings. */ |
6445 |
+ printf("none"); |
6446 |
+ } |
6447 |
+ printf("\n"); |
6448 |
+ |
6449 |
+ exit (0); |
6450 |
+} |
6451 |
+ |
6452 |
+static boolean |
6453 |
+parse_xdev (const struct parser_table* entry, char **argv, int *arg_ptr) |
6454 |
+{ |
6455 |
+ options.stay_on_filesystem = true; |
6456 |
+ return parse_noop(entry, argv, arg_ptr); |
6457 |
+} |
6458 |
+ |
6459 |
+static boolean |
6460 |
+parse_ignore_race (const struct parser_table* entry, char **argv, int *arg_ptr) |
6461 |
+{ |
6462 |
+ options.ignore_readdir_race = true; |
6463 |
+ return parse_noop(entry, argv, arg_ptr); |
6464 |
+} |
6465 |
+ |
6466 |
+static boolean |
6467 |
+parse_noignore_race (const struct parser_table* entry, char **argv, int *arg_ptr) |
6468 |
+{ |
6469 |
+ options.ignore_readdir_race = false; |
6470 |
+ return parse_noop(entry, argv, arg_ptr); |
6471 |
+} |
6472 |
+ |
6473 |
+static boolean |
6474 |
+parse_warn (const struct parser_table* entry, char **argv, int *arg_ptr) |
6475 |
+{ |
6476 |
+ options.warnings = true; |
6477 |
+ return parse_noop(entry, argv, arg_ptr); |
6478 |
+} |
6479 |
+ |
6480 |
+static boolean |
6481 |
+parse_xtype (const struct parser_table* entry, char **argv, int *arg_ptr) |
6482 |
+{ |
6483 |
+ return insert_type (argv, arg_ptr, entry, pred_xtype); |
6484 |
+} |
6485 |
+ |
6486 |
+static boolean |
6487 |
+insert_type (char **argv, int *arg_ptr, |
6488 |
+ const struct parser_table *entry, |
6489 |
+ PRED_FUNC which_pred) |
6490 |
+{ |
6491 |
+ mode_t type_cell; |
6492 |
+ struct predicate *our_pred; |
6493 |
+ float rate = 0.5; |
6494 |
+ const char *typeletter; |
6495 |
+ |
6496 |
+ if (collect_arg(argv, arg_ptr, &typeletter)) |
6497 |
+ { |
6498 |
+ if (strlen(typeletter) != 1u) |
6499 |
+ { |
6500 |
+ error(1, 0, _("Arguments to -type should contain only one letter")); |
6501 |
+ return false; |
6502 |
+ } |
6503 |
+ |
6504 |
+ switch (typeletter[0]) |
6505 |
+ { |
6506 |
+ case 'b': /* block special */ |
6507 |
+ type_cell = S_IFBLK; |
6508 |
+ rate = 0.01f; |
6509 |
+ break; |
6510 |
+ case 'c': /* character special */ |
6511 |
+ type_cell = S_IFCHR; |
6512 |
+ rate = 0.01f; |
6513 |
+ break; |
6514 |
+ case 'd': /* directory */ |
6515 |
+ type_cell = S_IFDIR; |
6516 |
+ rate = 0.4f; |
6517 |
+ break; |
6518 |
+ case 'f': /* regular file */ |
6519 |
+ type_cell = S_IFREG; |
6520 |
+ rate = 0.95f; |
6521 |
+ break; |
6522 |
+#ifdef S_IFLNK |
6523 |
+ case 'l': /* symbolic link */ |
6524 |
+ type_cell = S_IFLNK; |
6525 |
+ rate = 0.1f; |
6526 |
+ break; |
6527 |
+#endif |
6528 |
+#ifdef S_IFIFO |
6529 |
+ case 'p': /* pipe */ |
6530 |
+ type_cell = S_IFIFO; |
6531 |
+ rate = 0.01f; |
6532 |
+ break; |
6533 |
+#endif |
6534 |
+#ifdef S_IFSOCK |
6535 |
+ case 's': /* socket */ |
6536 |
+ type_cell = S_IFSOCK; |
6537 |
+ rate = 0.01f; |
6538 |
+ break; |
6539 |
+#endif |
6540 |
+#ifdef S_IFDOOR |
6541 |
+ case 'D': /* Solaris door */ |
6542 |
+ type_cell = S_IFDOOR; |
6543 |
+ rate = 0.01f; |
6544 |
+ break; |
6545 |
+#endif |
6546 |
+ default: /* None of the above ... nuke 'em. */ |
6547 |
+ error(1, 0, _("Unknown argument to -type: %c"), (*typeletter)); |
6548 |
+ return false; |
6549 |
+ } |
6550 |
+ our_pred = insert_primary_withpred (entry, which_pred); |
6551 |
+ our_pred->est_success_rate = rate; |
6552 |
+ |
6553 |
+ /* Figure out if we will need to stat the file, because if we don't |
6554 |
+ * need to follow symlinks, we can avoid a stat call by using |
6555 |
+ * struct dirent.d_type. |
6556 |
+ */ |
6557 |
+ if (which_pred == pred_xtype) |
6558 |
+ { |
6559 |
+ our_pred->need_stat = true; |
6560 |
+ our_pred->need_type = false; |
6561 |
+ } |
6562 |
+ else |
6563 |
+ { |
6564 |
+ our_pred->need_stat = false; /* struct dirent is enough */ |
6565 |
+ our_pred->need_type = true; |
6566 |
+ } |
6567 |
+ our_pred->args.type = type_cell; |
6568 |
+ return true; |
6569 |
+ } |
6570 |
+ return false; |
6571 |
+} |
6572 |
+ |
6573 |
+ |
6574 |
+/* Return true if the file accessed via FP is a terminal. |
6575 |
+ */ |
6576 |
+static boolean |
6577 |
+stream_is_tty(FILE *fp) |
6578 |
+{ |
6579 |
+ int fd = fileno(fp); |
6580 |
+ if (-1 == fd) |
6581 |
+ { |
6582 |
+ return false; /* not a valid stream */ |
6583 |
+ } |
6584 |
+ else |
6585 |
+ { |
6586 |
+ return isatty(fd) ? true : false; |
6587 |
+ } |
6588 |
+ |
6589 |
+} |
6590 |
+ |
6591 |
+ |
6592 |
+ |
6593 |
+ |
6594 |
+/* XXX: do we need to pass FUNC to this function? */ |
6595 |
+static boolean |
6596 |
+insert_fprintf (struct format_val *vec, |
6597 |
+ const struct parser_table *entry, PRED_FUNC func, |
6598 |
+ const char *format_const) |
6599 |
+{ |
6600 |
+ char *format = (char*)format_const; /* XXX: casting away constness */ |
6601 |
+ register char *scan; /* Current address in scanning `format'. */ |
6602 |
+ register char *scan2; /* Address inside of element being scanned. */ |
6603 |
+ struct segment **segmentp; /* Address of current segment. */ |
6604 |
+ struct predicate *our_pred; |
6605 |
+ |
6606 |
+ our_pred = insert_primary_withpred (entry, func); |
6607 |
+ our_pred->side_effects = our_pred->no_default_print = true; |
6608 |
+ our_pred->args.printf_vec = *vec; |
6609 |
+ our_pred->need_type = false; |
6610 |
+ our_pred->need_stat = false; |
6611 |
+ our_pred->p_cost = NeedsNothing; |
6612 |
+ |
6613 |
+ segmentp = &our_pred->args.printf_vec.segment; |
6614 |
+ *segmentp = NULL; |
6615 |
+ |
6616 |
+ for (scan = format; *scan; scan++) |
6617 |
+ { |
6618 |
+ if (*scan == '\\') |
6619 |
+ { |
6620 |
+ scan2 = scan + 1; |
6621 |
+ if (*scan2 >= '0' && *scan2 <= '7') |
6622 |
+ { |
6623 |
+ register int n, i; |
6624 |
+ |
6625 |
+ for (i = n = 0; i < 3 && (*scan2 >= '0' && *scan2 <= '7'); |
6626 |
+ i++, scan2++) |
6627 |
+ n = 8 * n + *scan2 - '0'; |
6628 |
+ scan2--; |
6629 |
+ *scan = n; |
6630 |
+ } |
6631 |
+ else |
6632 |
+ { |
6633 |
+ switch (*scan2) |
6634 |
+ { |
6635 |
+ case 'a': |
6636 |
+ *scan = 7; |
6637 |
+ break; |
6638 |
+ case 'b': |
6639 |
+ *scan = '\b'; |
6640 |
+ break; |
6641 |
+ case 'c': |
6642 |
+ make_segment (segmentp, format, scan - format, |
6643 |
+ KIND_STOP, 0, 0, |
6644 |
+ our_pred); |
6645 |
+ if (our_pred->need_stat && (our_pred->p_cost < NeedsStatInfo)) |
6646 |
+ our_pred->p_cost = NeedsStatInfo; |
6647 |
+ return true; |
6648 |
+ case 'f': |
6649 |
+ *scan = '\f'; |
6650 |
+ break; |
6651 |
+ case 'n': |
6652 |
+ *scan = '\n'; |
6653 |
+ break; |
6654 |
+ case 'r': |
6655 |
+ *scan = '\r'; |
6656 |
+ break; |
6657 |
+ case 't': |
6658 |
+ *scan = '\t'; |
6659 |
+ break; |
6660 |
+ case 'v': |
6661 |
+ *scan = '\v'; |
6662 |
+ break; |
6663 |
+ case '\\': |
6664 |
+ /* *scan = '\\'; * it already is */ |
6665 |
+ break; |
6666 |
+ default: |
6667 |
+ error (0, 0, |
6668 |
+ _("warning: unrecognized escape `\\%c'"), *scan2); |
6669 |
+ scan++; |
6670 |
+ continue; |
6671 |
+ } |
6672 |
+ } |
6673 |
+ segmentp = make_segment (segmentp, format, scan - format + 1, |
6674 |
+ KIND_PLAIN, 0, 0, |
6675 |
+ our_pred); |
6676 |
+ format = scan2 + 1; /* Move past the escape. */ |
6677 |
+ scan = scan2; /* Incremented immediately by `for'. */ |
6678 |
+ } |
6679 |
+ else if (*scan == '%') |
6680 |
+ { |
6681 |
+ if (scan[1] == 0) |
6682 |
+ { |
6683 |
+ /* Trailing %. We don't like those. */ |
6684 |
+ error (1, 0, _("error: %s at end of format string"), scan); |
6685 |
+ } |
6686 |
+ else if (scan[1] == '%') |
6687 |
+ { |
6688 |
+ segmentp = make_segment (segmentp, format, scan - format + 1, |
6689 |
+ KIND_PLAIN, 0, 0, |
6690 |
+ our_pred); |
6691 |
+ scan++; |
6692 |
+ format = scan + 1; |
6693 |
+ continue; |
6694 |
+ } |
6695 |
+ /* Scan past flags, width and precision, to verify kind. */ |
6696 |
+ for (scan2 = scan; *++scan2 && strchr ("-+ #", *scan2);) |
6697 |
+ /* Do nothing. */ ; |
6698 |
+ while (ISDIGIT (*scan2)) |
6699 |
+ scan2++; |
6700 |
+ if (*scan2 == '.') |
6701 |
+ for (scan2++; ISDIGIT (*scan2); scan2++) |
6702 |
+ /* Do nothing. */ ; |
6703 |
+ if (strchr ("abcdDfFgGhHiklmMnpPsStuUyY", *scan2)) |
6704 |
+ { |
6705 |
+ segmentp = make_segment (segmentp, format, scan2 - format, |
6706 |
+ KIND_FORMAT, *scan2, 0, |
6707 |
+ our_pred); |
6708 |
+ scan = scan2; |
6709 |
+ format = scan + 1; |
6710 |
+ } |
6711 |
+ else if (strchr ("ABCT", *scan2) && scan2[1]) |
6712 |
+ { |
6713 |
+ segmentp = make_segment (segmentp, format, scan2 - format, |
6714 |
+ KIND_FORMAT, scan2[0], scan2[1], |
6715 |
+ our_pred); |
6716 |
+ scan = scan2 + 1; |
6717 |
+ format = scan + 1; |
6718 |
+ continue; |
6719 |
+ } |
6720 |
+ else |
6721 |
+ { |
6722 |
+ /* An unrecognized % escape. Print the char after the %. */ |
6723 |
+ error (0, 0, _("warning: unrecognized format directive `%%%c'"), |
6724 |
+ *scan2); |
6725 |
+ segmentp = make_segment (segmentp, format, scan - format, |
6726 |
+ KIND_PLAIN, 0, 0, |
6727 |
+ our_pred); |
6728 |
+ format = scan + 1; |
6729 |
+ continue; |
6730 |
+ } |
6731 |
+ } |
6732 |
+ } |
6733 |
+ |
6734 |
+ if (scan > format) |
6735 |
+ make_segment (segmentp, format, scan - format, KIND_PLAIN, 0, 0, |
6736 |
+ our_pred); |
6737 |
+ return true; |
6738 |
+} |
6739 |
+ |
6740 |
+/* Create a new fprintf segment in *SEGMENT, with type KIND, |
6741 |
+ from the text in FORMAT, which has length LEN. |
6742 |
+ Return the address of the `next' pointer of the new segment. */ |
6743 |
+ |
6744 |
+static struct segment ** |
6745 |
+make_segment (struct segment **segment, |
6746 |
+ char *format, |
6747 |
+ int len, |
6748 |
+ int kind, |
6749 |
+ char format_char, |
6750 |
+ char aux_format_char, |
6751 |
+ struct predicate *pred) |
6752 |
+{ |
6753 |
+ enum EvaluationCost mycost = NeedsNothing; |
6754 |
+ char *fmt; |
6755 |
+ |
6756 |
+ *segment = xmalloc (sizeof (struct segment)); |
6757 |
+ |
6758 |
+ (*segment)->segkind = kind; |
6759 |
+ (*segment)->format_char[0] = format_char; |
6760 |
+ (*segment)->format_char[1] = aux_format_char; |
6761 |
+ (*segment)->next = NULL; |
6762 |
+ (*segment)->text_len = len; |
6763 |
+ |
6764 |
+ fmt = (*segment)->text = xmalloc (len + sizeof "d"); |
6765 |
+ strncpy (fmt, format, len); |
6766 |
+ fmt += len; |
6767 |
+ |
6768 |
+ switch (kind) |
6769 |
+ { |
6770 |
+ case KIND_PLAIN: /* Plain text string, no % conversion. */ |
6771 |
+ case KIND_STOP: /* Terminate argument, no newline. */ |
6772 |
+ assert (0 == format_char); |
6773 |
+ assert (0 == aux_format_char); |
6774 |
+ *fmt = '\0'; |
6775 |
+ if (mycost > pred->p_cost) |
6776 |
+ pred->p_cost = NeedsNothing; |
6777 |
+ return &(*segment)->next; |
6778 |
+ break; |
6779 |
+ } |
6780 |
+ |
6781 |
+ assert (kind == KIND_FORMAT); |
6782 |
+ switch (format_char) |
6783 |
+ { |
6784 |
+ case 'l': /* object of symlink */ |
6785 |
+ pred->need_stat = true; |
6786 |
+ mycost = NeedsLinkName; |
6787 |
+ *fmt++ = 's'; |
6788 |
+ break; |
6789 |
+ |
6790 |
+ case 'y': /* file type */ |
6791 |
+ pred->need_type = true; |
6792 |
+ mycost = NeedsType; |
6793 |
+ *fmt++ = 's'; |
6794 |
+ break; |
6795 |
+ |
6796 |
+ case 'a': /* atime in `ctime' format */ |
6797 |
+ case 'A': /* atime in user-specified strftime format */ |
6798 |
+ case 'B': /* birth time in user-specified strftime format */ |
6799 |
+ case 'c': /* ctime in `ctime' format */ |
6800 |
+ case 'C': /* ctime in user-specified strftime format */ |
6801 |
+ case 'F': /* file system type */ |
6802 |
+ case 'g': /* group name */ |
6803 |
+ case 'i': /* inode number */ |
6804 |
+ case 'M': /* mode in `ls -l' format (eg., "drwxr-xr-x") */ |
6805 |
+ case 's': /* size in bytes */ |
6806 |
+ case 't': /* mtime in `ctime' format */ |
6807 |
+ case 'T': /* mtime in user-specified strftime format */ |
6808 |
+ case 'u': /* user name */ |
6809 |
+ pred->need_stat = true; |
6810 |
+ mycost = NeedsStatInfo; |
6811 |
+ *fmt++ = 's'; |
6812 |
+ break; |
6813 |
+ |
6814 |
+ case 'S': /* sparseness */ |
6815 |
+ pred->need_stat = true; |
6816 |
+ mycost = NeedsStatInfo; |
6817 |
+ *fmt++ = 'g'; |
6818 |
+ break; |
6819 |
+ |
6820 |
+ case 'Y': /* symlink pointed file type */ |
6821 |
+ pred->need_stat = true; |
6822 |
+ mycost = NeedsType; /* true for amortised effect */ |
6823 |
+ *fmt++ = 's'; |
6824 |
+ break; |
6825 |
+ |
6826 |
+ case 'f': /* basename of path */ |
6827 |
+ case 'h': /* leading directories part of path */ |
6828 |
+ case 'p': /* pathname */ |
6829 |
+ case 'P': /* pathname with ARGV element stripped */ |
6830 |
+ *fmt++ = 's'; |
6831 |
+ break; |
6832 |
+ |
6833 |
+ case 'H': /* ARGV element file was found under */ |
6834 |
+ *fmt++ = 's'; |
6835 |
+ break; |
6836 |
+ |
6837 |
+ /* Numeric items that one might expect to honour |
6838 |
+ * #, 0, + flags but which do not. |
6839 |
+ */ |
6840 |
+ case 'G': /* GID number */ |
6841 |
+ case 'U': /* UID number */ |
6842 |
+ case 'b': /* size in 512-byte blocks (NOT birthtime in ctime fmt)*/ |
6843 |
+ case 'D': /* Filesystem device on which the file exits */ |
6844 |
+ case 'k': /* size in 1K blocks */ |
6845 |
+ case 'n': /* number of links */ |
6846 |
+ pred->need_stat = true; |
6847 |
+ mycost = NeedsStatInfo; |
6848 |
+ *fmt++ = 's'; |
6849 |
+ break; |
6850 |
+ |
6851 |
+ /* Numeric items that DO honour #, 0, + flags. |
6852 |
+ */ |
6853 |
+ case 'd': /* depth in search tree (0 = ARGV element) */ |
6854 |
+ *fmt++ = 'd'; |
6855 |
+ break; |
6856 |
+ |
6857 |
+ case 'm': /* mode as octal number (perms only) */ |
6858 |
+ *fmt++ = 'o'; |
6859 |
+ pred->need_stat = true; |
6860 |
+ mycost = NeedsStatInfo; |
6861 |
+ break; |
6862 |
+ |
6863 |
+ case '{': |
6864 |
+ case '[': |
6865 |
+ case '(': |
6866 |
+ error (1, 0, |
6867 |
+ _("error: the format directive `%%%c' is reserved for future use"), |
6868 |
+ (int)kind); |
6869 |
+ /*NOTREACHED*/ |
6870 |
+ break; |
6871 |
+ } |
6872 |
+ *fmt = '\0'; |
6873 |
+ |
6874 |
+ if (mycost > pred->p_cost) |
6875 |
+ pred->p_cost = mycost; |
6876 |
+ return &(*segment)->next; |
6877 |
+} |
6878 |
+ |
6879 |
+static void |
6880 |
+check_path_safety(const char *action, char **argv) |
6881 |
+{ |
6882 |
+ char *s; |
6883 |
+ const char *path = getenv("PATH"); |
6884 |
+ if (NULL == path) |
6885 |
+ { |
6886 |
+ /* $PATH is not set. Assume the OS default is safe. |
6887 |
+ * That may not be true on Windows, but I'm not aware |
6888 |
+ * of a way to get Windows to avoid searching the |
6889 |
+ * current directory anyway. |
6890 |
+ */ |
6891 |
+ return; |
6892 |
+ } |
6893 |
+ |
6894 |
+ (void)argv; |
6895 |
+ |
6896 |
+ s = next_element(path, 1); |
6897 |
+ while ((s = next_element ((char *) NULL, 1)) != NULL) |
6898 |
+ { |
6899 |
+ if (0 == strcmp(s, ".")) |
6900 |
+ { |
6901 |
+ error(1, 0, _("The current directory is included in the PATH " |
6902 |
+ "environment variable, which is insecure in " |
6903 |
+ "combination with the %s action of find. " |
6904 |
+ "Please remove the current directory from your " |
6905 |
+ "$PATH (that is, remove \".\" or leading or trailing " |
6906 |
+ "colons)"), |
6907 |
+ action); |
6908 |
+ } |
6909 |
+ else if ('/' != s[0]) |
6910 |
+ { |
6911 |
+ /* Relative paths are also dangerous in $PATH. */ |
6912 |
+ error(1, 0, _("The relative path %1$s is included in the PATH " |
6913 |
+ "environment variable, which is insecure in " |
6914 |
+ "combination with the %2$s action of find. " |
6915 |
+ "Please remove that entry from $PATH"), |
6916 |
+ safely_quote_err_filename(0, s), |
6917 |
+ action); |
6918 |
+ } |
6919 |
+ } |
6920 |
+} |
6921 |
+ |
6922 |
+ |
6923 |
+/* handles both exec and ok predicate */ |
6924 |
+static boolean |
6925 |
+new_insert_exec_ok (const char *action, |
6926 |
+ const struct parser_table *entry, |
6927 |
+ int dirfd, |
6928 |
+ char **argv, |
6929 |
+ int *arg_ptr) |
6930 |
+{ |
6931 |
+ int start, end; /* Indexes in ARGV of start & end of cmd. */ |
6932 |
+ int i; /* Index into cmd args */ |
6933 |
+ int saw_braces; /* True if previous arg was '{}'. */ |
6934 |
+ boolean allow_plus; /* True if + is a valid terminator */ |
6935 |
+ int brace_count; /* Number of instances of {}. */ |
6936 |
+ PRED_FUNC func = entry->pred_func; |
6937 |
+ enum BC_INIT_STATUS bcstatus; |
6938 |
+ |
6939 |
+ struct predicate *our_pred; |
6940 |
+ struct exec_val *execp; /* Pointer for efficiency. */ |
6941 |
+ |
6942 |
+ if ((argv == NULL) || (argv[*arg_ptr] == NULL)) |
6943 |
+ return false; |
6944 |
+ |
6945 |
+ our_pred = insert_primary_withpred (entry, func); |
6946 |
+ our_pred->side_effects = our_pred->no_default_print = true; |
6947 |
+ our_pred->need_type = our_pred->need_stat = false; |
6948 |
+ |
6949 |
+ execp = &our_pred->args.exec_vec; |
6950 |
+ |
6951 |
+ if ((func != pred_okdir) && (func != pred_ok)) |
6952 |
+ { |
6953 |
+ allow_plus = true; |
6954 |
+ execp->close_stdin = false; |
6955 |
+ } |
6956 |
+ else |
6957 |
+ { |
6958 |
+ allow_plus = false; |
6959 |
+ /* If find reads stdin (i.e. for -ok and similar), close stdin |
6960 |
+ * in the child to prevent some script from consiming the output |
6961 |
+ * intended for find. |
6962 |
+ */ |
6963 |
+ execp->close_stdin = true; |
6964 |
+ } |
6965 |
+ |
6966 |
+ |
6967 |
+ if ((func == pred_execdir) || (func == pred_okdir)) |
6968 |
+ { |
6969 |
+ options.ignore_readdir_race = false; |
6970 |
+ check_path_safety(action, argv); |
6971 |
+ execp->use_current_dir = true; |
6972 |
+ } |
6973 |
+ else |
6974 |
+ { |
6975 |
+ execp->use_current_dir = false; |
6976 |
+ } |
6977 |
+ |
6978 |
+ our_pred->args.exec_vec.multiple = 0; |
6979 |
+ |
6980 |
+ /* Count the number of args with path replacements, up until the ';'. |
6981 |
+ * Also figure out if the command is terminated by ";" or by "+". |
6982 |
+ */ |
6983 |
+ start = *arg_ptr; |
6984 |
+ for (end = start, saw_braces=0, brace_count=0; |
6985 |
+ (argv[end] != NULL) |
6986 |
+ && ((argv[end][0] != ';') || (argv[end][1] != '\0')); |
6987 |
+ end++) |
6988 |
+ { |
6989 |
+ /* For -exec and -execdir, "{} +" can terminate the command. */ |
6990 |
+ if ( allow_plus |
6991 |
+ && argv[end][0] == '+' && argv[end][1] == 0 |
6992 |
+ && saw_braces) |
6993 |
+ { |
6994 |
+ our_pred->args.exec_vec.multiple = 1; |
6995 |
+ break; |
6996 |
+ } |
6997 |
+ |
6998 |
+ saw_braces = 0; |
6999 |
+ if (mbsstr (argv[end], "{}")) |
7000 |
+ { |
7001 |
+ saw_braces = 1; |
7002 |
+ ++brace_count; |
7003 |
+ |
7004 |
+ if (0 == end && (func == pred_execdir || func == pred_okdir)) |
7005 |
+ { |
7006 |
+ /* The POSIX standard says that {} replacement should |
7007 |
+ * occur even in the utility name. This is insecure |
7008 |
+ * since it means we will be executing a command whose |
7009 |
+ * name is chosen according to whatever find finds in |
7010 |
+ * the file system. That can be influenced by an |
7011 |
+ * attacker. Hence for -execdir and -okdir this is not |
7012 |
+ * allowed. We can specify this as those options are |
7013 |
+ * not defined by POSIX. |
7014 |
+ */ |
7015 |
+ error(1, 0, _("You may not use {} within the utility name for " |
7016 |
+ "-execdir and -okdir, because this is a potential " |
7017 |
+ "security problem.")); |
7018 |
+ } |
7019 |
+ } |
7020 |
+ } |
7021 |
+ |
7022 |
+ /* Fail if no command given or no semicolon found. */ |
7023 |
+ if ((end == start) || (argv[end] == NULL)) |
7024 |
+ { |
7025 |
+ *arg_ptr = end; |
7026 |
+ free(our_pred); |
7027 |
+ return false; |
7028 |
+ } |
7029 |
+ |
7030 |
+ if (our_pred->args.exec_vec.multiple && brace_count > 1) |
7031 |
+ { |
7032 |
+ |
7033 |
+ const char *suffix; |
7034 |
+ if (func == pred_execdir) |
7035 |
+ suffix = "dir"; |
7036 |
+ else |
7037 |
+ suffix = ""; |
7038 |
+ |
7039 |
+ error(1, 0, |
7040 |
+ _("Only one instance of {} is supported with -exec%s ... +"), |
7041 |
+ suffix); |
7042 |
+ } |
7043 |
+ |
7044 |
+ /* We use a switch statement here so that the compiler warns us when |
7045 |
+ * we forget to handle a newly invented enum value. |
7046 |
+ * |
7047 |
+ * Like xargs, we allow 2KiB of headroom for the launched utility to |
7048 |
+ * export its own environment variables before calling something |
7049 |
+ * else. |
7050 |
+ */ |
7051 |
+ bcstatus = bc_init_controlinfo(&execp->ctl, 2048u); |
7052 |
+ switch (bcstatus) |
7053 |
+ { |
7054 |
+ case BC_INIT_ENV_TOO_BIG: |
7055 |
+ case BC_INIT_CANNOT_ACCOMODATE_HEADROOM: |
7056 |
+ error(1, 0, |
7057 |
+ _("The environment is too large for exec().")); |
7058 |
+ break; |
7059 |
+ case BC_INIT_OK: |
7060 |
+ /* Good news. Carry on. */ |
7061 |
+ break; |
7062 |
+ } |
7063 |
+ bc_use_sensible_arg_max(&execp->ctl); |
7064 |
+ |
7065 |
+ |
7066 |
+ execp->ctl.exec_callback = launch; |
7067 |
+ |
7068 |
+ if (our_pred->args.exec_vec.multiple) |
7069 |
+ { |
7070 |
+ /* "+" terminator, so we can just append our arguments after the |
7071 |
+ * command and initial arguments. |
7072 |
+ */ |
7073 |
+ execp->replace_vec = NULL; |
7074 |
+ execp->ctl.replace_pat = NULL; |
7075 |
+ execp->ctl.rplen = 0; |
7076 |
+ execp->ctl.lines_per_exec = 0; /* no limit */ |
7077 |
+ execp->ctl.args_per_exec = 0; /* no limit */ |
7078 |
+ |
7079 |
+ /* remember how many arguments there are */ |
7080 |
+ execp->ctl.initial_argc = (end-start) - 1; |
7081 |
+ |
7082 |
+ /* execp->state = xmalloc(sizeof struct buildcmd_state); */ |
7083 |
+ bc_init_state(&execp->ctl, &execp->state, execp); |
7084 |
+ |
7085 |
+ /* Gather the initial arguments. Skip the {}. */ |
7086 |
+ for (i=start; i<end-1; ++i) |
7087 |
+ { |
7088 |
+ bc_push_arg(&execp->ctl, &execp->state, |
7089 |
+ argv[i], strlen(argv[i])+1, |
7090 |
+ NULL, 0, |
7091 |
+ 1); |
7092 |
+ } |
7093 |
+ } |
7094 |
+ else |
7095 |
+ { |
7096 |
+ /* Semicolon terminator - more than one {} is supported, so we |
7097 |
+ * have to do brace-replacement. |
7098 |
+ */ |
7099 |
+ execp->num_args = end - start; |
7100 |
+ |
7101 |
+ execp->ctl.replace_pat = "{}"; |
7102 |
+ execp->ctl.rplen = strlen(execp->ctl.replace_pat); |
7103 |
+ execp->ctl.lines_per_exec = 0; /* no limit */ |
7104 |
+ execp->ctl.args_per_exec = 0; /* no limit */ |
7105 |
+ execp->replace_vec = xmalloc(sizeof(char*)*execp->num_args); |
7106 |
+ |
7107 |
+ |
7108 |
+ /* execp->state = xmalloc(sizeof(*(execp->state))); */ |
7109 |
+ bc_init_state(&execp->ctl, &execp->state, execp); |
7110 |
+ |
7111 |
+ /* Remember the (pre-replacement) arguments for later. */ |
7112 |
+ for (i=0; i<execp->num_args; ++i) |
7113 |
+ { |
7114 |
+ execp->replace_vec[i] = argv[i+start]; |
7115 |
+ } |
7116 |
+ } |
7117 |
+ |
7118 |
+ if (argv[end] == NULL) |
7119 |
+ *arg_ptr = end; |
7120 |
+ else |
7121 |
+ *arg_ptr = end + 1; |
7122 |
+ |
7123 |
+ return true; |
7124 |
+} |
7125 |
+ |
7126 |
+ |
7127 |
+ |
7128 |
+static boolean |
7129 |
+insert_exec_ok (const char *action, |
7130 |
+ const struct parser_table *entry, |
7131 |
+ int dirfd, |
7132 |
+ char **argv, |
7133 |
+ int *arg_ptr) |
7134 |
+{ |
7135 |
+ return new_insert_exec_ok(action, entry, dirfd, argv, arg_ptr); |
7136 |
+} |
7137 |
+ |
7138 |
+ |
7139 |
+ |
7140 |
+/* Get a timestamp and comparison type. |
7141 |
+ |
7142 |
+ STR is the ASCII representation. |
7143 |
+ Set *NUM_DAYS to the number of days/minutes/whatever, taken as being |
7144 |
+ relative to ORIGIN (usually the current moment or midnight). |
7145 |
+ Thus the sense of the comparison type appears to be reversed. |
7146 |
+ Set *COMP_TYPE to the kind of comparison that is requested. |
7147 |
+ Issue OVERFLOWMESSAGE if overflow occurs. |
7148 |
+ Return true if all okay, false if input error. |
7149 |
+ |
7150 |
+ Used by -atime, -ctime and -mtime (parsers) to |
7151 |
+ get the appropriate information for a time predicate processor. */ |
7152 |
+ |
7153 |
+static boolean |
7154 |
+get_relative_timestamp (const char *str, |
7155 |
+ struct time_val *result, |
7156 |
+ time_t origin, |
7157 |
+ double sec_per_unit, |
7158 |
+ const char *overflowmessage) |
7159 |
+{ |
7160 |
+ uintmax_t checkval; |
7161 |
+ double offset, seconds, f; |
7162 |
+ |
7163 |
+ if (get_comp_type(&str, &result->kind)) |
7164 |
+ { |
7165 |
+ /* Invert the sense of the comparison */ |
7166 |
+ switch (result->kind) |
7167 |
+ { |
7168 |
+ case COMP_LT: result->kind = COMP_GT; break; |
7169 |
+ case COMP_GT: result->kind = COMP_LT; break; |
7170 |
+ default: break; |
7171 |
+ } |
7172 |
+ |
7173 |
+ /* Convert the ASCII number into floating-point. */ |
7174 |
+ if (xstrtod(str, NULL, &offset, strtod)) |
7175 |
+ { |
7176 |
+ /* Separate the floating point number the user specified |
7177 |
+ * (which is a number of days, or minutes, etc) into an |
7178 |
+ * integral number of seconds (SECONDS) and a fraction (F). |
7179 |
+ */ |
7180 |
+ f = modf(offset * sec_per_unit, &seconds); |
7181 |
+ |
7182 |
+ result->ts.tv_sec = origin - seconds; |
7183 |
+ result->ts.tv_nsec = fabs(f * 1e9); |
7184 |
+ |
7185 |
+ /* Check for overflow. */ |
7186 |
+ checkval = (uintmax_t)origin - seconds; |
7187 |
+ if (checkval != result->ts.tv_sec) |
7188 |
+ { |
7189 |
+ /* an overflow has occurred. */ |
7190 |
+ error (1, 0, overflowmessage, str); |
7191 |
+ } |
7192 |
+ return true; |
7193 |
+ } |
7194 |
+ else |
7195 |
+ { |
7196 |
+ /* Conversion from ASCII to double failed. */ |
7197 |
+ return false; |
7198 |
+ } |
7199 |
+ } |
7200 |
+ else |
7201 |
+ { |
7202 |
+ return false; |
7203 |
+ } |
7204 |
+} |
7205 |
+ |
7206 |
+/* Insert a time predicate based on the information in ENTRY. |
7207 |
+ ARGV is a pointer to the argument array. |
7208 |
+ ARG_PTR is a pointer to an index into the array, incremented if |
7209 |
+ all went well. |
7210 |
+ |
7211 |
+ Return true if input is valid, false if not. |
7212 |
+ |
7213 |
+ A new predicate node is assigned, along with an argument node |
7214 |
+ obtained with malloc. |
7215 |
+ |
7216 |
+ Used by -atime, -ctime, and -mtime parsers. */ |
7217 |
+ |
7218 |
+static boolean |
7219 |
+parse_time (const struct parser_table* entry, char *argv[], int *arg_ptr) |
7220 |
+{ |
7221 |
+ struct predicate *our_pred; |
7222 |
+ struct time_val tval; |
7223 |
+ enum comparison_type comp; |
7224 |
+ const char *timearg, *orig_timearg; |
7225 |
+ const char *errmsg = "arithmetic overflow while converting %s " |
7226 |
+ "days to a number of seconds"; |
7227 |
+ time_t origin; |
7228 |
+ |
7229 |
+ if (!collect_arg(argv, arg_ptr, &timearg)) |
7230 |
+ return false; |
7231 |
+ orig_timearg = timearg; |
7232 |
+ |
7233 |
+ /* Decide the origin by previewing the comparison type. */ |
7234 |
+ origin = options.cur_day_start; |
7235 |
+ |
7236 |
+ if (get_comp_type(&timearg, &comp)) |
7237 |
+ { |
7238 |
+ /* Remember, we invert the sense of the comparison, so this tests |
7239 |
+ * against COMP_LT instead of COMP_GT... |
7240 |
+ */ |
7241 |
+ if (COMP_LT == comp) |
7242 |
+ { |
7243 |
+ uintmax_t expected = origin + (DAYSECS-1); |
7244 |
+ origin += (DAYSECS-1); |
7245 |
+ if (origin != expected) |
7246 |
+ { |
7247 |
+ error(1, 0, |
7248 |
+ _("arithmetic overflow when trying to calculate the end of today")); |
7249 |
+ } |
7250 |
+ } |
7251 |
+ } |
7252 |
+ /* We discard the value of comp here, as get_relative_timestamp |
7253 |
+ * will set tval.kind. For that to work, we have to restore |
7254 |
+ * timearg so that it points to the +/- prefix, if any. get_comp_type() |
7255 |
+ * will have advanced timearg, so we restore it. |
7256 |
+ */ |
7257 |
+ timearg = orig_timearg; |
7258 |
+ |
7259 |
+ if (!get_relative_timestamp(timearg, &tval, origin, DAYSECS, errmsg)) |
7260 |
+ return false; |
7261 |
+ |
7262 |
+ our_pred = insert_primary (entry); |
7263 |
+ our_pred->args.reftime = tval; |
7264 |
+ our_pred->est_success_rate = estimate_timestamp_success_rate(tval.ts.tv_sec); |
7265 |
+ |
7266 |
+ if (options.debug_options & DebugExpressionTree) |
7267 |
+ { |
7268 |
+ time_t t; |
7269 |
+ |
7270 |
+ fprintf (stderr, "inserting %s\n", our_pred->p_name); |
7271 |
+ fprintf (stderr, " type: %s %s ", |
7272 |
+ (tval.kind == COMP_GT) ? "gt" : |
7273 |
+ ((tval.kind == COMP_LT) ? "lt" : ((tval.kind == COMP_EQ) ? "eq" : "?")), |
7274 |
+ (tval.kind == COMP_GT) ? " >" : |
7275 |
+ ((tval.kind == COMP_LT) ? " <" : ((tval.kind == COMP_EQ) ? ">=" : " ?"))); |
7276 |
+ t = our_pred->args.reftime.ts.tv_sec; |
7277 |
+ fprintf (stderr, "%ju %s", |
7278 |
+ (uintmax_t) our_pred->args.reftime.ts.tv_sec, |
7279 |
+ ctime (&t)); |
7280 |
+ if (tval.kind == COMP_EQ) |
7281 |
+ { |
7282 |
+ t = our_pred->args.reftime.ts.tv_sec + DAYSECS; |
7283 |
+ fprintf (stderr, " < %ju %s", |
7284 |
+ (uintmax_t) t, ctime (&t)); |
7285 |
+ } |
7286 |
+ } |
7287 |
+ |
7288 |
+ return true; |
7289 |
+} |
7290 |
+ |
7291 |
+/* Get the comparison type prefix (if any) from a number argument. |
7292 |
+ The prefix is at *STR. |
7293 |
+ Set *COMP_TYPE to the kind of comparison that is requested. |
7294 |
+ Advance *STR beyond any initial comparison prefix. |
7295 |
+ |
7296 |
+ Return true if all okay, false if input error. */ |
7297 |
+static boolean |
7298 |
+get_comp_type(const char **str, enum comparison_type *comp_type) |
7299 |
+{ |
7300 |
+ switch (**str) |
7301 |
+ { |
7302 |
+ case '+': |
7303 |
+ *comp_type = COMP_GT; |
7304 |
+ (*str)++; |
7305 |
+ break; |
7306 |
+ case '-': |
7307 |
+ *comp_type = COMP_LT; |
7308 |
+ (*str)++; |
7309 |
+ break; |
7310 |
+ default: |
7311 |
+ *comp_type = COMP_EQ; |
7312 |
+ break; |
7313 |
+ } |
7314 |
+ return true; |
7315 |
+} |
7316 |
+ |
7317 |
+ |
7318 |
+ |
7319 |
+ |
7320 |
+ |
7321 |
+/* Get a number with comparison information. |
7322 |
+ The sense of the comparison information is 'normal'; that is, |
7323 |
+ '+' looks for a count > than the number and '-' less than. |
7324 |
+ |
7325 |
+ STR is the ASCII representation of the number. |
7326 |
+ Set *NUM to the number. |
7327 |
+ Set *COMP_TYPE to the kind of comparison that is requested. |
7328 |
+ |
7329 |
+ Return true if all okay, false if input error. */ |
7330 |
+ |
7331 |
+static boolean |
7332 |
+get_num (const char *str, |
7333 |
+ uintmax_t *num, |
7334 |
+ enum comparison_type *comp_type) |
7335 |
+{ |
7336 |
+ char *pend; |
7337 |
+ |
7338 |
+ if (str == NULL) |
7339 |
+ return false; |
7340 |
+ |
7341 |
+ /* Figure out the comparison type if the caller accepts one. */ |
7342 |
+ if (comp_type) |
7343 |
+ { |
7344 |
+ if (!get_comp_type(&str, comp_type)) |
7345 |
+ return false; |
7346 |
+ } |
7347 |
+ |
7348 |
+ return xstrtoumax (str, &pend, 10, num, "") == LONGINT_OK; |
7349 |
+} |
7350 |
+ |
7351 |
+/* Insert a number predicate. |
7352 |
+ ARGV is a pointer to the argument array. |
7353 |
+ *ARG_PTR is an index into ARGV, incremented if all went well. |
7354 |
+ *PRED is the predicate processor to insert. |
7355 |
+ |
7356 |
+ Return true if input is valid, false if error. |
7357 |
+ |
7358 |
+ A new predicate node is assigned, along with an argument node |
7359 |
+ obtained with malloc. |
7360 |
+ |
7361 |
+ Used by -inum and -links parsers. */ |
7362 |
+ |
7363 |
+static struct predicate * |
7364 |
+insert_num (char **argv, int *arg_ptr, const struct parser_table *entry) |
7365 |
+{ |
7366 |
+ const char *numstr; |
7367 |
+ |
7368 |
+ if (collect_arg(argv, arg_ptr, &numstr)) |
7369 |
+ { |
7370 |
+ uintmax_t num; |
7371 |
+ enum comparison_type c_type; |
7372 |
+ |
7373 |
+ if (get_num (numstr, &num, &c_type)) |
7374 |
+ { |
7375 |
+ struct predicate *our_pred = insert_primary (entry); |
7376 |
+ our_pred->args.numinfo.kind = c_type; |
7377 |
+ our_pred->args.numinfo.l_val = num; |
7378 |
+ |
7379 |
+ if (options.debug_options & DebugExpressionTree) |
7380 |
+ { |
7381 |
+ fprintf (stderr, "inserting %s\n", our_pred->p_name); |
7382 |
+ fprintf (stderr, " type: %s %s ", |
7383 |
+ (c_type == COMP_GT) ? "gt" : |
7384 |
+ ((c_type == COMP_LT) ? "lt" : ((c_type == COMP_EQ) ? "eq" : "?")), |
7385 |
+ (c_type == COMP_GT) ? " >" : |
7386 |
+ ((c_type == COMP_LT) ? " <" : ((c_type == COMP_EQ) ? " =" : " ?"))); |
7387 |
+ fprintf (stderr, "%ju\n", our_pred->args.numinfo.l_val); |
7388 |
+ } |
7389 |
+ return our_pred; |
7390 |
+ } |
7391 |
+ } |
7392 |
+ return NULL; |
7393 |
+} |
7394 |
+ |
7395 |
+static void |
7396 |
+open_output_file (const char *path, struct format_val *p) |
7397 |
+{ |
7398 |
+ p->segment = NULL; |
7399 |
+ p->quote_opts = clone_quoting_options (NULL); |
7400 |
+ |
7401 |
+ if (!strcmp (path, "/dev/stderr")) |
7402 |
+ { |
7403 |
+ p->stream = stderr; |
7404 |
+ p->filename = _("standard error"); |
7405 |
+ } |
7406 |
+ else if (!strcmp (path, "/dev/stdout")) |
7407 |
+ { |
7408 |
+ p->stream = stdout; |
7409 |
+ p->filename = _("standard output"); |
7410 |
+ } |
7411 |
+ else |
7412 |
+ { |
7413 |
+ p->stream = fopen_safer (path, "w"); |
7414 |
+ p->filename = path; |
7415 |
+ |
7416 |
+ if (p->stream == NULL) |
7417 |
+ { |
7418 |
+ fatal_file_error(path); |
7419 |
+ } |
7420 |
+ } |
7421 |
+ |
7422 |
+ p->dest_is_tty = stream_is_tty(p->stream); |
7423 |
+} |
7424 |
+ |
7425 |
+static void |
7426 |
+open_stdout (struct format_val *p) |
7427 |
+{ |
7428 |
+ open_output_file("/dev/stdout", p); |
7429 |
+} |
7430 |
diff -purN findutils-4.3.12.orig/find/pred.c findutils-4.3.12/find/pred.c |
7431 |
--- findutils-4.3.12.orig/find/pred.c 2007-12-19 16:12:34.000000000 -0500 |
7432 |
+++ findutils-4.3.12/find/pred.c 2008-01-30 08:46:05.758843847 -0500 |
7433 |
@@ -47,6 +47,14 @@ |
7434 |
#include "error.h" |
7435 |
#include "verify.h" |
7436 |
|
7437 |
+#ifdef WITH_SELINUX |
7438 |
+#include <selinux/selinux.h> |
7439 |
+#endif /*WITH_SELINUX*/ |
7440 |
+ |
7441 |
+#ifndef FNM_CASEFOLD |
7442 |
+#define FNM_CASEFOLD (1<<4) |
7443 |
+#endif /*FNM_CASEFOLD*/ |
7444 |
+ |
7445 |
#if ENABLE_NLS |
7446 |
# include <libintl.h> |
7447 |
# define _(Text) gettext (Text) |
7448 |
@@ -229,6 +237,9 @@ struct pred_assoc pred_table[] = |
7449 |
{pred_user, "user "}, |
7450 |
{pred_writable, "writable "}, |
7451 |
{pred_xtype, "xtype "}, |
7452 |
+#ifdef WITH_SELINUX |
7453 |
+ {pred_scontext, "context"}, |
7454 |
+#endif /*WITH_SELINUX*/ |
7455 |
{0, "none "} |
7456 |
}; |
7457 |
#endif |
7458 |
@@ -1045,6 +1056,26 @@ do_fprintf(struct format_val *dest, |
7459 |
mode_to_filetype(stat_buf->st_mode & S_IFMT)); |
7460 |
} |
7461 |
break; |
7462 |
+#ifdef WITH_SELINUX |
7463 |
+ case 'Z': /* SELinux security context */ |
7464 |
+ { |
7465 |
+ security_context_t scontext; |
7466 |
+ int rv; |
7467 |
+ rv = (*options.x_getfilecon)(state.rel_pathname, &scontext); |
7468 |
+ |
7469 |
+ if ( rv < 0 ) { |
7470 |
+ fprintf(stderr, "getfileconf(%s): %s", |
7471 |
+ pathname, strerror(errno)); |
7472 |
+ fflush(stderr); |
7473 |
+ } |
7474 |
+ else { |
7475 |
+ segment->text[segment->text_len] = 's'; |
7476 |
+ checked_fprintf (dest, segment->text, scontext); |
7477 |
+ freecon(scontext); |
7478 |
+ } |
7479 |
+ } |
7480 |
+ break ; |
7481 |
+#endif /* WITH_SELINUX */ |
7482 |
} |
7483 |
/* end of KIND_FORMAT case */ |
7484 |
break; |
7485 |
@@ -1838,6 +1869,31 @@ pred_xtype (const char *pathname, struct |
7486 |
*/ |
7487 |
return (pred_type (pathname, &sbuf, pred_ptr)); |
7488 |
} |
7489 |
+ |
7490 |
+ |
7491 |
+#ifdef WITH_SELINUX |
7492 |
+ |
7493 |
+boolean |
7494 |
+pred_scontext (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
7495 |
+{ |
7496 |
+ int rv; |
7497 |
+ security_context_t scontext; |
7498 |
+ |
7499 |
+ rv = (* options.x_getfilecon)(state.rel_pathname, &scontext); |
7500 |
+ |
7501 |
+ if ( rv < 0 ) { |
7502 |
+ (void) fprintf(stderr, "getfilecon(%s): %s\n", pathname, strerror(errno)); |
7503 |
+ (void) fflush(stderr); |
7504 |
+ return ( false ); |
7505 |
+ } |
7506 |
+ |
7507 |
+ rv = (fnmatch(pred_ptr->args.scontext, scontext,0)==0); |
7508 |
+ freecon(scontext); |
7509 |
+ return rv; |
7510 |
+} |
7511 |
+ |
7512 |
+#endif /*WITH_SELINUX*/ |
7513 |
+ |
7514 |
|
7515 |
/* 1) fork to get a child; parent remembers the child pid |
7516 |
2) child execs the command requested |
7517 |
diff -purN findutils-4.3.12.orig/find/pred.c.orig findutils-4.3.12/find/pred.c.orig |
7518 |
--- findutils-4.3.12.orig/find/pred.c.orig 1969-12-31 19:00:00.000000000 -0500 |
7519 |
+++ findutils-4.3.12/find/pred.c.orig 2007-12-19 16:12:34.000000000 -0500 |
7520 |
@@ -0,0 +1,2405 @@ |
7521 |
+/* pred.c -- execute the expression tree. |
7522 |
+ Copyright (C) 1990, 1991, 1992, 1993, 1994, 2000, 2003, |
7523 |
+ 2004, 2005, 2006, 2007 Free Software Foundation, Inc. |
7524 |
+ |
7525 |
+ This program is free software: you can redistribute it and/or modify |
7526 |
+ it under the terms of the GNU General Public License as published by |
7527 |
+ the Free Software Foundation, either version 3 of the License, or |
7528 |
+ (at your option) any later version. |
7529 |
+ |
7530 |
+ This program is distributed in the hope that it will be useful, |
7531 |
+ but WITHOUT ANY WARRANTY; without even the implied warranty of |
7532 |
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
7533 |
+ GNU General Public License for more details. |
7534 |
+ |
7535 |
+ You should have received a copy of the GNU General Public License |
7536 |
+ along with this program. If not, see <http://www.gnu.org/licenses/>. |
7537 |
+*/ |
7538 |
+ |
7539 |
+#include <config.h> |
7540 |
+#include "defs.h" |
7541 |
+ |
7542 |
+#include <fnmatch.h> |
7543 |
+#include <signal.h> |
7544 |
+#include <math.h> |
7545 |
+#include <pwd.h> |
7546 |
+#include <grp.h> |
7547 |
+#include <sys/types.h> |
7548 |
+#include <sys/stat.h> |
7549 |
+#include <errno.h> |
7550 |
+#include <assert.h> |
7551 |
+#include <stdarg.h> |
7552 |
+#include <fcntl.h> |
7553 |
+#include <locale.h> |
7554 |
+#include <openat.h> |
7555 |
+#include "xalloc.h" |
7556 |
+#include "dirname.h" |
7557 |
+#include "human.h" |
7558 |
+#include "modetype.h" |
7559 |
+#include "filemode.h" |
7560 |
+#include "wait.h" |
7561 |
+#include "printquoted.h" |
7562 |
+#include "buildcmd.h" |
7563 |
+#include "yesno.h" |
7564 |
+#include "listfile.h" |
7565 |
+#include "stat-time.h" |
7566 |
+#include "dircallback.h" |
7567 |
+#include "error.h" |
7568 |
+#include "verify.h" |
7569 |
+ |
7570 |
+#if ENABLE_NLS |
7571 |
+# include <libintl.h> |
7572 |
+# define _(Text) gettext (Text) |
7573 |
+#else |
7574 |
+# define _(Text) Text |
7575 |
+#endif |
7576 |
+#ifdef gettext_noop |
7577 |
+# define N_(String) gettext_noop (String) |
7578 |
+#else |
7579 |
+/* See locate.c for explanation as to why not use (String) */ |
7580 |
+# define N_(String) String |
7581 |
+#endif |
7582 |
+ |
7583 |
+#if !defined(SIGCHLD) && defined(SIGCLD) |
7584 |
+#define SIGCHLD SIGCLD |
7585 |
+#endif |
7586 |
+ |
7587 |
+ |
7588 |
+ |
7589 |
+#if HAVE_DIRENT_H |
7590 |
+# include <dirent.h> |
7591 |
+# define NAMLEN(dirent) strlen((dirent)->d_name) |
7592 |
+#else |
7593 |
+# define dirent direct |
7594 |
+# define NAMLEN(dirent) (dirent)->d_namlen |
7595 |
+# if HAVE_SYS_NDIR_H |
7596 |
+# include <sys/ndir.h> |
7597 |
+# endif |
7598 |
+# if HAVE_SYS_DIR_H |
7599 |
+# include <sys/dir.h> |
7600 |
+# endif |
7601 |
+# if HAVE_NDIR_H |
7602 |
+# include <ndir.h> |
7603 |
+# endif |
7604 |
+#endif |
7605 |
+ |
7606 |
+#ifdef CLOSEDIR_VOID |
7607 |
+/* Fake a return value. */ |
7608 |
+#define CLOSEDIR(d) (closedir (d), 0) |
7609 |
+#else |
7610 |
+#define CLOSEDIR(d) closedir (d) |
7611 |
+#endif |
7612 |
+ |
7613 |
+ |
7614 |
+ |
7615 |
+ |
7616 |
+/* Get or fake the disk device blocksize. |
7617 |
+ Usually defined by sys/param.h (if at all). */ |
7618 |
+#ifndef DEV_BSIZE |
7619 |
+# ifdef BSIZE |
7620 |
+# define DEV_BSIZE BSIZE |
7621 |
+# else /* !BSIZE */ |
7622 |
+# define DEV_BSIZE 4096 |
7623 |
+# endif /* !BSIZE */ |
7624 |
+#endif /* !DEV_BSIZE */ |
7625 |
+ |
7626 |
+/* Extract or fake data from a `struct stat'. |
7627 |
+ ST_BLKSIZE: Preferred I/O blocksize for the file, in bytes. |
7628 |
+ ST_NBLOCKS: Number of blocks in the file, including indirect blocks. |
7629 |
+ ST_NBLOCKSIZE: Size of blocks used when calculating ST_NBLOCKS. */ |
7630 |
+#ifndef HAVE_STRUCT_STAT_ST_BLOCKS |
7631 |
+# define ST_BLKSIZE(statbuf) DEV_BSIZE |
7632 |
+# if defined _POSIX_SOURCE || !defined BSIZE /* fileblocks.c uses BSIZE. */ |
7633 |
+# define ST_NBLOCKS(statbuf) \ |
7634 |
+ (S_ISREG ((statbuf).st_mode) \ |
7635 |
+ || S_ISDIR ((statbuf).st_mode) \ |
7636 |
+ ? (statbuf).st_size / ST_NBLOCKSIZE + ((statbuf).st_size % ST_NBLOCKSIZE != 0) : 0) |
7637 |
+# else /* !_POSIX_SOURCE && BSIZE */ |
7638 |
+# define ST_NBLOCKS(statbuf) \ |
7639 |
+ (S_ISREG ((statbuf).st_mode) \ |
7640 |
+ || S_ISDIR ((statbuf).st_mode) \ |
7641 |
+ ? st_blocks ((statbuf).st_size) : 0) |
7642 |
+# endif /* !_POSIX_SOURCE && BSIZE */ |
7643 |
+#else /* HAVE_STRUCT_STAT_ST_BLOCKS */ |
7644 |
+/* Some systems, like Sequents, return st_blksize of 0 on pipes. */ |
7645 |
+# define ST_BLKSIZE(statbuf) ((statbuf).st_blksize > 0 \ |
7646 |
+ ? (statbuf).st_blksize : DEV_BSIZE) |
7647 |
+# if defined hpux || defined __hpux__ || defined __hpux |
7648 |
+/* HP-UX counts st_blocks in 1024-byte units. |
7649 |
+ This loses when mixing HP-UX and BSD file systems with NFS. */ |
7650 |
+# define ST_NBLOCKSIZE 1024 |
7651 |
+# else /* !hpux */ |
7652 |
+# if defined _AIX && defined _I386 |
7653 |
+/* AIX PS/2 counts st_blocks in 4K units. */ |
7654 |
+# define ST_NBLOCKSIZE (4 * 1024) |
7655 |
+# else /* not AIX PS/2 */ |
7656 |
+# if defined _CRAY |
7657 |
+# define ST_NBLOCKS(statbuf) \ |
7658 |
+ (S_ISREG ((statbuf).st_mode) \ |
7659 |
+ || S_ISDIR ((statbuf).st_mode) \ |
7660 |
+ ? (statbuf).st_blocks * ST_BLKSIZE(statbuf)/ST_NBLOCKSIZE : 0) |
7661 |
+# endif /* _CRAY */ |
7662 |
+# endif /* not AIX PS/2 */ |
7663 |
+# endif /* !hpux */ |
7664 |
+#endif /* HAVE_STRUCT_STAT_ST_BLOCKS */ |
7665 |
+ |
7666 |
+#ifndef ST_NBLOCKS |
7667 |
+# define ST_NBLOCKS(statbuf) \ |
7668 |
+ (S_ISREG ((statbuf).st_mode) \ |
7669 |
+ || S_ISDIR ((statbuf).st_mode) \ |
7670 |
+ ? (statbuf).st_blocks : 0) |
7671 |
+#endif |
7672 |
+ |
7673 |
+#ifndef ST_NBLOCKSIZE |
7674 |
+# define ST_NBLOCKSIZE 512 |
7675 |
+#endif |
7676 |
+ |
7677 |
+ |
7678 |
+#undef MAX |
7679 |
+#define MAX(a, b) ((a) > (b) ? (a) : (b)) |
7680 |
+ |
7681 |
+static boolean match_lname PARAMS((const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr, boolean ignore_case)); |
7682 |
+ |
7683 |
+static char *format_date PARAMS((struct timespec ts, int kind)); |
7684 |
+static char *ctime_format PARAMS((struct timespec ts)); |
7685 |
+ |
7686 |
+#ifdef DEBUG |
7687 |
+struct pred_assoc |
7688 |
+{ |
7689 |
+ PRED_FUNC pred_func; |
7690 |
+ char *pred_name; |
7691 |
+}; |
7692 |
+ |
7693 |
+struct pred_assoc pred_table[] = |
7694 |
+{ |
7695 |
+ {pred_amin, "amin "}, |
7696 |
+ {pred_and, "and "}, |
7697 |
+ {pred_anewer, "anewer "}, |
7698 |
+ {pred_atime, "atime "}, |
7699 |
+ {pred_closeparen, ") "}, |
7700 |
+ {pred_cmin, "cmin "}, |
7701 |
+ {pred_cnewer, "cnewer "}, |
7702 |
+ {pred_comma, ", "}, |
7703 |
+ {pred_ctime, "ctime "}, |
7704 |
+ {pred_delete, "delete "}, |
7705 |
+ {pred_empty, "empty "}, |
7706 |
+ {pred_exec, "exec "}, |
7707 |
+ {pred_execdir, "execdir "}, |
7708 |
+ {pred_executable, "executable "}, |
7709 |
+ {pred_false, "false "}, |
7710 |
+ {pred_fprint, "fprint "}, |
7711 |
+ {pred_fprint0, "fprint0 "}, |
7712 |
+ {pred_fprintf, "fprintf "}, |
7713 |
+ {pred_fstype, "fstype "}, |
7714 |
+ {pred_gid, "gid "}, |
7715 |
+ {pred_group, "group "}, |
7716 |
+ {pred_ilname, "ilname "}, |
7717 |
+ {pred_iname, "iname "}, |
7718 |
+ {pred_inum, "inum "}, |
7719 |
+ {pred_ipath, "ipath "}, |
7720 |
+ {pred_links, "links "}, |
7721 |
+ {pred_lname, "lname "}, |
7722 |
+ {pred_ls, "ls "}, |
7723 |
+ {pred_mmin, "mmin "}, |
7724 |
+ {pred_mtime, "mtime "}, |
7725 |
+ {pred_name, "name "}, |
7726 |
+ {pred_negate, "not "}, |
7727 |
+ {pred_newer, "newer "}, |
7728 |
+ {pred_newerXY, "newerXY "}, |
7729 |
+ {pred_nogroup, "nogroup "}, |
7730 |
+ {pred_nouser, "nouser "}, |
7731 |
+ {pred_ok, "ok "}, |
7732 |
+ {pred_okdir, "okdir "}, |
7733 |
+ {pred_openparen, "( "}, |
7734 |
+ {pred_or, "or "}, |
7735 |
+ {pred_path, "path "}, |
7736 |
+ {pred_perm, "perm "}, |
7737 |
+ {pred_print, "print "}, |
7738 |
+ {pred_print0, "print0 "}, |
7739 |
+ {pred_prune, "prune "}, |
7740 |
+ {pred_quit, "quit "}, |
7741 |
+ {pred_readable, "readable "}, |
7742 |
+ {pred_regex, "regex "}, |
7743 |
+ {pred_samefile,"samefile "}, |
7744 |
+ {pred_size, "size "}, |
7745 |
+ {pred_true, "true "}, |
7746 |
+ {pred_type, "type "}, |
7747 |
+ {pred_uid, "uid "}, |
7748 |
+ {pred_used, "used "}, |
7749 |
+ {pred_user, "user "}, |
7750 |
+ {pred_writable, "writable "}, |
7751 |
+ {pred_xtype, "xtype "}, |
7752 |
+ {0, "none "} |
7753 |
+}; |
7754 |
+#endif |
7755 |
+ |
7756 |
+/* Returns ts1 - ts2 */ |
7757 |
+static double ts_difference(struct timespec ts1, |
7758 |
+ struct timespec ts2) |
7759 |
+{ |
7760 |
+ double d = difftime(ts1.tv_sec, ts2.tv_sec) |
7761 |
+ + (1.0e-9 * (ts1.tv_nsec - ts2.tv_nsec)); |
7762 |
+ return d; |
7763 |
+} |
7764 |
+ |
7765 |
+ |
7766 |
+static int |
7767 |
+compare_ts(struct timespec ts1, |
7768 |
+ struct timespec ts2) |
7769 |
+{ |
7770 |
+ if ((ts1.tv_sec == ts2.tv_sec) && |
7771 |
+ (ts1.tv_nsec == ts2.tv_nsec)) |
7772 |
+ { |
7773 |
+ return 0; |
7774 |
+ } |
7775 |
+ else |
7776 |
+ { |
7777 |
+ double diff = ts_difference(ts1, ts2); |
7778 |
+ return diff < 0.0 ? -1 : +1; |
7779 |
+ } |
7780 |
+} |
7781 |
+ |
7782 |
+/* Predicate processing routines. |
7783 |
+ |
7784 |
+ PATHNAME is the full pathname of the file being checked. |
7785 |
+ *STAT_BUF contains information about PATHNAME. |
7786 |
+ *PRED_PTR contains information for applying the predicate. |
7787 |
+ |
7788 |
+ Return true if the file passes this predicate, false if not. */ |
7789 |
+ |
7790 |
+ |
7791 |
+/* pred_timewindow |
7792 |
+ * |
7793 |
+ * Returns true if THE_TIME is |
7794 |
+ * COMP_GT: after the specified time |
7795 |
+ * COMP_LT: before the specified time |
7796 |
+ * COMP_EQ: less than WINDOW seconds after the specified time. |
7797 |
+ */ |
7798 |
+static boolean |
7799 |
+pred_timewindow(struct timespec ts, struct predicate const *pred_ptr, int window) |
7800 |
+{ |
7801 |
+ switch (pred_ptr->args.reftime.kind) |
7802 |
+ { |
7803 |
+ case COMP_GT: |
7804 |
+ return compare_ts(ts, pred_ptr->args.reftime.ts) > 0; |
7805 |
+ |
7806 |
+ case COMP_LT: |
7807 |
+ return compare_ts(ts, pred_ptr->args.reftime.ts) < 0; |
7808 |
+ |
7809 |
+ case COMP_EQ: |
7810 |
+ { |
7811 |
+ double delta = ts_difference(ts, pred_ptr->args.reftime.ts); |
7812 |
+ return (delta >= 0.0 && delta < window); |
7813 |
+ } |
7814 |
+ } |
7815 |
+ assert (0); |
7816 |
+ abort (); |
7817 |
+} |
7818 |
+ |
7819 |
+ |
7820 |
+boolean |
7821 |
+pred_amin (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
7822 |
+{ |
7823 |
+ (void) &pathname; |
7824 |
+ return pred_timewindow(get_stat_atime(stat_buf), pred_ptr, 60); |
7825 |
+} |
7826 |
+ |
7827 |
+boolean |
7828 |
+pred_and (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
7829 |
+{ |
7830 |
+ if (pred_ptr->pred_left == NULL |
7831 |
+ || apply_predicate(pathname, stat_buf, pred_ptr->pred_left)) |
7832 |
+ { |
7833 |
+ return apply_predicate(pathname, stat_buf, pred_ptr->pred_right); |
7834 |
+ } |
7835 |
+ else |
7836 |
+ return false; |
7837 |
+} |
7838 |
+ |
7839 |
+boolean |
7840 |
+pred_anewer (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
7841 |
+{ |
7842 |
+ (void) &pathname; |
7843 |
+ assert (COMP_GT == pred_ptr->args.reftime.kind); |
7844 |
+ return compare_ts(get_stat_atime(stat_buf), pred_ptr->args.reftime.ts) > 0; |
7845 |
+} |
7846 |
+ |
7847 |
+boolean |
7848 |
+pred_atime (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
7849 |
+{ |
7850 |
+ (void) &pathname; |
7851 |
+ return pred_timewindow(get_stat_atime(stat_buf), pred_ptr, DAYSECS); |
7852 |
+} |
7853 |
+ |
7854 |
+boolean |
7855 |
+pred_closeparen (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
7856 |
+{ |
7857 |
+ (void) &pathname; |
7858 |
+ (void) &stat_buf; |
7859 |
+ (void) &pred_ptr; |
7860 |
+ |
7861 |
+ return true; |
7862 |
+} |
7863 |
+ |
7864 |
+boolean |
7865 |
+pred_cmin (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
7866 |
+{ |
7867 |
+ (void) pathname; |
7868 |
+ return pred_timewindow(get_stat_ctime(stat_buf), pred_ptr, 60); |
7869 |
+} |
7870 |
+ |
7871 |
+boolean |
7872 |
+pred_cnewer (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
7873 |
+{ |
7874 |
+ (void) pathname; |
7875 |
+ |
7876 |
+ assert (COMP_GT == pred_ptr->args.reftime.kind); |
7877 |
+ return compare_ts(get_stat_ctime(stat_buf), pred_ptr->args.reftime.ts) > 0; |
7878 |
+} |
7879 |
+ |
7880 |
+boolean |
7881 |
+pred_comma (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
7882 |
+{ |
7883 |
+ if (pred_ptr->pred_left != NULL) |
7884 |
+ { |
7885 |
+ apply_predicate(pathname, stat_buf,pred_ptr->pred_left); |
7886 |
+ } |
7887 |
+ return apply_predicate(pathname, stat_buf, pred_ptr->pred_right); |
7888 |
+} |
7889 |
+ |
7890 |
+boolean |
7891 |
+pred_ctime (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
7892 |
+{ |
7893 |
+ (void) &pathname; |
7894 |
+ return pred_timewindow(get_stat_ctime(stat_buf), pred_ptr, DAYSECS); |
7895 |
+} |
7896 |
+ |
7897 |
+static boolean |
7898 |
+perform_delete(int flags) |
7899 |
+{ |
7900 |
+ return 0 == unlinkat(state.cwd_dir_fd, state.rel_pathname, flags); |
7901 |
+} |
7902 |
+ |
7903 |
+ |
7904 |
+boolean |
7905 |
+pred_delete (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
7906 |
+{ |
7907 |
+ (void) pred_ptr; |
7908 |
+ (void) stat_buf; |
7909 |
+ if (strcmp (state.rel_pathname, ".")) |
7910 |
+ { |
7911 |
+ int flags=0; |
7912 |
+ if (state.have_stat && S_ISDIR(stat_buf->st_mode)) |
7913 |
+ flags |= AT_REMOVEDIR; |
7914 |
+ if (perform_delete(flags)) |
7915 |
+ { |
7916 |
+ return true; |
7917 |
+ } |
7918 |
+ else |
7919 |
+ { |
7920 |
+ if (EISDIR == errno) |
7921 |
+ { |
7922 |
+ if ((flags & AT_REMOVEDIR) == 0) |
7923 |
+ { |
7924 |
+ /* unlink() operation failed because we should have done rmdir(). */ |
7925 |
+ flags |= AT_REMOVEDIR; |
7926 |
+ if (perform_delete(flags)) |
7927 |
+ return true; |
7928 |
+ } |
7929 |
+ } |
7930 |
+ } |
7931 |
+ error (0, errno, _("cannot delete %s" |
7932 |
+ /* TRANSLATORS: the argument is either a |
7933 |
+ * file or a directory, but we do not know which. |
7934 |
+ * Mail bug-findutils@×××.org if you cannot correctly |
7935 |
+ * translate the string without knowing. |
7936 |
+ */), |
7937 |
+ safely_quote_err_filename(0, pathname)); |
7938 |
+ /* Previously I had believed that having the -delete action |
7939 |
+ * return false provided the user with control over whether an |
7940 |
+ * error message is issued. While this is true, the policy of |
7941 |
+ * not affecting the exit status is contrary to the POSIX |
7942 |
+ * requirement that diagnostic messages are accompanied by a |
7943 |
+ * nonzero exit status. While -delete is not a POSIX option and |
7944 |
+ * we can therefore opt not to follow POSIX in this case, that |
7945 |
+ * seems somewhat arbitrary and confusing. So, as of |
7946 |
+ * findutils-4.3.11, we also set the exit status in this case. |
7947 |
+ */ |
7948 |
+ state.exit_status = 1; |
7949 |
+ return false; |
7950 |
+ } |
7951 |
+ else |
7952 |
+ { |
7953 |
+ /* nothing to do. */ |
7954 |
+ return true; |
7955 |
+ } |
7956 |
+} |
7957 |
+ |
7958 |
+boolean |
7959 |
+pred_empty (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
7960 |
+{ |
7961 |
+ (void) pathname; |
7962 |
+ (void) pred_ptr; |
7963 |
+ |
7964 |
+ if (S_ISDIR (stat_buf->st_mode)) |
7965 |
+ { |
7966 |
+ int fd; |
7967 |
+ DIR *d; |
7968 |
+ struct dirent *dp; |
7969 |
+ boolean empty = true; |
7970 |
+ |
7971 |
+ errno = 0; |
7972 |
+ if ((fd = openat(state.cwd_dir_fd, state.rel_pathname, O_RDONLY |
7973 |
+#if defined O_LARGEFILE |
7974 |
+ |O_LARGEFILE |
7975 |
+#endif |
7976 |
+ )) < 0) |
7977 |
+ { |
7978 |
+ error (0, errno, "%s", safely_quote_err_filename(0, pathname)); |
7979 |
+ state.exit_status = 1; |
7980 |
+ return false; |
7981 |
+ } |
7982 |
+ d = fdopendir (fd); |
7983 |
+ if (d == NULL) |
7984 |
+ { |
7985 |
+ error (0, errno, "%s", safely_quote_err_filename(0, pathname)); |
7986 |
+ state.exit_status = 1; |
7987 |
+ return false; |
7988 |
+ } |
7989 |
+ for (dp = readdir (d); dp; dp = readdir (d)) |
7990 |
+ { |
7991 |
+ if (dp->d_name[0] != '.' |
7992 |
+ || (dp->d_name[1] != '\0' |
7993 |
+ && (dp->d_name[1] != '.' || dp->d_name[2] != '\0'))) |
7994 |
+ { |
7995 |
+ empty = false; |
7996 |
+ break; |
7997 |
+ } |
7998 |
+ } |
7999 |
+ if (CLOSEDIR (d)) |
8000 |
+ { |
8001 |
+ error (0, errno, "%s", safely_quote_err_filename(0, pathname)); |
8002 |
+ state.exit_status = 1; |
8003 |
+ return false; |
8004 |
+ } |
8005 |
+ return (empty); |
8006 |
+ } |
8007 |
+ else if (S_ISREG (stat_buf->st_mode)) |
8008 |
+ return (stat_buf->st_size == 0); |
8009 |
+ else |
8010 |
+ return (false); |
8011 |
+} |
8012 |
+ |
8013 |
+static boolean |
8014 |
+new_impl_pred_exec (int dirfd, const char *pathname, |
8015 |
+ struct stat *stat_buf, |
8016 |
+ struct predicate *pred_ptr, |
8017 |
+ const char *prefix, size_t pfxlen) |
8018 |
+{ |
8019 |
+ struct exec_val *execp = &pred_ptr->args.exec_vec; |
8020 |
+ size_t len = strlen(pathname); |
8021 |
+ |
8022 |
+ (void) stat_buf; |
8023 |
+ execp->dirfd = dirfd; |
8024 |
+ if (execp->multiple) |
8025 |
+ { |
8026 |
+ /* Push the argument onto the current list. |
8027 |
+ * The command may or may not be run at this point, |
8028 |
+ * depending on the command line length limits. |
8029 |
+ */ |
8030 |
+ bc_push_arg(&execp->ctl, |
8031 |
+ &execp->state, |
8032 |
+ pathname, len+1, |
8033 |
+ prefix, pfxlen, |
8034 |
+ 0); |
8035 |
+ |
8036 |
+ /* remember that there are pending execdirs. */ |
8037 |
+ state.execdirs_outstanding = true; |
8038 |
+ |
8039 |
+ /* POSIX: If the primary expression is punctuated by a plus |
8040 |
+ * sign, the primary shall always evaluate as true |
8041 |
+ */ |
8042 |
+ return true; |
8043 |
+ } |
8044 |
+ else |
8045 |
+ { |
8046 |
+ int i; |
8047 |
+ |
8048 |
+ for (i=0; i<execp->num_args; ++i) |
8049 |
+ { |
8050 |
+ bc_do_insert(&execp->ctl, |
8051 |
+ &execp->state, |
8052 |
+ execp->replace_vec[i], |
8053 |
+ strlen(execp->replace_vec[i]), |
8054 |
+ prefix, pfxlen, |
8055 |
+ pathname, len, |
8056 |
+ 0); |
8057 |
+ } |
8058 |
+ |
8059 |
+ /* Actually invoke the command. */ |
8060 |
+ return execp->ctl.exec_callback(&execp->ctl, |
8061 |
+ &execp->state); |
8062 |
+ } |
8063 |
+} |
8064 |
+ |
8065 |
+ |
8066 |
+boolean |
8067 |
+pred_exec (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8068 |
+{ |
8069 |
+ return new_impl_pred_exec(get_start_dirfd(), |
8070 |
+ pathname, stat_buf, pred_ptr, NULL, 0); |
8071 |
+} |
8072 |
+ |
8073 |
+boolean |
8074 |
+pred_execdir (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8075 |
+{ |
8076 |
+ const char *prefix = (state.rel_pathname[0] == '/') ? NULL : "./"; |
8077 |
+ (void) &pathname; |
8078 |
+ return new_impl_pred_exec (get_current_dirfd(), |
8079 |
+ state.rel_pathname, stat_buf, pred_ptr, |
8080 |
+ prefix, (prefix ? 2 : 0)); |
8081 |
+} |
8082 |
+ |
8083 |
+boolean |
8084 |
+pred_false (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8085 |
+{ |
8086 |
+ (void) &pathname; |
8087 |
+ (void) &stat_buf; |
8088 |
+ (void) &pred_ptr; |
8089 |
+ |
8090 |
+ |
8091 |
+ return (false); |
8092 |
+} |
8093 |
+ |
8094 |
+boolean |
8095 |
+pred_fls (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8096 |
+{ |
8097 |
+ FILE * stream = pred_ptr->args.printf_vec.stream; |
8098 |
+ list_file (pathname, state.cwd_dir_fd, state.rel_pathname, stat_buf, |
8099 |
+ options.start_time.tv_sec, |
8100 |
+ options.output_block_size, |
8101 |
+ pred_ptr->literal_control_chars, stream); |
8102 |
+ return true; |
8103 |
+} |
8104 |
+ |
8105 |
+boolean |
8106 |
+pred_fprint (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8107 |
+{ |
8108 |
+ (void) &pathname; |
8109 |
+ (void) &stat_buf; |
8110 |
+ |
8111 |
+ print_quoted(pred_ptr->args.printf_vec.stream, |
8112 |
+ pred_ptr->args.printf_vec.quote_opts, |
8113 |
+ pred_ptr->args.printf_vec.dest_is_tty, |
8114 |
+ "%s\n", |
8115 |
+ pathname); |
8116 |
+ return true; |
8117 |
+} |
8118 |
+ |
8119 |
+boolean |
8120 |
+pred_fprint0 (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8121 |
+{ |
8122 |
+ FILE * fp = pred_ptr->args.printf_vec.stream; |
8123 |
+ |
8124 |
+ (void) &stat_buf; |
8125 |
+ |
8126 |
+ fputs (pathname, fp); |
8127 |
+ putc (0, fp); |
8128 |
+ return true; |
8129 |
+} |
8130 |
+ |
8131 |
+ |
8132 |
+ |
8133 |
+static char* |
8134 |
+mode_to_filetype(mode_t m) |
8135 |
+{ |
8136 |
+#define HANDLE_TYPE(t,letter) if (m==t) { return letter; } |
8137 |
+#ifdef S_IFREG |
8138 |
+ HANDLE_TYPE(S_IFREG, "f"); /* regular file */ |
8139 |
+#endif |
8140 |
+#ifdef S_IFDIR |
8141 |
+ HANDLE_TYPE(S_IFDIR, "d"); /* directory */ |
8142 |
+#endif |
8143 |
+#ifdef S_IFLNK |
8144 |
+ HANDLE_TYPE(S_IFLNK, "l"); /* symbolic link */ |
8145 |
+#endif |
8146 |
+#ifdef S_IFSOCK |
8147 |
+ HANDLE_TYPE(S_IFSOCK, "s"); /* Unix domain socket */ |
8148 |
+#endif |
8149 |
+#ifdef S_IFBLK |
8150 |
+ HANDLE_TYPE(S_IFBLK, "b"); /* block device */ |
8151 |
+#endif |
8152 |
+#ifdef S_IFCHR |
8153 |
+ HANDLE_TYPE(S_IFCHR, "c"); /* character device */ |
8154 |
+#endif |
8155 |
+#ifdef S_IFIFO |
8156 |
+ HANDLE_TYPE(S_IFIFO, "p"); /* FIFO */ |
8157 |
+#endif |
8158 |
+#ifdef S_IFDOOR |
8159 |
+ HANDLE_TYPE(S_IFDOOR, "D"); /* Door (e.g. on Solaris) */ |
8160 |
+#endif |
8161 |
+ return "U"; /* Unknown */ |
8162 |
+} |
8163 |
+ |
8164 |
+static double |
8165 |
+file_sparseness(const struct stat *p) |
8166 |
+{ |
8167 |
+#if defined HAVE_STRUCT_STAT_ST_BLOCKS |
8168 |
+ if (0 == p->st_size) |
8169 |
+ { |
8170 |
+ if (0 == p->st_blocks) |
8171 |
+ return 1.0; |
8172 |
+ else |
8173 |
+ return p->st_blocks < 0 ? -HUGE_VAL : HUGE_VAL; |
8174 |
+ } |
8175 |
+ else |
8176 |
+ { |
8177 |
+ double blklen = file_blocksize(p) * (double)p->st_blocks; |
8178 |
+ return blklen / p->st_size; |
8179 |
+ } |
8180 |
+#else |
8181 |
+ return 1.0; |
8182 |
+#endif |
8183 |
+} |
8184 |
+ |
8185 |
+ |
8186 |
+ |
8187 |
+static void |
8188 |
+checked_fprintf(struct format_val *dest, const char *fmt, ...) |
8189 |
+{ |
8190 |
+ int rv; |
8191 |
+ va_list ap; |
8192 |
+ |
8193 |
+ va_start(ap, fmt); |
8194 |
+ rv = vfprintf(dest->stream, fmt, ap); |
8195 |
+ if (rv < 0) |
8196 |
+ nonfatal_file_error(dest->filename); |
8197 |
+} |
8198 |
+ |
8199 |
+ |
8200 |
+static void |
8201 |
+checked_print_quoted (struct format_val *dest, |
8202 |
+ const char *format, const char *s) |
8203 |
+{ |
8204 |
+ int rv = print_quoted(dest->stream, dest->quote_opts, dest->dest_is_tty, |
8205 |
+ format, s); |
8206 |
+ if (rv < 0) |
8207 |
+ nonfatal_file_error(dest->filename); |
8208 |
+} |
8209 |
+ |
8210 |
+ |
8211 |
+static void |
8212 |
+checked_fwrite(void *p, size_t siz, size_t nmemb, struct format_val *dest) |
8213 |
+{ |
8214 |
+ int items_written = fwrite(p, siz, nmemb, dest->stream); |
8215 |
+ if (items_written < nmemb) |
8216 |
+ nonfatal_file_error(dest->filename); |
8217 |
+} |
8218 |
+ |
8219 |
+static void |
8220 |
+checked_fflush(struct format_val *dest) |
8221 |
+{ |
8222 |
+ if (0 != fflush(dest->stream)) |
8223 |
+ { |
8224 |
+ nonfatal_file_error(dest->filename); |
8225 |
+ } |
8226 |
+} |
8227 |
+ |
8228 |
+static void |
8229 |
+do_fprintf(struct format_val *dest, |
8230 |
+ struct segment *segment, |
8231 |
+ const char *pathname, |
8232 |
+ const struct stat *stat_buf) |
8233 |
+{ |
8234 |
+ char hbuf[LONGEST_HUMAN_READABLE + 1]; |
8235 |
+ const char *cp; |
8236 |
+ |
8237 |
+ switch (segment->segkind) |
8238 |
+ { |
8239 |
+ case KIND_PLAIN: /* Plain text string (no % conversion). */ |
8240 |
+ /* trusted */ |
8241 |
+ checked_fwrite(segment->text, 1, segment->text_len, dest); |
8242 |
+ break; |
8243 |
+ |
8244 |
+ case KIND_STOP: /* Terminate argument and flush output. */ |
8245 |
+ /* trusted */ |
8246 |
+ checked_fwrite(segment->text, 1, segment->text_len, dest); |
8247 |
+ checked_fflush(dest); |
8248 |
+ break; |
8249 |
+ |
8250 |
+ case KIND_FORMAT: |
8251 |
+ switch (segment->format_char[0]) |
8252 |
+ { |
8253 |
+ case 'a': /* atime in `ctime' format. */ |
8254 |
+ /* UNTRUSTED, probably unexploitable */ |
8255 |
+ checked_fprintf (dest, segment->text, ctime_format (get_stat_atime(stat_buf))); |
8256 |
+ break; |
8257 |
+ case 'b': /* size in 512-byte blocks */ |
8258 |
+ /* UNTRUSTED, probably unexploitable */ |
8259 |
+ checked_fprintf (dest, segment->text, |
8260 |
+ human_readable ((uintmax_t) ST_NBLOCKS (*stat_buf), |
8261 |
+ hbuf, human_ceiling, |
8262 |
+ ST_NBLOCKSIZE, 512)); |
8263 |
+ break; |
8264 |
+ case 'c': /* ctime in `ctime' format */ |
8265 |
+ /* UNTRUSTED, probably unexploitable */ |
8266 |
+ checked_fprintf (dest, segment->text, ctime_format (get_stat_ctime(stat_buf))); |
8267 |
+ break; |
8268 |
+ case 'd': /* depth in search tree */ |
8269 |
+ /* UNTRUSTED, probably unexploitable */ |
8270 |
+ checked_fprintf (dest, segment->text, state.curdepth); |
8271 |
+ break; |
8272 |
+ case 'D': /* Device on which file exists (stat.st_dev) */ |
8273 |
+ /* trusted */ |
8274 |
+ checked_fprintf (dest, segment->text, |
8275 |
+ human_readable ((uintmax_t) stat_buf->st_dev, hbuf, |
8276 |
+ human_ceiling, 1, 1)); |
8277 |
+ break; |
8278 |
+ case 'f': /* base name of path */ |
8279 |
+ /* sanitised */ |
8280 |
+ { |
8281 |
+ char *base = base_name (pathname); |
8282 |
+ checked_print_quoted (dest, segment->text, base); |
8283 |
+ free (base); |
8284 |
+ } |
8285 |
+ break; |
8286 |
+ case 'F': /* file system type */ |
8287 |
+ /* trusted */ |
8288 |
+ checked_print_quoted (dest, segment->text, filesystem_type (stat_buf, pathname)); |
8289 |
+ break; |
8290 |
+ case 'g': /* group name */ |
8291 |
+ /* trusted */ |
8292 |
+ /* (well, the actual group is selected by the user but |
8293 |
+ * its name was selected by the system administrator) |
8294 |
+ */ |
8295 |
+ { |
8296 |
+ struct group *g; |
8297 |
+ |
8298 |
+ g = getgrgid (stat_buf->st_gid); |
8299 |
+ if (g) |
8300 |
+ { |
8301 |
+ segment->text[segment->text_len] = 's'; |
8302 |
+ checked_fprintf (dest, segment->text, g->gr_name); |
8303 |
+ break; |
8304 |
+ } |
8305 |
+ else |
8306 |
+ { |
8307 |
+ /* Do nothing. */ |
8308 |
+ /*FALLTHROUGH*/ |
8309 |
+ } |
8310 |
+ } |
8311 |
+ /*FALLTHROUGH*/ /*...sometimes, so 'G' case.*/ |
8312 |
+ |
8313 |
+ case 'G': /* GID number */ |
8314 |
+ /* UNTRUSTED, probably unexploitable */ |
8315 |
+ checked_fprintf (dest, segment->text, |
8316 |
+ human_readable ((uintmax_t) stat_buf->st_gid, hbuf, |
8317 |
+ human_ceiling, 1, 1)); |
8318 |
+ break; |
8319 |
+ case 'h': /* leading directories part of path */ |
8320 |
+ /* sanitised */ |
8321 |
+ { |
8322 |
+ cp = strrchr (pathname, '/'); |
8323 |
+ if (cp == NULL) /* No leading directories. */ |
8324 |
+ { |
8325 |
+ /* If there is no slash in the pathname, we still |
8326 |
+ * print the string because it contains characters |
8327 |
+ * other than just '%s'. The %h expands to ".". |
8328 |
+ */ |
8329 |
+ checked_print_quoted (dest, segment->text, "."); |
8330 |
+ } |
8331 |
+ else |
8332 |
+ { |
8333 |
+ char *s = strdup(pathname); |
8334 |
+ s[cp - pathname] = 0; |
8335 |
+ checked_print_quoted (dest, segment->text, s); |
8336 |
+ free(s); |
8337 |
+ } |
8338 |
+ } |
8339 |
+ break; |
8340 |
+ |
8341 |
+ case 'H': /* ARGV element file was found under */ |
8342 |
+ /* trusted */ |
8343 |
+ { |
8344 |
+ char *s = xmalloc(state.starting_path_length+1); |
8345 |
+ memcpy(s, pathname, state.starting_path_length); |
8346 |
+ s[state.starting_path_length] = 0; |
8347 |
+ checked_fprintf (dest, segment->text, s); |
8348 |
+ free(s); |
8349 |
+ } |
8350 |
+ break; |
8351 |
+ |
8352 |
+ case 'i': /* inode number */ |
8353 |
+ /* UNTRUSTED, but not exploitable I think */ |
8354 |
+ checked_fprintf (dest, segment->text, |
8355 |
+ human_readable ((uintmax_t) stat_buf->st_ino, hbuf, |
8356 |
+ human_ceiling, |
8357 |
+ 1, 1)); |
8358 |
+ break; |
8359 |
+ case 'k': /* size in 1K blocks */ |
8360 |
+ /* UNTRUSTED, but not exploitable I think */ |
8361 |
+ checked_fprintf (dest, segment->text, |
8362 |
+ human_readable ((uintmax_t) ST_NBLOCKS (*stat_buf), |
8363 |
+ hbuf, human_ceiling, |
8364 |
+ ST_NBLOCKSIZE, 1024)); |
8365 |
+ break; |
8366 |
+ case 'l': /* object of symlink */ |
8367 |
+ /* sanitised */ |
8368 |
+#ifdef S_ISLNK |
8369 |
+ { |
8370 |
+ char *linkname = 0; |
8371 |
+ |
8372 |
+ if (S_ISLNK (stat_buf->st_mode)) |
8373 |
+ { |
8374 |
+ linkname = get_link_name_at (pathname, state.cwd_dir_fd, state.rel_pathname); |
8375 |
+ if (linkname == 0) |
8376 |
+ state.exit_status = 1; |
8377 |
+ } |
8378 |
+ if (linkname) |
8379 |
+ { |
8380 |
+ checked_print_quoted (dest, segment->text, linkname); |
8381 |
+ free (linkname); |
8382 |
+ } |
8383 |
+ else |
8384 |
+ { |
8385 |
+ /* We still need to honour the field width etc., so this is |
8386 |
+ * not a no-op. |
8387 |
+ */ |
8388 |
+ checked_print_quoted (dest, segment->text, ""); |
8389 |
+ } |
8390 |
+ } |
8391 |
+#endif /* S_ISLNK */ |
8392 |
+ break; |
8393 |
+ |
8394 |
+ case 'M': /* mode as 10 chars (eg., "-rwxr-x--x" */ |
8395 |
+ /* UNTRUSTED, probably unexploitable */ |
8396 |
+ { |
8397 |
+ char modestring[16] ; |
8398 |
+ filemodestring (stat_buf, modestring); |
8399 |
+ modestring[10] = '\0'; |
8400 |
+ checked_fprintf (dest, segment->text, modestring); |
8401 |
+ } |
8402 |
+ break; |
8403 |
+ |
8404 |
+ case 'm': /* mode as octal number (perms only) */ |
8405 |
+ /* UNTRUSTED, probably unexploitable */ |
8406 |
+ { |
8407 |
+ /* Output the mode portably using the traditional numbers, |
8408 |
+ even if the host unwisely uses some other numbering |
8409 |
+ scheme. But help the compiler in the common case where |
8410 |
+ the host uses the traditional numbering scheme. */ |
8411 |
+ mode_t m = stat_buf->st_mode; |
8412 |
+ boolean traditional_numbering_scheme = |
8413 |
+ (S_ISUID == 04000 && S_ISGID == 02000 && S_ISVTX == 01000 |
8414 |
+ && S_IRUSR == 00400 && S_IWUSR == 00200 && S_IXUSR == 00100 |
8415 |
+ && S_IRGRP == 00040 && S_IWGRP == 00020 && S_IXGRP == 00010 |
8416 |
+ && S_IROTH == 00004 && S_IWOTH == 00002 && S_IXOTH == 00001); |
8417 |
+ checked_fprintf (dest, segment->text, |
8418 |
+ (traditional_numbering_scheme |
8419 |
+ ? m & MODE_ALL |
8420 |
+ : ((m & S_ISUID ? 04000 : 0) |
8421 |
+ | (m & S_ISGID ? 02000 : 0) |
8422 |
+ | (m & S_ISVTX ? 01000 : 0) |
8423 |
+ | (m & S_IRUSR ? 00400 : 0) |
8424 |
+ | (m & S_IWUSR ? 00200 : 0) |
8425 |
+ | (m & S_IXUSR ? 00100 : 0) |
8426 |
+ | (m & S_IRGRP ? 00040 : 0) |
8427 |
+ | (m & S_IWGRP ? 00020 : 0) |
8428 |
+ | (m & S_IXGRP ? 00010 : 0) |
8429 |
+ | (m & S_IROTH ? 00004 : 0) |
8430 |
+ | (m & S_IWOTH ? 00002 : 0) |
8431 |
+ | (m & S_IXOTH ? 00001 : 0)))); |
8432 |
+ } |
8433 |
+ break; |
8434 |
+ |
8435 |
+ case 'n': /* number of links */ |
8436 |
+ /* UNTRUSTED, probably unexploitable */ |
8437 |
+ checked_fprintf (dest, segment->text, |
8438 |
+ human_readable ((uintmax_t) stat_buf->st_nlink, |
8439 |
+ hbuf, |
8440 |
+ human_ceiling, |
8441 |
+ 1, 1)); |
8442 |
+ break; |
8443 |
+ |
8444 |
+ case 'p': /* pathname */ |
8445 |
+ /* sanitised */ |
8446 |
+ checked_print_quoted (dest, segment->text, pathname); |
8447 |
+ break; |
8448 |
+ |
8449 |
+ case 'P': /* pathname with ARGV element stripped */ |
8450 |
+ /* sanitised */ |
8451 |
+ if (state.curdepth > 0) |
8452 |
+ { |
8453 |
+ cp = pathname + state.starting_path_length; |
8454 |
+ if (*cp == '/') |
8455 |
+ /* Move past the slash between the ARGV element |
8456 |
+ and the rest of the pathname. But if the ARGV element |
8457 |
+ ends in a slash, we didn't add another, so we've |
8458 |
+ already skipped past it. */ |
8459 |
+ cp++; |
8460 |
+ } |
8461 |
+ else |
8462 |
+ { |
8463 |
+ cp = ""; |
8464 |
+ } |
8465 |
+ checked_print_quoted (dest, segment->text, cp); |
8466 |
+ break; |
8467 |
+ |
8468 |
+ case 's': /* size in bytes */ |
8469 |
+ /* UNTRUSTED, probably unexploitable */ |
8470 |
+ checked_fprintf (dest, segment->text, |
8471 |
+ human_readable ((uintmax_t) stat_buf->st_size, |
8472 |
+ hbuf, human_ceiling, 1, 1)); |
8473 |
+ break; |
8474 |
+ |
8475 |
+ case 'S': /* sparseness */ |
8476 |
+ /* UNTRUSTED, probably unexploitable */ |
8477 |
+ checked_fprintf (dest, segment->text, file_sparseness(stat_buf));; |
8478 |
+ break; |
8479 |
+ |
8480 |
+ case 't': /* mtime in `ctime' format */ |
8481 |
+ /* UNTRUSTED, probably unexploitable */ |
8482 |
+ checked_fprintf (dest, segment->text, |
8483 |
+ ctime_format (get_stat_mtime(stat_buf))); |
8484 |
+ break; |
8485 |
+ |
8486 |
+ case 'u': /* user name */ |
8487 |
+ /* trusted */ |
8488 |
+ /* (well, the actual user is selected by the user on systems |
8489 |
+ * where chown is not restricted, but the user name was |
8490 |
+ * selected by the system administrator) |
8491 |
+ */ |
8492 |
+ { |
8493 |
+ struct passwd *p; |
8494 |
+ |
8495 |
+ p = getpwuid (stat_buf->st_uid); |
8496 |
+ if (p) |
8497 |
+ { |
8498 |
+ segment->text[segment->text_len] = 's'; |
8499 |
+ checked_fprintf (dest, segment->text, p->pw_name); |
8500 |
+ break; |
8501 |
+ } |
8502 |
+ /* else fallthru */ |
8503 |
+ } |
8504 |
+ /* FALLTHROUGH*/ /* .. to case U */ |
8505 |
+ |
8506 |
+ case 'U': /* UID number */ |
8507 |
+ /* UNTRUSTED, probably unexploitable */ |
8508 |
+ checked_fprintf (dest, segment->text, |
8509 |
+ human_readable ((uintmax_t) stat_buf->st_uid, hbuf, |
8510 |
+ human_ceiling, 1, 1)); |
8511 |
+ break; |
8512 |
+ |
8513 |
+ /* %Y: type of file system entry like `ls -l`: |
8514 |
+ * (d,-,l,s,p,b,c,n) n=nonexistent(symlink) |
8515 |
+ */ |
8516 |
+ case 'Y': /* in case of symlink */ |
8517 |
+ /* trusted */ |
8518 |
+ { |
8519 |
+#ifdef S_ISLNK |
8520 |
+ if (S_ISLNK (stat_buf->st_mode)) |
8521 |
+ { |
8522 |
+ struct stat sbuf; |
8523 |
+ /* If we would normally follow links, do not do so. |
8524 |
+ * If we would normally not follow links, do so. |
8525 |
+ */ |
8526 |
+ if ((following_links() ? lstat : stat) |
8527 |
+ (state.rel_pathname, &sbuf) != 0) |
8528 |
+ { |
8529 |
+ if ( errno == ENOENT ) |
8530 |
+ { |
8531 |
+ checked_fprintf (dest, segment->text, "N"); |
8532 |
+ break; |
8533 |
+ } |
8534 |
+ else if ( errno == ELOOP ) |
8535 |
+ { |
8536 |
+ checked_fprintf (dest, segment->text, "L"); |
8537 |
+ break; |
8538 |
+ } |
8539 |
+ else |
8540 |
+ { |
8541 |
+ checked_fprintf (dest, segment->text, "?"); |
8542 |
+ error (0, errno, "%s", |
8543 |
+ safely_quote_err_filename(0, pathname)); |
8544 |
+ /* exit_status = 1; |
8545 |
+ return ; */ |
8546 |
+ break; |
8547 |
+ } |
8548 |
+ } |
8549 |
+ checked_fprintf (dest, segment->text, |
8550 |
+ mode_to_filetype(sbuf.st_mode & S_IFMT)); |
8551 |
+ } |
8552 |
+#endif /* S_ISLNK */ |
8553 |
+ else |
8554 |
+ { |
8555 |
+ checked_fprintf (dest, segment->text, |
8556 |
+ mode_to_filetype(stat_buf->st_mode & S_IFMT)); |
8557 |
+ } |
8558 |
+ } |
8559 |
+ break; |
8560 |
+ |
8561 |
+ case 'y': |
8562 |
+ /* trusted */ |
8563 |
+ { |
8564 |
+ checked_fprintf (dest, segment->text, |
8565 |
+ mode_to_filetype(stat_buf->st_mode & S_IFMT)); |
8566 |
+ } |
8567 |
+ break; |
8568 |
+ } |
8569 |
+ /* end of KIND_FORMAT case */ |
8570 |
+ break; |
8571 |
+ } |
8572 |
+} |
8573 |
+ |
8574 |
+boolean |
8575 |
+pred_fprintf (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8576 |
+{ |
8577 |
+ struct format_val *dest = &pred_ptr->args.printf_vec; |
8578 |
+ struct segment *segment; |
8579 |
+ |
8580 |
+ for (segment = dest->segment; segment; segment = segment->next) |
8581 |
+ { |
8582 |
+ if ( (KIND_FORMAT == segment->segkind) && segment->format_char[1]) /* Component of date. */ |
8583 |
+ { |
8584 |
+ struct timespec ts; |
8585 |
+ int valid = 0; |
8586 |
+ |
8587 |
+ switch (segment->format_char[0]) |
8588 |
+ { |
8589 |
+ case 'A': |
8590 |
+ ts = get_stat_atime(stat_buf); |
8591 |
+ valid = 1; |
8592 |
+ break; |
8593 |
+ case 'B': |
8594 |
+ ts = get_stat_birthtime(stat_buf); |
8595 |
+ if ('@' == segment->format_char[1]) |
8596 |
+ valid = 1; |
8597 |
+ else |
8598 |
+ valid = (ts.tv_nsec >= 0); |
8599 |
+ break; |
8600 |
+ case 'C': |
8601 |
+ ts = get_stat_ctime(stat_buf); |
8602 |
+ valid = 1; |
8603 |
+ break; |
8604 |
+ case 'T': |
8605 |
+ ts = get_stat_mtime(stat_buf); |
8606 |
+ valid = 1; |
8607 |
+ break; |
8608 |
+ default: |
8609 |
+ assert (0); |
8610 |
+ abort (); |
8611 |
+ } |
8612 |
+ /* We trust the output of format_date not to contain |
8613 |
+ * nasty characters, though the value of the date |
8614 |
+ * is itself untrusted data. |
8615 |
+ */ |
8616 |
+ if (valid) |
8617 |
+ { |
8618 |
+ /* trusted */ |
8619 |
+ checked_fprintf (dest, segment->text, |
8620 |
+ format_date (ts, segment->format_char[1])); |
8621 |
+ } |
8622 |
+ else |
8623 |
+ { |
8624 |
+ /* The specified timestamp is not available, output |
8625 |
+ * nothing for the timestamp, but use the rest (so that |
8626 |
+ * for example find foo -printf '[%Bs] %p\n' can print |
8627 |
+ * "[] foo"). |
8628 |
+ */ |
8629 |
+ /* trusted */ |
8630 |
+ checked_fprintf (dest, segment->text, ""); |
8631 |
+ } |
8632 |
+ } |
8633 |
+ else |
8634 |
+ { |
8635 |
+ /* Print a segment which is not a date. */ |
8636 |
+ do_fprintf(dest, segment, pathname, stat_buf); |
8637 |
+ } |
8638 |
+ } |
8639 |
+ return true; |
8640 |
+} |
8641 |
+ |
8642 |
+boolean |
8643 |
+pred_fstype (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8644 |
+{ |
8645 |
+ (void) pathname; |
8646 |
+ |
8647 |
+ if (strcmp (filesystem_type (stat_buf, pathname), pred_ptr->args.str) == 0) |
8648 |
+ return true; |
8649 |
+ else |
8650 |
+ return false; |
8651 |
+} |
8652 |
+ |
8653 |
+boolean |
8654 |
+pred_gid (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8655 |
+{ |
8656 |
+ (void) pathname; |
8657 |
+ |
8658 |
+ switch (pred_ptr->args.numinfo.kind) |
8659 |
+ { |
8660 |
+ case COMP_GT: |
8661 |
+ if (stat_buf->st_gid > pred_ptr->args.numinfo.l_val) |
8662 |
+ return (true); |
8663 |
+ break; |
8664 |
+ case COMP_LT: |
8665 |
+ if (stat_buf->st_gid < pred_ptr->args.numinfo.l_val) |
8666 |
+ return (true); |
8667 |
+ break; |
8668 |
+ case COMP_EQ: |
8669 |
+ if (stat_buf->st_gid == pred_ptr->args.numinfo.l_val) |
8670 |
+ return (true); |
8671 |
+ break; |
8672 |
+ } |
8673 |
+ return (false); |
8674 |
+} |
8675 |
+ |
8676 |
+boolean |
8677 |
+pred_group (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8678 |
+{ |
8679 |
+ (void) pathname; |
8680 |
+ |
8681 |
+ if (pred_ptr->args.gid == stat_buf->st_gid) |
8682 |
+ return (true); |
8683 |
+ else |
8684 |
+ return (false); |
8685 |
+} |
8686 |
+ |
8687 |
+boolean |
8688 |
+pred_ilname (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8689 |
+{ |
8690 |
+ return match_lname (pathname, stat_buf, pred_ptr, true); |
8691 |
+} |
8692 |
+ |
8693 |
+/* Common code between -name, -iname. PATHNAME is being visited, STR |
8694 |
+ is name to compare basename against, and FLAGS are passed to |
8695 |
+ fnmatch. Recall that 'find / -name /' is one of the few times where a '/' |
8696 |
+ in the -name must actually find something. */ |
8697 |
+static boolean |
8698 |
+pred_name_common (const char *pathname, const char *str, int flags) |
8699 |
+{ |
8700 |
+ char *p; |
8701 |
+ boolean b; |
8702 |
+ /* We used to use last_component() here, but that would not allow us to modify the |
8703 |
+ * input string, which is const. We could optimise by duplicating the string only |
8704 |
+ * if we need to modify it, and I'll do that if there is a measurable |
8705 |
+ * performance difference on a machine built after 1990... |
8706 |
+ */ |
8707 |
+ char *base = base_name (pathname); |
8708 |
+ /* remove trailing slashes, but leave "/" or "//foo" unchanged. */ |
8709 |
+ strip_trailing_slashes(base); |
8710 |
+ |
8711 |
+ /* FNM_PERIOD is not used here because POSIX requires that it not be. |
8712 |
+ * See http://standards.ieee.org/reading/ieee/interp/1003-2-92_int/pasc-1003.2-126.html |
8713 |
+ */ |
8714 |
+ b = fnmatch (str, base, flags) == 0; |
8715 |
+ free (base); |
8716 |
+ return b; |
8717 |
+} |
8718 |
+ |
8719 |
+boolean |
8720 |
+pred_iname (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8721 |
+{ |
8722 |
+ (void) stat_buf; |
8723 |
+ return pred_name_common (pathname, pred_ptr->args.str, FNM_CASEFOLD); |
8724 |
+} |
8725 |
+ |
8726 |
+boolean |
8727 |
+pred_inum (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8728 |
+{ |
8729 |
+ (void) pathname; |
8730 |
+ |
8731 |
+ switch (pred_ptr->args.numinfo.kind) |
8732 |
+ { |
8733 |
+ case COMP_GT: |
8734 |
+ if (stat_buf->st_ino > pred_ptr->args.numinfo.l_val) |
8735 |
+ return (true); |
8736 |
+ break; |
8737 |
+ case COMP_LT: |
8738 |
+ if (stat_buf->st_ino < pred_ptr->args.numinfo.l_val) |
8739 |
+ return (true); |
8740 |
+ break; |
8741 |
+ case COMP_EQ: |
8742 |
+ if (stat_buf->st_ino == pred_ptr->args.numinfo.l_val) |
8743 |
+ return (true); |
8744 |
+ break; |
8745 |
+ } |
8746 |
+ return (false); |
8747 |
+} |
8748 |
+ |
8749 |
+boolean |
8750 |
+pred_ipath (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8751 |
+{ |
8752 |
+ (void) stat_buf; |
8753 |
+ |
8754 |
+ if (fnmatch (pred_ptr->args.str, pathname, FNM_CASEFOLD) == 0) |
8755 |
+ return (true); |
8756 |
+ return (false); |
8757 |
+} |
8758 |
+ |
8759 |
+boolean |
8760 |
+pred_links (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8761 |
+{ |
8762 |
+ (void) pathname; |
8763 |
+ |
8764 |
+ switch (pred_ptr->args.numinfo.kind) |
8765 |
+ { |
8766 |
+ case COMP_GT: |
8767 |
+ if (stat_buf->st_nlink > pred_ptr->args.numinfo.l_val) |
8768 |
+ return (true); |
8769 |
+ break; |
8770 |
+ case COMP_LT: |
8771 |
+ if (stat_buf->st_nlink < pred_ptr->args.numinfo.l_val) |
8772 |
+ return (true); |
8773 |
+ break; |
8774 |
+ case COMP_EQ: |
8775 |
+ if (stat_buf->st_nlink == pred_ptr->args.numinfo.l_val) |
8776 |
+ return (true); |
8777 |
+ break; |
8778 |
+ } |
8779 |
+ return (false); |
8780 |
+} |
8781 |
+ |
8782 |
+boolean |
8783 |
+pred_lname (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8784 |
+{ |
8785 |
+ return match_lname (pathname, stat_buf, pred_ptr, false); |
8786 |
+} |
8787 |
+ |
8788 |
+static boolean |
8789 |
+match_lname (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr, boolean ignore_case) |
8790 |
+{ |
8791 |
+ boolean ret = false; |
8792 |
+#ifdef S_ISLNK |
8793 |
+ if (S_ISLNK (stat_buf->st_mode)) |
8794 |
+ { |
8795 |
+ char *linkname = get_link_name_at (pathname, state.cwd_dir_fd, state.rel_pathname); |
8796 |
+ if (linkname) |
8797 |
+ { |
8798 |
+ if (fnmatch (pred_ptr->args.str, linkname, |
8799 |
+ ignore_case ? FNM_CASEFOLD : 0) == 0) |
8800 |
+ ret = true; |
8801 |
+ free (linkname); |
8802 |
+ } |
8803 |
+ } |
8804 |
+#endif /* S_ISLNK */ |
8805 |
+ return ret; |
8806 |
+} |
8807 |
+ |
8808 |
+boolean |
8809 |
+pred_ls (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8810 |
+{ |
8811 |
+ return pred_fls(pathname, stat_buf, pred_ptr); |
8812 |
+} |
8813 |
+ |
8814 |
+boolean |
8815 |
+pred_mmin (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8816 |
+{ |
8817 |
+ (void) &pathname; |
8818 |
+ return pred_timewindow(get_stat_mtime(stat_buf), pred_ptr, 60); |
8819 |
+} |
8820 |
+ |
8821 |
+boolean |
8822 |
+pred_mtime (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8823 |
+{ |
8824 |
+ (void) pathname; |
8825 |
+ return pred_timewindow(get_stat_mtime(stat_buf), pred_ptr, DAYSECS); |
8826 |
+} |
8827 |
+ |
8828 |
+boolean |
8829 |
+pred_name (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8830 |
+{ |
8831 |
+ (void) stat_buf; |
8832 |
+ return pred_name_common (pathname, pred_ptr->args.str, 0); |
8833 |
+} |
8834 |
+ |
8835 |
+boolean |
8836 |
+pred_negate (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8837 |
+{ |
8838 |
+ return !apply_predicate(pathname, stat_buf, pred_ptr->pred_right); |
8839 |
+} |
8840 |
+ |
8841 |
+boolean |
8842 |
+pred_newer (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8843 |
+{ |
8844 |
+ (void) pathname; |
8845 |
+ |
8846 |
+ assert (COMP_GT == pred_ptr->args.reftime.kind); |
8847 |
+ return compare_ts(get_stat_mtime(stat_buf), pred_ptr->args.reftime.ts) > 0; |
8848 |
+} |
8849 |
+ |
8850 |
+boolean |
8851 |
+pred_newerXY (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8852 |
+{ |
8853 |
+ struct timespec ts; |
8854 |
+ boolean collected = false; |
8855 |
+ |
8856 |
+ assert (COMP_GT == pred_ptr->args.reftime.kind); |
8857 |
+ |
8858 |
+ switch (pred_ptr->args.reftime.xval) |
8859 |
+ { |
8860 |
+ case XVAL_TIME: |
8861 |
+ assert (pred_ptr->args.reftime.xval != XVAL_TIME); |
8862 |
+ return false; |
8863 |
+ |
8864 |
+ case XVAL_ATIME: |
8865 |
+ ts = get_stat_atime(stat_buf); |
8866 |
+ collected = true; |
8867 |
+ break; |
8868 |
+ |
8869 |
+ case XVAL_BIRTHTIME: |
8870 |
+ ts = get_stat_birthtime(stat_buf); |
8871 |
+ collected = true; |
8872 |
+ if (ts.tv_nsec < 0); |
8873 |
+ { |
8874 |
+ /* XXX: Cannot determine birth time. Warn once. */ |
8875 |
+ error(0, 0, _("Warning: cannot determine birth time of file %s"), |
8876 |
+ safely_quote_err_filename(0, pathname)); |
8877 |
+ return false; |
8878 |
+ } |
8879 |
+ break; |
8880 |
+ |
8881 |
+ case XVAL_CTIME: |
8882 |
+ ts = get_stat_ctime(stat_buf); |
8883 |
+ collected = true; |
8884 |
+ break; |
8885 |
+ |
8886 |
+ case XVAL_MTIME: |
8887 |
+ ts = get_stat_mtime(stat_buf); |
8888 |
+ collected = true; |
8889 |
+ break; |
8890 |
+ } |
8891 |
+ |
8892 |
+ assert (collected); |
8893 |
+ return compare_ts(ts, pred_ptr->args.reftime.ts) > 0; |
8894 |
+} |
8895 |
+ |
8896 |
+boolean |
8897 |
+pred_nogroup (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8898 |
+{ |
8899 |
+ (void) pathname; |
8900 |
+ (void) pred_ptr; |
8901 |
+ |
8902 |
+#ifdef CACHE_IDS |
8903 |
+ extern char *gid_unused; |
8904 |
+ |
8905 |
+ return gid_unused[(unsigned) stat_buf->st_gid]; |
8906 |
+#else |
8907 |
+ return getgrgid (stat_buf->st_gid) == NULL; |
8908 |
+#endif |
8909 |
+} |
8910 |
+ |
8911 |
+boolean |
8912 |
+pred_nouser (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8913 |
+{ |
8914 |
+#ifdef CACHE_IDS |
8915 |
+ extern char *uid_unused; |
8916 |
+#endif |
8917 |
+ |
8918 |
+ (void) pathname; |
8919 |
+ (void) pred_ptr; |
8920 |
+ |
8921 |
+#ifdef CACHE_IDS |
8922 |
+ return uid_unused[(unsigned) stat_buf->st_uid]; |
8923 |
+#else |
8924 |
+ return getpwuid (stat_buf->st_uid) == NULL; |
8925 |
+#endif |
8926 |
+} |
8927 |
+ |
8928 |
+ |
8929 |
+static boolean |
8930 |
+is_ok(const char *program, const char *arg) |
8931 |
+{ |
8932 |
+ fflush (stdout); |
8933 |
+ /* The draft open standard requires that, in the POSIX locale, |
8934 |
+ the last non-blank character of this prompt be '?'. |
8935 |
+ The exact format is not specified. |
8936 |
+ This standard does not have requirements for locales other than POSIX |
8937 |
+ */ |
8938 |
+ /* XXX: printing UNTRUSTED data here. */ |
8939 |
+ fprintf (stderr, _("< %s ... %s > ? " |
8940 |
+ /* TRANSLATORS: we would like, if possible, the final non-blank |
8941 |
+ * character of this string to be '?', but it is not |
8942 |
+ * wholly essential. */ |
8943 |
+ ), program, arg); |
8944 |
+ fflush (stderr); |
8945 |
+ return yesno(); |
8946 |
+} |
8947 |
+ |
8948 |
+boolean |
8949 |
+pred_ok (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8950 |
+{ |
8951 |
+ if (is_ok(pred_ptr->args.exec_vec.replace_vec[0], pathname)) |
8952 |
+ return new_impl_pred_exec (get_start_dirfd(), |
8953 |
+ pathname, stat_buf, pred_ptr, NULL, 0); |
8954 |
+ else |
8955 |
+ return false; |
8956 |
+} |
8957 |
+ |
8958 |
+boolean |
8959 |
+pred_okdir (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8960 |
+{ |
8961 |
+ const char *prefix = (state.rel_pathname[0] == '/') ? NULL : "./"; |
8962 |
+ if (is_ok(pred_ptr->args.exec_vec.replace_vec[0], pathname)) |
8963 |
+ return new_impl_pred_exec (get_current_dirfd(), |
8964 |
+ state.rel_pathname, stat_buf, pred_ptr, |
8965 |
+ prefix, (prefix ? 2 : 0)); |
8966 |
+ else |
8967 |
+ return false; |
8968 |
+} |
8969 |
+ |
8970 |
+boolean |
8971 |
+pred_openparen (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8972 |
+{ |
8973 |
+ (void) pathname; |
8974 |
+ (void) stat_buf; |
8975 |
+ (void) pred_ptr; |
8976 |
+ return true; |
8977 |
+} |
8978 |
+ |
8979 |
+boolean |
8980 |
+pred_or (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8981 |
+{ |
8982 |
+ if (pred_ptr->pred_left == NULL |
8983 |
+ || !apply_predicate(pathname, stat_buf, pred_ptr->pred_left)) |
8984 |
+ { |
8985 |
+ return apply_predicate(pathname, stat_buf, pred_ptr->pred_right); |
8986 |
+ } |
8987 |
+ else |
8988 |
+ return true; |
8989 |
+} |
8990 |
+ |
8991 |
+boolean |
8992 |
+pred_path (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
8993 |
+{ |
8994 |
+ (void) stat_buf; |
8995 |
+ if (fnmatch (pred_ptr->args.str, pathname, 0) == 0) |
8996 |
+ return (true); |
8997 |
+ return (false); |
8998 |
+} |
8999 |
+ |
9000 |
+boolean |
9001 |
+pred_perm (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
9002 |
+{ |
9003 |
+ mode_t mode = stat_buf->st_mode; |
9004 |
+ mode_t perm_val = pred_ptr->args.perm.val[S_ISDIR (mode) != 0]; |
9005 |
+ (void) pathname; |
9006 |
+ switch (pred_ptr->args.perm.kind) |
9007 |
+ { |
9008 |
+ case PERM_AT_LEAST: |
9009 |
+ return (mode & perm_val) == perm_val; |
9010 |
+ break; |
9011 |
+ |
9012 |
+ case PERM_ANY: |
9013 |
+ /* True if any of the bits set in the mask are also set in the file's mode. |
9014 |
+ * |
9015 |
+ * |
9016 |
+ * Otherwise, if onum is prefixed by a hyphen, the primary shall |
9017 |
+ * evaluate as true if at least all of the bits specified in |
9018 |
+ * onum that are also set in the octal mask 07777 are set. |
9019 |
+ * |
9020 |
+ * Eric Blake's interpretation is that the mode argument is zero, |
9021 |
+ |
9022 |
+ */ |
9023 |
+ if (0 == perm_val) |
9024 |
+ return true; /* Savannah bug 14748; we used to return false */ |
9025 |
+ else |
9026 |
+ return (mode & perm_val) != 0; |
9027 |
+ break; |
9028 |
+ |
9029 |
+ case PERM_EXACT: |
9030 |
+ return (mode & MODE_ALL) == perm_val; |
9031 |
+ break; |
9032 |
+ |
9033 |
+ default: |
9034 |
+ abort (); |
9035 |
+ break; |
9036 |
+ } |
9037 |
+} |
9038 |
+ |
9039 |
+ |
9040 |
+struct access_check_args |
9041 |
+{ |
9042 |
+ const char *filename; |
9043 |
+ int access_type; |
9044 |
+ int cb_errno; |
9045 |
+}; |
9046 |
+ |
9047 |
+ |
9048 |
+static int |
9049 |
+access_callback(void *context) |
9050 |
+{ |
9051 |
+ int rv; |
9052 |
+ struct access_check_args *args = context; |
9053 |
+ if ((rv = access(args->filename, args->access_type)) < 0) |
9054 |
+ args->cb_errno = errno; |
9055 |
+ return rv; |
9056 |
+} |
9057 |
+ |
9058 |
+static int |
9059 |
+can_access(int access_type) |
9060 |
+{ |
9061 |
+ struct access_check_args args; |
9062 |
+ args.filename = state.rel_pathname; |
9063 |
+ args.access_type = access_type; |
9064 |
+ args.cb_errno = 0; |
9065 |
+ return 0 == run_in_dir(state.cwd_dir_fd, access_callback, &args); |
9066 |
+} |
9067 |
+ |
9068 |
+ |
9069 |
+boolean |
9070 |
+pred_executable (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
9071 |
+{ |
9072 |
+ (void) pathname; |
9073 |
+ (void) stat_buf; |
9074 |
+ (void) pred_ptr; |
9075 |
+ |
9076 |
+ return can_access(X_OK); |
9077 |
+} |
9078 |
+ |
9079 |
+boolean |
9080 |
+pred_readable (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
9081 |
+{ |
9082 |
+ (void) pathname; |
9083 |
+ (void) stat_buf; |
9084 |
+ (void) pred_ptr; |
9085 |
+ |
9086 |
+ return can_access(R_OK); |
9087 |
+} |
9088 |
+ |
9089 |
+boolean |
9090 |
+pred_writable (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
9091 |
+{ |
9092 |
+ (void) pathname; |
9093 |
+ (void) stat_buf; |
9094 |
+ (void) pred_ptr; |
9095 |
+ |
9096 |
+ return can_access(W_OK); |
9097 |
+} |
9098 |
+ |
9099 |
+boolean |
9100 |
+pred_print (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
9101 |
+{ |
9102 |
+ (void) stat_buf; |
9103 |
+ (void) pred_ptr; |
9104 |
+ |
9105 |
+ print_quoted(pred_ptr->args.printf_vec.stream, |
9106 |
+ pred_ptr->args.printf_vec.quote_opts, |
9107 |
+ pred_ptr->args.printf_vec.dest_is_tty, |
9108 |
+ "%s\n", pathname); |
9109 |
+ return true; |
9110 |
+} |
9111 |
+ |
9112 |
+boolean |
9113 |
+pred_print0 (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
9114 |
+{ |
9115 |
+ return pred_fprint0(pathname, stat_buf, pred_ptr); |
9116 |
+} |
9117 |
+ |
9118 |
+boolean |
9119 |
+pred_prune (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
9120 |
+{ |
9121 |
+ (void) pathname; |
9122 |
+ (void) pred_ptr; |
9123 |
+ |
9124 |
+ if (options.do_dir_first == true && /* no effect with -depth */ |
9125 |
+ stat_buf != NULL && |
9126 |
+ S_ISDIR(stat_buf->st_mode)) |
9127 |
+ state.stop_at_current_level = true; |
9128 |
+ |
9129 |
+ /* findutils used to return options.do_dir_first here, so that -prune |
9130 |
+ * returns true only if -depth is not in effect. But POSIX requires |
9131 |
+ * that -prune always evaluate as true. |
9132 |
+ */ |
9133 |
+ return true; |
9134 |
+} |
9135 |
+ |
9136 |
+boolean |
9137 |
+pred_quit (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
9138 |
+{ |
9139 |
+ (void) pathname; |
9140 |
+ (void) stat_buf; |
9141 |
+ (void) pred_ptr; |
9142 |
+ |
9143 |
+ /* Run any cleanups. This includes executing any command lines |
9144 |
+ * we have partly built but not executed. |
9145 |
+ */ |
9146 |
+ cleanup(); |
9147 |
+ |
9148 |
+ /* Since -exec and friends don't leave child processes running in the |
9149 |
+ * background, there is no need to wait for them here. |
9150 |
+ */ |
9151 |
+ exit(state.exit_status); /* 0 for success, etc. */ |
9152 |
+} |
9153 |
+ |
9154 |
+boolean |
9155 |
+pred_regex (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
9156 |
+{ |
9157 |
+ int len = strlen (pathname); |
9158 |
+(void) stat_buf; |
9159 |
+ if (re_match (pred_ptr->args.regex, pathname, len, 0, |
9160 |
+ (struct re_registers *) NULL) == len) |
9161 |
+ return (true); |
9162 |
+ return (false); |
9163 |
+} |
9164 |
+ |
9165 |
+boolean |
9166 |
+pred_size (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
9167 |
+{ |
9168 |
+ uintmax_t f_val; |
9169 |
+ |
9170 |
+ (void) pathname; |
9171 |
+ f_val = ((stat_buf->st_size / pred_ptr->args.size.blocksize) |
9172 |
+ + (stat_buf->st_size % pred_ptr->args.size.blocksize != 0)); |
9173 |
+ switch (pred_ptr->args.size.kind) |
9174 |
+ { |
9175 |
+ case COMP_GT: |
9176 |
+ if (f_val > pred_ptr->args.size.size) |
9177 |
+ return (true); |
9178 |
+ break; |
9179 |
+ case COMP_LT: |
9180 |
+ if (f_val < pred_ptr->args.size.size) |
9181 |
+ return (true); |
9182 |
+ break; |
9183 |
+ case COMP_EQ: |
9184 |
+ if (f_val == pred_ptr->args.size.size) |
9185 |
+ return (true); |
9186 |
+ break; |
9187 |
+ } |
9188 |
+ return (false); |
9189 |
+} |
9190 |
+ |
9191 |
+boolean |
9192 |
+pred_samefile (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
9193 |
+{ |
9194 |
+ /* Potential optimisation: because of the loop protection, we always |
9195 |
+ * know the device of the current directory, hence the device number |
9196 |
+ * of the file we're currently considering. If -L is not in effect, |
9197 |
+ * and the device number of the file we're looking for is not the |
9198 |
+ * same as the device number of the current directory, this |
9199 |
+ * predicate cannot return true. Hence there would be no need to |
9200 |
+ * stat the file we're looking at. |
9201 |
+ */ |
9202 |
+ (void) pathname; |
9203 |
+ |
9204 |
+ /* We will often still have an fd open on the file under consideration, |
9205 |
+ * but that's just to ensure inode number stability by maintaining |
9206 |
+ * a reference to it; we don't need the file for anything else. |
9207 |
+ */ |
9208 |
+ return stat_buf->st_ino == pred_ptr->args.samefileid.ino |
9209 |
+ && stat_buf->st_dev == pred_ptr->args.samefileid.dev; |
9210 |
+} |
9211 |
+ |
9212 |
+boolean |
9213 |
+pred_true (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
9214 |
+{ |
9215 |
+ (void) pathname; |
9216 |
+ (void) stat_buf; |
9217 |
+ (void) pred_ptr; |
9218 |
+ return true; |
9219 |
+} |
9220 |
+ |
9221 |
+boolean |
9222 |
+pred_type (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
9223 |
+{ |
9224 |
+ mode_t mode; |
9225 |
+ mode_t type = pred_ptr->args.type; |
9226 |
+ |
9227 |
+ assert (state.have_type); |
9228 |
+ |
9229 |
+ if (0 == state.type) |
9230 |
+ { |
9231 |
+ /* This can sometimes happen with broken NFS servers. |
9232 |
+ * See Savannah bug #16378. |
9233 |
+ */ |
9234 |
+ return false; |
9235 |
+ } |
9236 |
+ |
9237 |
+ (void) pathname; |
9238 |
+ |
9239 |
+ if (state.have_stat) |
9240 |
+ mode = stat_buf->st_mode; |
9241 |
+ else |
9242 |
+ mode = state.type; |
9243 |
+ |
9244 |
+#ifndef S_IFMT |
9245 |
+ /* POSIX system; check `mode' the slow way. */ |
9246 |
+ if ((S_ISBLK (mode) && type == S_IFBLK) |
9247 |
+ || (S_ISCHR (mode) && type == S_IFCHR) |
9248 |
+ || (S_ISDIR (mode) && type == S_IFDIR) |
9249 |
+ || (S_ISREG (mode) && type == S_IFREG) |
9250 |
+#ifdef S_IFLNK |
9251 |
+ || (S_ISLNK (mode) && type == S_IFLNK) |
9252 |
+#endif |
9253 |
+#ifdef S_IFIFO |
9254 |
+ || (S_ISFIFO (mode) && type == S_IFIFO) |
9255 |
+#endif |
9256 |
+#ifdef S_IFSOCK |
9257 |
+ || (S_ISSOCK (mode) && type == S_IFSOCK) |
9258 |
+#endif |
9259 |
+#ifdef S_IFDOOR |
9260 |
+ || (S_ISDOOR (mode) && type == S_IFDOOR) |
9261 |
+#endif |
9262 |
+ ) |
9263 |
+#else /* S_IFMT */ |
9264 |
+ /* Unix system; check `mode' the fast way. */ |
9265 |
+ if ((mode & S_IFMT) == type) |
9266 |
+#endif /* S_IFMT */ |
9267 |
+ return (true); |
9268 |
+ else |
9269 |
+ return (false); |
9270 |
+} |
9271 |
+ |
9272 |
+boolean |
9273 |
+pred_uid (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
9274 |
+{ |
9275 |
+ (void) pathname; |
9276 |
+ switch (pred_ptr->args.numinfo.kind) |
9277 |
+ { |
9278 |
+ case COMP_GT: |
9279 |
+ if (stat_buf->st_uid > pred_ptr->args.numinfo.l_val) |
9280 |
+ return (true); |
9281 |
+ break; |
9282 |
+ case COMP_LT: |
9283 |
+ if (stat_buf->st_uid < pred_ptr->args.numinfo.l_val) |
9284 |
+ return (true); |
9285 |
+ break; |
9286 |
+ case COMP_EQ: |
9287 |
+ if (stat_buf->st_uid == pred_ptr->args.numinfo.l_val) |
9288 |
+ return (true); |
9289 |
+ break; |
9290 |
+ } |
9291 |
+ return (false); |
9292 |
+} |
9293 |
+ |
9294 |
+boolean |
9295 |
+pred_used (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
9296 |
+{ |
9297 |
+ struct timespec delta, at, ct; |
9298 |
+ |
9299 |
+ (void) pathname; |
9300 |
+ |
9301 |
+ /* TODO: this needs to be retested carefully (manually, if necessary) */ |
9302 |
+ at = get_stat_atime(stat_buf); |
9303 |
+ ct = get_stat_ctime(stat_buf); |
9304 |
+ delta.tv_sec = at.tv_sec - ct.tv_sec; |
9305 |
+ delta.tv_nsec = at.tv_nsec - ct.tv_nsec; |
9306 |
+ if (delta.tv_nsec < 0) |
9307 |
+ { |
9308 |
+ delta.tv_nsec += 1000000000; |
9309 |
+ delta.tv_sec -= 1; |
9310 |
+ } |
9311 |
+ return pred_timewindow(delta, pred_ptr, DAYSECS); |
9312 |
+} |
9313 |
+ |
9314 |
+boolean |
9315 |
+pred_user (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
9316 |
+{ |
9317 |
+ (void) pathname; |
9318 |
+ if (pred_ptr->args.uid == stat_buf->st_uid) |
9319 |
+ return (true); |
9320 |
+ else |
9321 |
+ return (false); |
9322 |
+} |
9323 |
+ |
9324 |
+boolean |
9325 |
+pred_xtype (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) |
9326 |
+{ |
9327 |
+ struct stat sbuf; /* local copy, not stat_buf because we're using a different stat method */ |
9328 |
+ int (*ystat) (const char*, struct stat *p); |
9329 |
+ |
9330 |
+ /* If we would normally stat the link itself, stat the target instead. |
9331 |
+ * If we would normally follow the link, stat the link itself instead. |
9332 |
+ */ |
9333 |
+ if (following_links()) |
9334 |
+ ystat = optionp_stat; |
9335 |
+ else |
9336 |
+ ystat = optionl_stat; |
9337 |
+ |
9338 |
+ set_stat_placeholders(&sbuf); |
9339 |
+ if ((*ystat) (state.rel_pathname, &sbuf) != 0) |
9340 |
+ { |
9341 |
+ if (following_links() && errno == ENOENT) |
9342 |
+ { |
9343 |
+ /* If we failed to follow the symlink, |
9344 |
+ * fall back on looking at the symlink itself. |
9345 |
+ */ |
9346 |
+ /* Mimic behavior of ls -lL. */ |
9347 |
+ return (pred_type (pathname, stat_buf, pred_ptr)); |
9348 |
+ } |
9349 |
+ else |
9350 |
+ { |
9351 |
+ error (0, errno, "%s", safely_quote_err_filename(0, pathname)); |
9352 |
+ state.exit_status = 1; |
9353 |
+ } |
9354 |
+ return false; |
9355 |
+ } |
9356 |
+ /* Now that we have our stat() information, query it in the same |
9357 |
+ * way that -type does. |
9358 |
+ */ |
9359 |
+ return (pred_type (pathname, &sbuf, pred_ptr)); |
9360 |
+} |
9361 |
+ |
9362 |
+/* 1) fork to get a child; parent remembers the child pid |
9363 |
+ 2) child execs the command requested |
9364 |
+ 3) parent waits for child; checks for proper pid of child |
9365 |
+ |
9366 |
+ Possible returns: |
9367 |
+ |
9368 |
+ ret errno status(h) status(l) |
9369 |
+ |
9370 |
+ pid x signal# 0177 stopped |
9371 |
+ pid x exit arg 0 term by _exit |
9372 |
+ pid x 0 signal # term by signal |
9373 |
+ -1 EINTR parent got signal |
9374 |
+ -1 other some other kind of error |
9375 |
+ |
9376 |
+ Return true only if the pid matches, status(l) is |
9377 |
+ zero, and the exit arg (status high) is 0. |
9378 |
+ Otherwise return false, possibly printing an error message. */ |
9379 |
+ |
9380 |
+ |
9381 |
+static boolean |
9382 |
+prep_child_for_exec (boolean close_stdin, int dirfd) |
9383 |
+{ |
9384 |
+ boolean ok = true; |
9385 |
+ if (close_stdin) |
9386 |
+ { |
9387 |
+ const char inputfile[] = "/dev/null"; |
9388 |
+ |
9389 |
+ if (close(0) < 0) |
9390 |
+ { |
9391 |
+ error(0, errno, _("Cannot close standard input")); |
9392 |
+ ok = false; |
9393 |
+ } |
9394 |
+ else |
9395 |
+ { |
9396 |
+ if (open(inputfile, O_RDONLY |
9397 |
+#if defined O_LARGEFILE |
9398 |
+ |O_LARGEFILE |
9399 |
+#endif |
9400 |
+ ) < 0) |
9401 |
+ { |
9402 |
+ /* This is not entirely fatal, since |
9403 |
+ * executing the child with a closed |
9404 |
+ * stdin is almost as good as executing it |
9405 |
+ * with its stdin attached to /dev/null. |
9406 |
+ */ |
9407 |
+ error (0, errno, "%s", safely_quote_err_filename(0, inputfile)); |
9408 |
+ /* do not set ok=false, it is OK to continue anyway. */ |
9409 |
+ } |
9410 |
+ } |
9411 |
+ } |
9412 |
+ |
9413 |
+ /* Even if DebugSearch is set, don't announce our change of |
9414 |
+ * directory, since we're not going to emit a subsequent |
9415 |
+ * announcement of a call to stat() anyway, as we're about to exec |
9416 |
+ * something. |
9417 |
+ */ |
9418 |
+ if (dirfd != AT_FDCWD) |
9419 |
+ { |
9420 |
+ assert (dirfd >= 0); |
9421 |
+ if (0 != fchdir(dirfd)) |
9422 |
+ { |
9423 |
+ /* If we cannot execute our command in the correct directory, |
9424 |
+ * we should not execute it at all. |
9425 |
+ */ |
9426 |
+ error(0, errno, _("Failed to change directory")); |
9427 |
+ ok = false; |
9428 |
+ } |
9429 |
+ } |
9430 |
+ return ok; |
9431 |
+} |
9432 |
+ |
9433 |
+ |
9434 |
+ |
9435 |
+int |
9436 |
+launch (const struct buildcmd_control *ctl, |
9437 |
+ struct buildcmd_state *buildstate) |
9438 |
+{ |
9439 |
+ int wait_status; |
9440 |
+ pid_t child_pid; |
9441 |
+ static int first_time = 1; |
9442 |
+ const struct exec_val *execp = buildstate->usercontext; |
9443 |
+ |
9444 |
+ if (!execp->use_current_dir) |
9445 |
+ { |
9446 |
+ assert (starting_desc >= 0); |
9447 |
+ assert (execp->dirfd == starting_desc); |
9448 |
+ } |
9449 |
+ |
9450 |
+ |
9451 |
+ /* Null terminate the arg list. */ |
9452 |
+ bc_push_arg (ctl, buildstate, (char *) NULL, 0, NULL, 0, false); |
9453 |
+ |
9454 |
+ /* Make sure output of command doesn't get mixed with find output. */ |
9455 |
+ fflush (stdout); |
9456 |
+ fflush (stderr); |
9457 |
+ |
9458 |
+ /* Make sure to listen for the kids. */ |
9459 |
+ if (first_time) |
9460 |
+ { |
9461 |
+ first_time = 0; |
9462 |
+ signal (SIGCHLD, SIG_DFL); |
9463 |
+ } |
9464 |
+ |
9465 |
+ child_pid = fork (); |
9466 |
+ if (child_pid == -1) |
9467 |
+ error (1, errno, _("cannot fork")); |
9468 |
+ if (child_pid == 0) |
9469 |
+ { |
9470 |
+ /* We are the child. */ |
9471 |
+ assert (starting_desc >= 0); |
9472 |
+ if (!prep_child_for_exec(execp->close_stdin, execp->dirfd)) |
9473 |
+ { |
9474 |
+ _exit(1); |
9475 |
+ } |
9476 |
+ |
9477 |
+ execvp (buildstate->cmd_argv[0], buildstate->cmd_argv); |
9478 |
+ error (0, errno, "%s", |
9479 |
+ safely_quote_err_filename(0, buildstate->cmd_argv[0])); |
9480 |
+ _exit (1); |
9481 |
+ } |
9482 |
+ |
9483 |
+ |
9484 |
+ /* In parent; set up for next time. */ |
9485 |
+ bc_clear_args(ctl, buildstate); |
9486 |
+ |
9487 |
+ |
9488 |
+ while (waitpid (child_pid, &wait_status, 0) == (pid_t) -1) |
9489 |
+ { |
9490 |
+ if (errno != EINTR) |
9491 |
+ { |
9492 |
+ error (0, errno, _("error waiting for %s"), |
9493 |
+ safely_quote_err_filename(0, buildstate->cmd_argv[0])); |
9494 |
+ state.exit_status = 1; |
9495 |
+ return 0; /* FAIL */ |
9496 |
+ } |
9497 |
+ } |
9498 |
+ |
9499 |
+ if (WIFSIGNALED (wait_status)) |
9500 |
+ { |
9501 |
+ error (0, 0, _("%1$s terminated by signal %2$d"), |
9502 |
+ quotearg_n_style(0, options.err_quoting_style, |
9503 |
+ buildstate->cmd_argv[0]), |
9504 |
+ WTERMSIG (wait_status)); |
9505 |
+ |
9506 |
+ if (execp->multiple) |
9507 |
+ { |
9508 |
+ /* -exec \; just returns false if the invoked command fails. |
9509 |
+ * -exec {} + returns true if the invoked command fails, but |
9510 |
+ * sets the program exit status. |
9511 |
+ */ |
9512 |
+ state.exit_status = 1; |
9513 |
+ } |
9514 |
+ |
9515 |
+ return 1; /* OK */ |
9516 |
+ } |
9517 |
+ |
9518 |
+ if (0 == WEXITSTATUS (wait_status)) |
9519 |
+ { |
9520 |
+ return 1; /* OK */ |
9521 |
+ } |
9522 |
+ else |
9523 |
+ { |
9524 |
+ if (execp->multiple) |
9525 |
+ { |
9526 |
+ /* -exec \; just returns false if the invoked command fails. |
9527 |
+ * -exec {} + returns true if the invoked command fails, but |
9528 |
+ * sets the program exit status. |
9529 |
+ */ |
9530 |
+ state.exit_status = 1; |
9531 |
+ } |
9532 |
+ return 0; /* FAIL */ |
9533 |
+ } |
9534 |
+ |
9535 |
+} |
9536 |
+ |
9537 |
+ |
9538 |
+/* Return a static string formatting the time WHEN according to the |
9539 |
+ * strftime format character KIND. |
9540 |
+ * |
9541 |
+ * This function contains a number of assertions. These look like |
9542 |
+ * runtime checks of the results of computations, which would be a |
9543 |
+ * problem since external events should not be tested for with |
9544 |
+ * "assert" (instead you should use "if"). However, they are not |
9545 |
+ * really runtime checks. The assertions actually exist to verify |
9546 |
+ * that the various buffers are correctly sized. |
9547 |
+ */ |
9548 |
+static char * |
9549 |
+format_date (struct timespec ts, int kind) |
9550 |
+{ |
9551 |
+ /* In theory, we use an extra 10 characters for 9 digits of |
9552 |
+ * nanoseconds and 1 for the decimal point. However, the real |
9553 |
+ * world is more complex than that. |
9554 |
+ * |
9555 |
+ * For example, some systems return junk in the tv_nsec part of |
9556 |
+ * st_birthtime. An example of this is the NetBSD-4.0-RELENG kernel |
9557 |
+ * (at Sat Mar 24 18:46:46 2007) running a NetBSD-3.1-RELEASE |
9558 |
+ * runtime and examining files on an msdos filesytem. So for that |
9559 |
+ * reason we set NS_BUF_LEN to 32, which is simply "long enough" as |
9560 |
+ * opposed to "exactly the right size". Note that the behaviour of |
9561 |
+ * NetBSD appears to be a result of the use of uninitialised data, |
9562 |
+ * as it's not 100% reproducible (more like 25%). |
9563 |
+ */ |
9564 |
+ enum { |
9565 |
+ NS_BUF_LEN = 32, |
9566 |
+ DATE_LEN_PERCENT_APLUS=21 /* length of result of %A+ (it's longer than %c)*/ |
9567 |
+ }; |
9568 |
+ static char buf[128u+10u + MAX(DATE_LEN_PERCENT_APLUS, |
9569 |
+ MAX (LONGEST_HUMAN_READABLE + 2, NS_BUF_LEN+64+200))]; |
9570 |
+ char ns_buf[NS_BUF_LEN]; /* -.9999999990 (- sign can happen!)*/ |
9571 |
+ int charsprinted, need_ns_suffix; |
9572 |
+ struct tm *tm; |
9573 |
+ char fmt[6]; |
9574 |
+ |
9575 |
+ /* human_readable() assumes we pass a buffer which is at least as |
9576 |
+ * long as LONGEST_HUMAN_READABLE. We use an assertion here to |
9577 |
+ * ensure that no nasty unsigned overflow happend in our calculation |
9578 |
+ * of the size of buf. Do the assertion here rather than in the |
9579 |
+ * code for %@ so that we find the problem quickly if it exists. If |
9580 |
+ * you want to submit a patch to move this into the if statement, go |
9581 |
+ * ahead, I'll apply it. But include performance timings |
9582 |
+ * demonstrating that the performance difference is actually |
9583 |
+ * measurable. |
9584 |
+ */ |
9585 |
+ verify (sizeof(buf) >= LONGEST_HUMAN_READABLE); |
9586 |
+ |
9587 |
+ charsprinted = 0; |
9588 |
+ need_ns_suffix = 0; |
9589 |
+ |
9590 |
+ /* Format the main part of the time. */ |
9591 |
+ if (kind == '+') |
9592 |
+ { |
9593 |
+ strcpy (fmt, "%F+%T"); |
9594 |
+ need_ns_suffix = 1; |
9595 |
+ } |
9596 |
+ else |
9597 |
+ { |
9598 |
+ fmt[0] = '%'; |
9599 |
+ fmt[1] = kind; |
9600 |
+ fmt[2] = '\0'; |
9601 |
+ |
9602 |
+ /* %a, %c, and %t are handled in ctime_format() */ |
9603 |
+ switch (kind) |
9604 |
+ { |
9605 |
+ case 'S': |
9606 |
+ case 'T': |
9607 |
+ case 'X': |
9608 |
+ case '@': |
9609 |
+ need_ns_suffix = 1; |
9610 |
+ break; |
9611 |
+ default: |
9612 |
+ need_ns_suffix = 0; |
9613 |
+ break; |
9614 |
+ } |
9615 |
+ } |
9616 |
+ |
9617 |
+ if (need_ns_suffix) |
9618 |
+ { |
9619 |
+ /* Format the nanoseconds part. Leave a trailing zero to |
9620 |
+ * discourage people from writing scripts which extract the |
9621 |
+ * fractional part of the timestamp by using column offsets. |
9622 |
+ * The reason for discouraging this is that in the future, the |
9623 |
+ * granularity may not be nanoseconds. |
9624 |
+ */ |
9625 |
+ ns_buf[0] = 0; |
9626 |
+ charsprinted = snprintf(ns_buf, NS_BUF_LEN, ".%09ld0", (long int)ts.tv_nsec); |
9627 |
+ assert (charsprinted < NS_BUF_LEN); |
9628 |
+ } |
9629 |
+ |
9630 |
+ if (kind != '@' |
9631 |
+ && (tm = localtime (&ts.tv_sec)) |
9632 |
+ && strftime (buf, sizeof buf, fmt, tm)) |
9633 |
+ { |
9634 |
+ /* For %AS, %CS, %TS, add the fractional part of the seconds |
9635 |
+ * information. |
9636 |
+ */ |
9637 |
+ if (need_ns_suffix) |
9638 |
+ { |
9639 |
+ assert ((sizeof buf - strlen(buf)) > strlen(ns_buf)); |
9640 |
+ strcat(buf, ns_buf); |
9641 |
+ } |
9642 |
+ return buf; |
9643 |
+ } |
9644 |
+ else |
9645 |
+ { |
9646 |
+ uintmax_t w = ts.tv_sec; |
9647 |
+ size_t used, len, remaining; |
9648 |
+ |
9649 |
+ /* XXX: note that we are negating an unsigned type which is the |
9650 |
+ * widest possible unsigned type. |
9651 |
+ */ |
9652 |
+ char *p = human_readable (ts.tv_sec < 0 ? -w : w, buf + 1, |
9653 |
+ human_ceiling, 1, 1); |
9654 |
+ assert (p > buf); |
9655 |
+ assert (p < (buf + (sizeof buf))); |
9656 |
+ if (ts.tv_sec < 0) |
9657 |
+ *--p = '-'; /* XXX: Ugh, relying on internal details of human_readable(). */ |
9658 |
+ |
9659 |
+ /* Add the nanoseconds part. Because we cannot enforce a |
9660 |
+ * particlar implementation of human_readable, we cannot assume |
9661 |
+ * any particular value for (p-buf). So we need to be careful |
9662 |
+ * that there is enough space remaining in the buffer. |
9663 |
+ */ |
9664 |
+ if (need_ns_suffix) |
9665 |
+ { |
9666 |
+ len = strlen(p); |
9667 |
+ used = (p-buf) + len; /* Offset into buf of current end */ |
9668 |
+ assert (sizeof buf > used); /* Ensure we can perform subtraction safely. */ |
9669 |
+ remaining = sizeof buf - used - 1u; /* allow space for NUL */ |
9670 |
+ |
9671 |
+ if (strlen(ns_buf) >= remaining) |
9672 |
+ { |
9673 |
+ error(0, 0, |
9674 |
+ "charsprinted=%ld but remaining=%lu: ns_buf=%s", |
9675 |
+ (long)charsprinted, (unsigned long)remaining, ns_buf); |
9676 |
+ } |
9677 |
+ assert (strlen(ns_buf) < remaining); |
9678 |
+ strcat(p, ns_buf); |
9679 |
+ } |
9680 |
+ return p; |
9681 |
+ } |
9682 |
+} |
9683 |
+ |
9684 |
+static const char *weekdays[] = |
9685 |
+ { |
9686 |
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" |
9687 |
+ }; |
9688 |
+static char * months[] = |
9689 |
+ { |
9690 |
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun", |
9691 |
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" |
9692 |
+ }; |
9693 |
+ |
9694 |
+ |
9695 |
+static char * |
9696 |
+ctime_format (struct timespec ts) |
9697 |
+{ |
9698 |
+ const struct tm * ptm; |
9699 |
+#define TIME_BUF_LEN 1024u |
9700 |
+ static char resultbuf[TIME_BUF_LEN]; |
9701 |
+ int nout; |
9702 |
+ |
9703 |
+ ptm = localtime(&ts.tv_sec); |
9704 |
+ if (ptm) |
9705 |
+ { |
9706 |
+ assert (ptm->tm_wday >= 0); |
9707 |
+ assert (ptm->tm_wday < 7); |
9708 |
+ assert (ptm->tm_mon >= 0); |
9709 |
+ assert (ptm->tm_mon < 12); |
9710 |
+ assert (ptm->tm_hour >= 0); |
9711 |
+ assert (ptm->tm_hour < 24); |
9712 |
+ assert (ptm->tm_min < 60); |
9713 |
+ assert (ptm->tm_sec <= 61); /* allows 2 leap seconds. */ |
9714 |
+ |
9715 |
+ /* wkday mon mday hh:mm:ss.nnnnnnnnn yyyy */ |
9716 |
+ nout = snprintf(resultbuf, TIME_BUF_LEN, |
9717 |
+ "%3s %3s %2d %02d:%02d:%02d.%010ld %04d", |
9718 |
+ weekdays[ptm->tm_wday], |
9719 |
+ months[ptm->tm_mon], |
9720 |
+ ptm->tm_mday, |
9721 |
+ ptm->tm_hour, |
9722 |
+ ptm->tm_min, |
9723 |
+ ptm->tm_sec, |
9724 |
+ (long int)ts.tv_nsec, |
9725 |
+ 1900 + ptm->tm_year); |
9726 |
+ |
9727 |
+ assert (nout < TIME_BUF_LEN); |
9728 |
+ return resultbuf; |
9729 |
+ } |
9730 |
+ else |
9731 |
+ { |
9732 |
+ /* The time cannot be represented as a struct tm. |
9733 |
+ Output it as an integer. */ |
9734 |
+ return format_date (ts, '@'); |
9735 |
+ } |
9736 |
+} |
9737 |
+ |
9738 |
+/* Copy STR into BUF and trim blanks from the end of BUF. |
9739 |
+ Return BUF. */ |
9740 |
+ |
9741 |
+static char * |
9742 |
+blank_rtrim (str, buf) |
9743 |
+ char *str; |
9744 |
+ char *buf; |
9745 |
+{ |
9746 |
+ int i; |
9747 |
+ |
9748 |
+ if (str == NULL) |
9749 |
+ return (NULL); |
9750 |
+ strcpy (buf, str); |
9751 |
+ i = strlen (buf) - 1; |
9752 |
+ while ((i >= 0) && ((buf[i] == ' ') || buf[i] == '\t')) |
9753 |
+ i--; |
9754 |
+ buf[++i] = '\0'; |
9755 |
+ return (buf); |
9756 |
+} |
9757 |
+ |
9758 |
+/* Print out the predicate list starting at NODE. */ |
9759 |
+void |
9760 |
+print_list (FILE *fp, struct predicate *node) |
9761 |
+{ |
9762 |
+ struct predicate *cur; |
9763 |
+ char name[256]; |
9764 |
+ |
9765 |
+ cur = node; |
9766 |
+ while (cur != NULL) |
9767 |
+ { |
9768 |
+ fprintf (fp, "[%s] ", blank_rtrim (cur->p_name, name)); |
9769 |
+ cur = cur->pred_next; |
9770 |
+ } |
9771 |
+ fprintf (fp, "\n"); |
9772 |
+} |
9773 |
+ |
9774 |
+/* Print out the predicate list starting at NODE. */ |
9775 |
+static void |
9776 |
+print_parenthesised(FILE *fp, struct predicate *node) |
9777 |
+{ |
9778 |
+ int parens = 0; |
9779 |
+ |
9780 |
+ if (node) |
9781 |
+ { |
9782 |
+ if ((pred_is(node, pred_or) || pred_is(node, pred_and)) |
9783 |
+ && node->pred_left == NULL) |
9784 |
+ { |
9785 |
+ /* We print "<nothing> or X" as just "X" |
9786 |
+ * We print "<nothing> and X" as just "X" |
9787 |
+ */ |
9788 |
+ print_parenthesised(fp, node->pred_right); |
9789 |
+ } |
9790 |
+ else |
9791 |
+ { |
9792 |
+ if (node->pred_left || node->pred_right) |
9793 |
+ parens = 1; |
9794 |
+ |
9795 |
+ if (parens) |
9796 |
+ fprintf(fp, "%s", " ( "); |
9797 |
+ print_optlist(fp, node); |
9798 |
+ if (parens) |
9799 |
+ fprintf(fp, "%s", " ) "); |
9800 |
+ } |
9801 |
+ } |
9802 |
+} |
9803 |
+ |
9804 |
+void |
9805 |
+print_optlist (FILE *fp, const struct predicate *p) |
9806 |
+{ |
9807 |
+ if (p) |
9808 |
+ { |
9809 |
+ print_parenthesised(fp, p->pred_left); |
9810 |
+ fprintf (fp, |
9811 |
+ "%s%s", |
9812 |
+ p->need_stat ? "[call stat] " : "", |
9813 |
+ p->need_type ? "[need type] " : ""); |
9814 |
+ print_predicate(fp, p); |
9815 |
+ fprintf(fp, " [%g] ", p->est_success_rate); |
9816 |
+ if (options.debug_options & DebugSuccessRates) |
9817 |
+ { |
9818 |
+ fprintf(fp, "[%ld/%ld", p->perf.successes, p->perf.visits); |
9819 |
+ if (p->perf.visits) |
9820 |
+ { |
9821 |
+ double real_rate = (double)p->perf.successes / (double)p->perf.visits; |
9822 |
+ fprintf(fp, "=%g] ", real_rate); |
9823 |
+ } |
9824 |
+ else |
9825 |
+ { |
9826 |
+ fprintf(fp, "=_] "); |
9827 |
+ } |
9828 |
+ } |
9829 |
+ print_parenthesised(fp, p->pred_right); |
9830 |
+ } |
9831 |
+} |
9832 |
+ |
9833 |
+void show_success_rates(const struct predicate *p) |
9834 |
+{ |
9835 |
+ if (options.debug_options & DebugSuccessRates) |
9836 |
+ { |
9837 |
+ fprintf(stderr, "Predicate success rates after completion:\n"); |
9838 |
+ print_optlist(stderr, p); |
9839 |
+ fprintf(stderr, "\n"); |
9840 |
+ } |
9841 |
+} |
9842 |
+ |
9843 |
+ |
9844 |
+ |
9845 |
+ |
9846 |
+#ifdef _NDEBUG |
9847 |
+/* If _NDEBUG is defined, the assertions will do nothing. Hence |
9848 |
+ * there is no point in having a function body for pred_sanity_check() |
9849 |
+ * if that preprocessor macro is defined. |
9850 |
+ */ |
9851 |
+void |
9852 |
+pred_sanity_check(const struct predicate *predicates) |
9853 |
+{ |
9854 |
+ /* Do nothing, since assert is a no-op with _NDEBUG set */ |
9855 |
+ return; |
9856 |
+} |
9857 |
+#else |
9858 |
+void |
9859 |
+pred_sanity_check(const struct predicate *predicates) |
9860 |
+{ |
9861 |
+ const struct predicate *p; |
9862 |
+ |
9863 |
+ for (p=predicates; p != NULL; p=p->pred_next) |
9864 |
+ { |
9865 |
+ /* All predicates must do something. */ |
9866 |
+ assert (p->pred_func != NULL); |
9867 |
+ |
9868 |
+ /* All predicates must have a parser table entry. */ |
9869 |
+ assert (p->parser_entry != NULL); |
9870 |
+ |
9871 |
+ /* If the parser table tells us that just one predicate function is |
9872 |
+ * possible, verify that that is still the one that is in effect. |
9873 |
+ * If the parser has NULL for the predicate function, that means that |
9874 |
+ * the parse_xxx function fills it in, so we can't check it. |
9875 |
+ */ |
9876 |
+ if (p->parser_entry->pred_func) |
9877 |
+ { |
9878 |
+ assert (p->parser_entry->pred_func == p->pred_func); |
9879 |
+ } |
9880 |
+ |
9881 |
+ switch (p->parser_entry->type) |
9882 |
+ { |
9883 |
+ /* Options all take effect during parsing, so there should |
9884 |
+ * be no predicate entries corresponding to them. Hence we |
9885 |
+ * should not see any ARG_OPTION or ARG_POSITIONAL_OPTION |
9886 |
+ * items. |
9887 |
+ * |
9888 |
+ * This is a silly way of coding this test, but it prevents |
9889 |
+ * a compiler warning (i.e. otherwise it would think that |
9890 |
+ * there would be case statements missing). |
9891 |
+ */ |
9892 |
+ case ARG_OPTION: |
9893 |
+ case ARG_POSITIONAL_OPTION: |
9894 |
+ assert (p->parser_entry->type != ARG_OPTION); |
9895 |
+ assert (p->parser_entry->type != ARG_POSITIONAL_OPTION); |
9896 |
+ break; |
9897 |
+ |
9898 |
+ case ARG_ACTION: |
9899 |
+ assert(p->side_effects); /* actions have side effects. */ |
9900 |
+ if (!pred_is(p, pred_prune) && !pred_is(p, pred_quit)) |
9901 |
+ { |
9902 |
+ /* actions other than -prune and -quit should |
9903 |
+ * inhibit the default -print |
9904 |
+ */ |
9905 |
+ assert (p->no_default_print); |
9906 |
+ } |
9907 |
+ break; |
9908 |
+ |
9909 |
+ /* We happen to know that the only user of ARG_SPECIAL_PARSE |
9910 |
+ * is a test, so handle it like ARG_TEST. |
9911 |
+ */ |
9912 |
+ case ARG_SPECIAL_PARSE: |
9913 |
+ case ARG_TEST: |
9914 |
+ case ARG_PUNCTUATION: |
9915 |
+ case ARG_NOOP: |
9916 |
+ /* Punctuation and tests should have no side |
9917 |
+ * effects and not inhibit default print. |
9918 |
+ */ |
9919 |
+ assert (!p->no_default_print); |
9920 |
+ assert (!p->side_effects); |
9921 |
+ break; |
9922 |
+ } |
9923 |
+ } |
9924 |
+} |
9925 |
+#endif |
9926 |
diff -purN findutils-4.3.12.orig/find/tree.c findutils-4.3.12/find/tree.c |
9927 |
--- findutils-4.3.12.orig/find/tree.c 2007-12-19 16:12:34.000000000 -0500 |
9928 |
+++ findutils-4.3.12/find/tree.c 2008-01-30 08:46:05.758843847 -0500 |
9929 |
@@ -1195,6 +1195,10 @@ build_expression_tree(int argc, char *ar |
9930 |
const struct parser_table *entry_close, *entry_print, *entry_open; |
9931 |
int i, oldi; |
9932 |
|
9933 |
+#ifdef WITH_SELINUX |
9934 |
+ int is_selinux_enabled_flag = is_selinux_enabled()>0; |
9935 |
+#endif /* WITH_SELINUX */ |
9936 |
+ |
9937 |
predicates = NULL; |
9938 |
|
9939 |
/* Find where in ARGV the predicates begin by skipping the list of |
9940 |
@@ -1230,6 +1234,16 @@ build_expression_tree(int argc, char *ar |
9941 |
} |
9942 |
|
9943 |
predicate_name = argv[i]; |
9944 |
+ |
9945 |
+#ifdef WITH_SELINUX |
9946 |
+ if (! is_selinux_enabled_flag) { |
9947 |
+ if ((strncmp(predicate_name,"-context",strlen("-context"))==0) || |
9948 |
+ (strncmp(predicate_name,"--context",strlen("--context"))==0)) { |
9949 |
+ error (1, 0,_("Error: invalid predicate %s: the kernel is not selinux-enabled.\n"),predicate_name); |
9950 |
+ } |
9951 |
+ } |
9952 |
+#endif |
9953 |
+ |
9954 |
parse_entry = find_parser (predicate_name); |
9955 |
if (parse_entry == NULL) |
9956 |
{ |
9957 |
@@ -1434,6 +1448,9 @@ get_new_pred (const struct parser_table |
9958 |
last_pred->need_stat = true; |
9959 |
last_pred->need_type = true; |
9960 |
last_pred->args.str = NULL; |
9961 |
+#ifdef WITH_SELINUX |
9962 |
+ last_pred->args.scontext = NULL; |
9963 |
+#endif |
9964 |
last_pred->pred_next = NULL; |
9965 |
last_pred->pred_left = NULL; |
9966 |
last_pred->pred_right = NULL; |
9967 |
|
9968 |
|
9969 |
|
9970 |
-- |
9971 |
gentoo-commits@l.g.o mailing list |