From a3f4821c4a3f723d21c9298d54bee8a656bfd7fb Mon Sep 17 00:00:00 2001
From: Bernhard Voelker <mail@bernhard-voelker.de>
Date: Tue, 30 Jan 2018 23:30:09 +0100
Subject: [PATCH] find: make -delete honour the -ignore_readdir_race option

* find/pred.c (pred_delete): Return true when the -ignore_readdir_race
option is active and unlinkat() came back with ENOENT.
* doc/find.texi (Option -ignore_readdir_race): Document the change.
(Action -delete): Likewise.
* find/find.1: Likewise.
* NEWS (Bug Fixes): Mention the fix.

For now, it seems a bit hard to add a proper test for this,
so the following shell snippet demonstrates the race:

  $ seq 10 | xargs touch
  $ env time -f 'find exit status: %x\nfind time: %e' \
      find -ignore_readdir_race -type f \
        -delete \
        -exec sh -c 'sleep $(basename {})' \; \
        -printf 'find deleted: %p\n' \
        & \
    sleep 20; \
    seq 10 | xargs rm -fv; \
    wait $!

Reported by Alexander Golubev in
https://savannah.gnu.org/bugs/?52981

Upstream-Status: Backport
Signed-off-by: Zhixiong Chi <zhixiong.chi@windriver.com>

---
 NEWS          |  4 ++++
 doc/find.texi | 15 ++++++++++++++-
 find/find.1   | 22 ++++++++++++++++++++++
 find/pred.c   |  6 ++++++
 4 files changed, 46 insertions(+), 1 deletion(-)

diff --git a/NEWS b/NEWS
index 660c241..b86ec1e 100644
--- a/NEWS
+++ b/NEWS
@@ -42,6 +42,10 @@ Updated the Danish translation.
 
 ** Bug Fixes:
 
+#52981: find: the '-delete' action no longer complains about disappeared files
+        when the '-ignore_readdir_race' option is given, too.  That action will
+        also returns true in such a case now.
+
 Applied patch #8688: Spelling fixes.
 
 * Major changes in release 4.5.18, 2015-12-27
diff --git a/doc/find.texi b/doc/find.texi
index fdeb841..247c19a 100644
--- a/doc/find.texi
+++ b/doc/find.texi
@@ -1418,7 +1418,15 @@ gives a significant increase in search speed.
 If a file disappears after its name has been read from a directory but
 before @code{find} gets around to examining the file with @code{stat},
 don't issue an error message.  If you don't specify this option, an
-error message will be issued.  This option can be useful in system
+error message will be issued.
+
+Furthermore, @code{find} with the @samp{-ignore_readdir_race} option
+will ignore errors of the @samp{-delete} action in the case the file
+has disappeared since the parent directory was read: it will not output
+an error diagnostic, and the return code of the @samp{-delete} action
+will be true.
+
+This option can be useful in system
 scripts (cron scripts, for example) that examine areas of the
 filesystem that change frequently (mail queues, temporary directories,
 and so forth), because this scenario is common for those sorts of
@@ -2779,6 +2787,11 @@ explicitly.
 
 If @samp{-delete} fails, @code{find}'s exit status will be nonzero
 (when it eventually exits).
+
+Together with the @samp{-ignore_readdir_race} option, @code{find} will
+ignore errors of the @samp{-delete} action in the case the file has disappeared
+since the parent directory was read: it will not output an error diagnostic, and
+the return code of the @samp{-delete} action will be true.
 @end deffn
 
 @node Adding Tests
diff --git a/find/find.1 b/find/find.1
index 7b141b8..0eec41c 100644
--- a/find/find.1
+++ b/find/find.1
@@ -479,6 +479,17 @@ one part of the filesystem with this option on and part of it with this option
 off (if you need to do that, you will need to issue two \fBfind\fR commands
 instead, one with the option and one without it).
 
+Furthermore,
+.B find
+with the
+.B \-ignore_readdir_race
+option will ignore errors of the
+.B \-delete
+action in the case the file has disappeared since the parent directory was read:
+it will not output an error diagnostic, and the return code of the
+.B \-delete
+action will be true.
+
 .IP "\-maxdepth \fIlevels\fR"
 Descend at most \fIlevels\fR (a non-negative integer) levels of
 directories below the starting-points.
@@ -1030,6 +1041,17 @@ and
 .B \-delete
 together.
 
+Together with the
+.B \-ignore_readdir_race
+option,
+.B find
+will ignore errors of the
+.B \-delete
+action in the case the file has disappeared since the parent directory was
+read: it will not output an error diagnostic, and the return code of the
+.B \-delete
+action will be true.
+
 .IP "\-exec \fIcommand\fR ;"
 Execute \fIcommand\fR; true if 0 status is returned.  All following
 arguments to
diff --git a/find/pred.c b/find/pred.c
index 32938fb..431f065 100644
--- a/find/pred.c
+++ b/find/pred.c
@@ -324,6 +324,12 @@ pred_delete (const char *pathname, struct stat *stat_buf, struct predicate *pred
 	}
       else
 	{
+	  if (ENOENT == errno && options.ignore_readdir_race)
+	    {
+	      /* Ignore unlink() error for vanished files.  */
+	      errno = 0;
+	      return true;
+	    }
 	  if (EISDIR == errno)
 	    {
 	      if ((flags & AT_REMOVEDIR) == 0)
