Fixes https://savannah.gnu.org/bugs/?46305
* NEWS (Bug Fixes): Mention the change. * doc/find.texi (-delete action): Add explanation of the issue. * find/find.1 (-delete action): Add explanation of the issue. * find/pred.c (pred_delete): Do unlink when rmdir fails (ENOTDIR). * find/testsuite/Makefile.am: Add new test and output files. * find/testsuite/find.gnu/deletedirsymlink.exp: New test. * find/testsuite/find.gnu/deletedirsymlink.xo: New test output. --- NEWS | 2 ++ doc/find.texi | 10 ++++++++++ find/find.1 | 15 +++++++++++++++ find/pred.c | 10 ++++++++++ find/testsuite/Makefile.am | 2 ++ find/testsuite/find.gnu/deletedirsymlink.exp | 10 ++++++++++ find/testsuite/find.gnu/deletedirsymlink.xo | 2 ++ 7 files changed, 51 insertions(+) create mode 100644 find/testsuite/find.gnu/deletedirsymlink.exp create mode 100644 find/testsuite/find.gnu/deletedirsymlink.xo diff --git a/NEWS b/NEWS index dffca5aa..6dec7342 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,8 @@ GNU findutils NEWS - User visible changes. -*- outline -*- (allout) to match the root directory "/". Previously, a diagnostic falsely claimed that this pattern would not match anything. [#62227] + #46305: 'find -L . -delete' now successfully deletes symlinks to directories. + ** Changes to the build process findutils now builds again on systems with musl-libc. diff --git a/doc/find.texi b/doc/find.texi index 1f295837..e6da6b7b 100644 --- a/doc/find.texi +++ b/doc/find.texi @@ -3001,6 +3001,16 @@ change the exit code to nonzero, and the return code of the @samp{-delete} action will be true. @end deffn +When @samp{-delete} is used with the @samp{-L} or @samp{-H} option, and a +symlink to a directory is followed, the symlink's ultimate target +directory's contents are searched, and any matches found there are deleted, +but the target directory itself is never deleted. It's not possible to +delete a filesystem entry via a symlink to it. If the directory itself +matches the search criteria, the symlink is deleted. Similarly, when a +symlink to a non-directory is followed, and the symlink's ultimate target +matches the search criteria, the symlink is deleted, never the ultimate +target. + @node Adding Tests @section Adding Tests diff --git a/find/find.1 b/find/find.1 index 429aa2f0..510390b0 100644 --- a/find/find.1 +++ b/find/find.1 @@ -1224,6 +1224,21 @@ nonzero, and the return code of the .B \-delete action will be true. +When +.B \-delete +is used with the +.B \-L +or +.B \-H +option, and a symlink to a directory is followed, the symlink's ultimate +target directory's contents are searched, and any matches found there are +deleted, but the target directory itself is never deleted. It's not possible +to delete a filesystem entry via a symlink to it. If the directory itself +matches the search criteria, the symlink is deleted. Similarly, when a +symlink to a non-directory is followed, and the symlink's ultimate target +matches the search criteria, the symlink is deleted, never the ultimate +target. + .IP "\-exec \fIcommand\fR ;" Execute diff --git a/find/pred.c b/find/pred.c index f9f42fac..c6643f7b 100644 --- a/find/pred.c +++ b/find/pred.c @@ -256,6 +256,16 @@ pred_delete (const char *pathname, struct stat *stat_buf, struct predicate *pred return true; } } + if (ENOTDIR == errno) + { + if ((flags & AT_REMOVEDIR) != 0) + { + /* rmdir() operation failed because we should have done unlink(). */ + flags &= ~AT_REMOVEDIR; + if (perform_delete (flags)) + return true; + } + } } error (0, errno, _("cannot delete %s"), safely_quote_err_filename (0, pathname)); diff --git a/find/testsuite/Makefile.am b/find/testsuite/Makefile.am index 568f3841..a305ca9d 100644 --- a/find/testsuite/Makefile.am +++ b/find/testsuite/Makefile.am @@ -30,6 +30,7 @@ find.gnu/comma.xo \ find.gnu/delete.xo \ find.gnu/deletedir.xo \ find.gnu/deletefile.xo \ +find.gnu/deletedirsymlink.xo \ find.gnu/depth.xo \ find.gnu/depth-d.xo \ find.gnu/empty.xo \ @@ -129,6 +130,7 @@ find.gnu/comma.exp \ find.gnu/delete.exp \ find.gnu/deletedir.exp \ find.gnu/deletefile.exp \ +find.gnu/deletedirsymlink.exp \ find.gnu/depth.exp \ find.gnu/depth-d.exp \ find.gnu/empty.exp \ diff --git a/find/testsuite/find.gnu/deletedirsymlink.exp b/find/testsuite/find.gnu/deletedirsymlink.exp new file mode 100644 index 00000000..f3d7a1ef --- /dev/null +++ b/find/testsuite/find.gnu/deletedirsymlink.exp @@ -0,0 +1,10 @@ +# Test that symlinks to directories are deleted even with -L +# (necessarily so), not the target directories (and not an error) +global FTSFIND +global FINDFLAGS +exec rm -rf tmp +exec mkdir tmp tmp/t tmp/dd tmp/dd/d +exec ln -s ../dd tmp/t/ld +eval exec $FTSFIND -L tmp/t $FINDFLAGS -delete +find_start p {tmp -print} +exec rm -rf tmp diff --git a/find/testsuite/find.gnu/deletedirsymlink.xo b/find/testsuite/find.gnu/deletedirsymlink.xo new file mode 100644 index 00000000..a09bfee5 --- /dev/null +++ b/find/testsuite/find.gnu/deletedirsymlink.xo @@ -0,0 +1,2 @@ +tmp +tmp/dd -- 2.30.2