Subject: Re: bug#58721: 28.2; dired with delete-by-moving-to-trash can't trash directory twice

From: Mike Kupfer <[email-masked]>
Date: Sun, 30 Oct 2022 15:20:05 -0700

Eli Zaretskii wrote:

> I meant in the code, not the log message, and I meant to tell more
> detail.
> 
> But never mind, I can always add it later myself.

Well, it turns out I hadn't tested my previous patch enough.  It handled
the first deletion of directory foo, but not subsequent deletions (which
was the entire point of the fix, sigh).  I've attached another patch.
I took a stab at improving the commenting in the code while I was
there.

mike
>From 402c7c819dbe2a1cd96462be699ae2af1f5c2440 Mon Sep 17 00:00:00 2001
From: Mike Kupfer <[email-masked]>
Date: Sun, 30 Oct 2022 10:31:11 -0700
Subject: [PATCH] Fix cross-filesystem directory trashing (Bug#58721)

* lisp/files.el (move-file-to-trash): When trashing a directory with
the same name as something that's already in the trash, copy it into
the trash folder and then delete it, rather than using rename-file.
---
 lisp/files.el | 21 +++++++++++++++++++--
 1 file changed, 19 insertions(+), 2 deletions(-)

diff --git a/lisp/files.el b/lisp/files.el
index 1e1ec6127d..6bfb3aa738 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -8565,10 +8565,27 @@ move-file-to-trash
 		    (setq files-base (substring (file-name-nondirectory info-fn)
                                                 0 (- (length ".trashinfo"))))
 		    (write-region nil nil info-fn nil 'quiet info-fn)))
-		 ;; Finally, try to move the file to the trashcan.
+		 ;; Finally, try to move the item to the trashcan.  If
+                 ;; it's a file, just move it.  Things are more
+                 ;; complicated for directories.  If the target
+                 ;; directory already exists (due to uniquification)
+                 ;; and the trash directory is in a different
+                 ;; filesystem, rename-file will error out, even when
+                 ;; 'overwrite' is non-nil.  Rather than worry about
+                 ;; whether we're crossing filesystems, just check if
+                 ;; we've moving a directory and the target directory
+                 ;; already exists.  That handles both the
+                 ;; same-filesystem and cross-filesystem cases.
 		 (let ((delete-by-moving-to-trash nil)
 		       (new-fn (file-name-concat trash-files-dir files-base)))
-		   (rename-file fn new-fn overwrite)))))))))
+                   (if (or (not is-directory)
+                           (not (file-exists-p new-fn)))
+                       (rename-file fn new-fn overwrite)
+                     (copy-directory fn
+                                     (file-name-as-directory new-fn)
+                                     t nil t)
+                     (delete-directory fn t))))))))))
+
 
 
 (defsubst file-attribute-type (attributes)
-- 
2.30.2