[PATCH] find: fix -L and -delete removing symlinks to directories

raf <[email protected]>
Newsgroups gmane.comp.gnu.findutils.bugs,gmane.comp.gnu.findutils.patches
Message-ID <[email protected]>
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.