Issue an error message when fts_read fails. Fixes bug #39324.

* find/ftsfind.c (find): when fts_read fails, issue an error
message, set the exit status to zero and stop.  Previously the
program would just stop (i.e. it failed to distinguish "done" from
"failed").
* find/find.1 (-exec): explain that on failure, some pending
command launches may not happen.  The Texinfo documentation
already pointed this out, so that didn't need to be changed.
* NEWS: Mention this bugfix.
This commit is contained in:
James Youngman 2013-09-22 16:25:12 +01:00
parent 17ae32160c
commit f8b3e65f77
7 changed files with 134 additions and 67 deletions

View File

@ -1,3 +1,15 @@
2013-09-22 James Youngman <jay@gnu.org>
Issue an error message when fts_read fails. Fixes bug #39324.
* find/ftsfind.c (find): when fts_read fails, issue an error
message, set the exit status to zero and stop. Previously the
program would just stop (i.e. it failed to distinguish "done" from
"failed").
* find/find.1 (-exec): explain that on failure, some pending
command launches may not happen. The Texinfo documentation
already pointed this out, so that didn't need to be changed.
* NEWS: Mention this bugfix.
2011-04-02 James Youngman <jay@gnu.org>
Use gnulib's parse-datetime module instead of getdate.

2
NEWS
View File

@ -4,6 +4,8 @@ GNU findutils NEWS - User visible changes. -*- outline -*- (allout)
** Bug Fixes
#39324: exits without error on OOM
#28872: Mistake in "#safer" example in "Problems with -exec and
filenames" section of the Texinfo manual.

View File

@ -12,3 +12,6 @@ Makefile.in
Makefile
mkinstalldirs
ylwrap
arg-nonnull.h
c++defs.h
warn-on-use.h

View File

@ -11,3 +11,6 @@ Makefile.in
mkinstalldirs
ylwrap
compile
/arg-nonnull.h
/c++defs.h
/warn-on-use.h

View File

@ -30,3 +30,4 @@ versionmaint.texi
fdl.texi
gpl-3.0.texi
regexprops-generic.texi
parse-datetime.texi

View File

@ -1009,6 +1009,11 @@ command line is built in much the same way that
.B xargs
builds its command lines. Only one instance of `{}' is allowed within
the command. The command is executed in the starting directory.
If
.B find
encounters an error, this can sometimes cause an
immediate exit, so some pending commands may not be run
at all.
.IP "\-execdir \fIcommand\fR ;"
.IP "\-execdir \fIcommand\fR {} +"
@ -1038,6 +1043,11 @@ appropriately-named file in a directory in which you will run
The same applies to having entries in
.B $PATH
which are empty or which are not absolute directory names.
If
.B find
encounters an error, this can sometimes cause an
immediate exit, so some pending commands may not be run
at all.
.IP "\-fls \fIfile\fR"
True; like
@ -1977,6 +1987,16 @@ description, but if the return value is non-zero, you should not rely
on the correctness of the results of
.BR find .
When some error occurs,
.B find
may stop immeidately, without completing all the actions specified.
For example, some starting points may not have been examined or some
pending program invocations for
.B \-exec ... {} +
or
.B \-execdir ... {} +
may not have been performed.
.SH "SEE ALSO"
\fBlocate\fP(1), \fBlocatedb\fP(5), \fBupdatedb\fP(1), \fBxargs\fP(1),
\fBchmod\fP(1), \fBfnmatch\fP(3), \fBregex\fP(7), \fBstat\fP(2),

View File

@ -17,13 +17,13 @@
*/
/* This file was written by James Youngman, based on find.c.
GNU find was written by Eric Decker <cire@cisco.com>,
with enhancements by David MacKenzie <djm@gnu.org>,
Jay Plett <jay@silence.princeton.nj.us>,
and Tim Wood <axolotl!tim@toad.com>.
The idea for -print0 and xargs -0 came from
Dan Bernstein <brnstnd@kramden.acf.nyu.edu>.
Dan Bernstein <brnstnd@kramden.acf.nyu.edu>.
*/
@ -116,15 +116,15 @@ static void left_dir(void)
/*
* Signal that we are now inside a directory pointed to by dir_fd.
* The caller can't tell if this is the first time this happens, so
* we have to be careful not to call dup() more than once
* The caller can't tell if this is the first time this happens, so
* we have to be careful not to call dup() more than once
*/
static void inside_dir(int dir_fd)
{
if (ftsoptions & FTS_CWDFD)
{
assert (dir_fd == AT_FDCWD || dir_fd >= 0);
state.cwd_dir_fd = dir_fd;
if (curr_fd < 0)
{
@ -137,7 +137,7 @@ static void inside_dir(int dir_fd)
curr_fd = dup(dir_fd);
set_close_on_exec(curr_fd);
}
else
else
{
/* curr_fd is invalid, but dir_fd is also invalid.
* This should not have happened.
@ -148,7 +148,7 @@ static void inside_dir(int dir_fd)
}
else
{
/* FTS_CWDFD is not in use. We can always assume that
/* FTS_CWDFD is not in use. We can always assume that
* AT_FDCWD refers to the directory we are currentl searching.
*
* Therefore there is nothing to do.
@ -164,7 +164,7 @@ static void init_mounted_dev_list(void);
/* We have encountered an error which should affect the exit status.
* This is normally used to change the exit status from 0 to 1.
* However, if the exit status is already 2 for example, we don't want to
* However, if the exit status is already 2 for example, we don't want to
* reduce it to 1.
*/
static void
@ -208,7 +208,7 @@ static void
visit(FTS *p, FTSENT *ent, struct stat *pstat)
{
struct predicate *eval_tree;
state.curdepth = ent->fts_level;
state.have_stat = (ent->fts_info != FTS_NS) && (ent->fts_info != FTS_NSOK);
state.rel_pathname = ent->fts_accpath;
@ -236,7 +236,7 @@ partial_quotearg_n(int n, char *s, size_t len, enum quoting_style style)
{
char saved;
const char *result;
saved = s[len];
s[len] = 0;
result = quotearg_n_style(n, style, s);
@ -246,14 +246,14 @@ partial_quotearg_n(int n, char *s, size_t len, enum quoting_style style)
}
/* We've detected a file system loop. This is caused by one of
/* We've detected a file system loop. This is caused by one of
* two things:
*
* 1. Option -L is in effect and we've hit a symbolic link that
* points to an ancestor. This is harmless. We won't traverse the
* 1. Option -L is in effect and we've hit a symbolic link that
* points to an ancestor. This is harmless. We won't traverse the
* symbolic link.
*
* 2. We have hit a real cycle in the directory hierarchy. In this
* 2. We have hit a real cycle in the directory hierarchy. In this
* case, we issue a diagnostic message (POSIX requires this) and we
* skip that directory entry.
*/
@ -287,13 +287,13 @@ issue_loop_warning(FTSENT * ent)
}
}
/*
* Return true if NAME corresponds to a file which forms part of a
* symbolic link loop. The command
* rm -f a b; ln -s a b; ln -s b a
/*
* Return true if NAME corresponds to a file which forms part of a
* symbolic link loop. The command
* rm -f a b; ln -s a b; ln -s b a
* produces such a loop.
*/
static boolean
static boolean
symlink_loop(const char *name)
{
struct stat stbuf;
@ -305,7 +305,7 @@ symlink_loop(const char *name)
return (0 != rv) && (ELOOP == errno);
}
static void
show_outstanding_execdirs(FILE *fp)
{
@ -319,7 +319,7 @@ show_outstanding_execdirs(FILE *fp)
while (p)
{
const char *pfx;
if (pred_is(p, pred_execdir))
pfx = "-execdir";
else if (pred_is(p, pred_okdir))
@ -331,7 +331,7 @@ show_outstanding_execdirs(FILE *fp)
int i;
const struct exec_val *execp = &p->args.exec_vec;
++seen;
fprintf(fp, "%s ", pfx);
if (execp->multiple)
fprintf(fp, "multiple ");
@ -362,7 +362,7 @@ consider_visiting(FTS *p, FTSENT *ent)
struct stat statbuf;
mode_t mode;
int ignore, isdir;
if (options.debug_options & DebugSearch)
fprintf(stderr,
"consider_visiting: fts_info=%-6s, fts_level=%2d, prev_depth=%d "
@ -371,7 +371,7 @@ consider_visiting(FTS *p, FTSENT *ent)
(int)ent->fts_level, prev_depth,
quotearg_n_style(0, options.err_quoting_style, ent->fts_path),
quotearg_n_style(1, options.err_quoting_style, ent->fts_accpath));
if (ent->fts_info == FTS_DP)
{
left_dir();
@ -383,7 +383,7 @@ consider_visiting(FTS *p, FTSENT *ent)
inside_dir(p->fts_cwd_fd);
prev_depth = ent->fts_level;
/* Cope with various error conditions. */
if (ent->fts_info == FTS_ERR
|| ent->fts_info == FTS_DNR)
@ -404,8 +404,8 @@ consider_visiting(FTS *p, FTSENT *ent)
/* fts_read() claims that ent->fts_accpath is a broken symbolic
* link. That would be fine, but if this is part of a symbolic
* link loop, we diagnose the problem and also ensure that the
* eventual return value is nonzero. Note that while the path
* we stat is local (fts_accpath), we print the full path name
* eventual return value is nonzero. Note that while the path
* we stat is local (fts_accpath), we print the full path name
* of the file (fts_path) in the error message.
*/
if (symlink_loop(ent->fts_accpath))
@ -439,7 +439,7 @@ consider_visiting(FTS *p, FTSENT *ent)
}
}
}
/* Cope with the usual cases. */
if (ent->fts_info == FTS_NSOK
|| ent->fts_info == FTS_NS /* e.g. symlink loop */)
@ -454,7 +454,7 @@ consider_visiting(FTS *p, FTSENT *ent)
state.have_type = true;
statbuf = *(ent->fts_statp);
state.type = mode = statbuf.st_mode;
if (00000 == mode)
{
/* Savannah bug #16378. */
@ -492,20 +492,20 @@ consider_visiting(FTS *p, FTSENT *ent)
if (ent->fts_level >= options.maxdepth)
{
fts_set(p, ent, FTS_SKIP); /* descend no further */
if (ent->fts_level > options.maxdepth)
if (ent->fts_level > options.maxdepth)
ignore = 1; /* don't even look at this one */
}
}
if ( (ent->fts_info == FTS_D) && !options.do_dir_first )
{
/* this is the preorder visit, but user said -depth */
/* this is the preorder visit, but user said -depth */
ignore = 1;
}
else if ( (ent->fts_info == FTS_DP) && options.do_dir_first )
{
/* this is the postorder visit, but user didn't say -depth */
/* this is the postorder visit, but user didn't say -depth */
ignore = 1;
}
else if (ent->fts_level < options.mindepth)
@ -527,30 +527,30 @@ consider_visiting(FTS *p, FTSENT *ent)
static void
static boolean
find(char *arg)
{
char * arglist[2];
FTS *p;
FTSENT *ent;
state.starting_path_length = strlen(arg);
inside_dir(AT_FDCWD);
arglist[0] = arg;
arglist[1] = NULL;
switch (options.symlink_handling)
{
case SYMLINK_ALWAYS_DEREF:
ftsoptions |= FTS_COMFOLLOW|FTS_LOGICAL;
break;
case SYMLINK_DEREF_ARGSONLY:
ftsoptions |= FTS_COMFOLLOW|FTS_PHYSICAL;
break;
case SYMLINK_NEVER_DEREF:
ftsoptions |= FTS_PHYSICAL;
break;
@ -558,18 +558,19 @@ find(char *arg)
if (options.stay_on_filesystem)
ftsoptions |= FTS_XDEV;
p = fts_open(arglist, ftsoptions, NULL);
if (NULL == p)
{
error (0, errno, _("cannot search %s"),
safely_quote_err_filename(0, arg));
return false;
}
else
{
int level = INT_MIN;
while ( (ent=fts_read(p)) != NULL )
while ( (errno=0, ent=fts_read(p)) != NULL )
{
if (state.execdirs_outstanding)
{
@ -593,13 +594,35 @@ find(char *arg)
state.type = 0;
consider_visiting(p, ent);
}
fts_close(p);
/* fts_read returned NULL; distinguish between "finished" and "error". */
if (errno)
{
error (0, errno,
"failed to read file names from file system at or below %s",
safely_quote_err_filename (0, arg));
error_severity (EXIT_FAILURE);
return false;
}
if (0 != fts_close (p))
{
/* Here we break the abstraction of fts_close a bit, because we
* are going to skip the rest of the start points, and return with
* nonzero exit status. Hence we need to issue a diagnostic on
* stderr. */
error (0, errno,
_("failed to restore working directory after searching %s"),
arg);
error_severity (EXIT_FAILURE);
return false;
}
p = NULL;
}
return true;
}
static void
static boolean
process_all_startpoints(int argc, char *argv[])
{
int i;
@ -607,21 +630,23 @@ process_all_startpoints(int argc, char *argv[])
/* figure out how many start points there are */
for (i = 0; i < argc && !looks_like_expression(argv[i], true); i++)
{
state.starting_path_length = strlen(argv[i]); /* TODO: is this redundant? */
find(argv[i]);
state.starting_path_length = strlen (argv[i]); /* TODO: is this redundant? */
if (!find (argv[i]))
return false;
}
if (i == 0)
{
/*
* We use a temporary variable here because some actions modify
* the path temporarily. Hence if we use a string constant,
* we get a coredump. The best example of this is if we say
/*
* We use a temporary variable here because some actions modify
* the path temporarily. Hence if we use a string constant,
* we get a coredump. The best example of this is if we say
* "find -printf %H" (note, not "find . -printf %H").
*/
char defaultpath[2] = ".";
find(defaultpath);
return find (defaultpath);
}
return true;
}
@ -644,7 +669,7 @@ main (int argc, char **argv)
* check_nofollow() needs to be executed in the POSIX locale.
*/
set_option_defaults(&options);
#ifdef HAVE_SETLOCALE
setlocale (LC_ALL, "");
#endif
@ -653,11 +678,11 @@ main (int argc, char **argv)
textdomain (PACKAGE);
atexit (close_stdout);
/* Check for -P, -H or -L options. Also -D and -O, which are
/* Check for -P, -H or -L options. Also -D and -O, which are
* both GNU extensions.
*/
end_of_leading_options = process_leading_options(argc, argv);
if (options.debug_options & DebugStat)
options.xstat = debug_stat;
@ -666,20 +691,20 @@ main (int argc, char **argv)
#endif /* DEBUG */
/* We are now processing the part of the "find" command line
/* We are now processing the part of the "find" command line
* after the -H/-L options (if any).
*/
eval_tree = build_expression_tree(argc, argv, end_of_leading_options);
/* safely_chdir() needs to check that it has ended up in the right place.
* To avoid bailing out when something gets automounted, it checks if
/* safely_chdir() needs to check that it has ended up in the right place.
* To avoid bailing out when something gets automounted, it checks if
* the target directory appears to have had a directory mounted on it as
* we chdir()ed. The problem with this is that in order to notice that
* we chdir()ed. The problem with this is that in order to notice that
* a file system was mounted, we would need to lstat() all the mount points.
* That strategy loses if our machine is a client of a dead NFS server.
*
* Hence if safely_chdir() and wd_sanity_check() can manage without needing
* to know the mounted device list, we do that.
* Hence if safely_chdir() and wd_sanity_check() can manage without needing
* to know the mounted device list, we do that.
*/
if (!options.open_nofollow_available)
{
@ -687,16 +712,17 @@ main (int argc, char **argv)
init_mounted_dev_list();
#endif
}
process_all_startpoints(argc-end_of_leading_options, argv+end_of_leading_options);
/* If "-exec ... {} +" has been used, there may be some
* partially-full command lines which have been built,
* but which are not yet complete. Execute those now.
*/
show_success_rates(eval_tree);
cleanup();
if (process_all_startpoints(argc-end_of_leading_options, argv+end_of_leading_options))
{
/* If "-exec ... {} +" has been used, there may be some
* partially-full command lines which have been built,
* but which are not yet complete. Execute those now.
*/
show_success_rates(eval_tree);
cleanup();
}
return state.exit_status;
}