Subject: bug#70421: eshell/sudo with delete-by-moving-to-trash

From: Michael Albinus <[email-masked]>
Date: Wed, 17 Apr 2024 10:28:56 +0200

кппкяып япвквкп via "Bug reports for GNU Emacs, the Swiss army knife of
text editors" <[email-masked]> writes:

Hi,

> In GNU Emacs 30.0.50 on Arch Linux, I encountered a permission issue
> when trying to delete a file with elevated privileges using `eshell/rm`
> in combination with `delete-by-moving-to-trash` enabled. This suggests
> that the deletion process is not fully "TRAMP aware."
>  
> Steps to reproduce:
>  
> 1. emacs -Q
> 2. ~ $ (setq delete-by-moving-to-trash t)
> t
> 3. ~ $ (require 'em-tramp)
> em-tramp
> 4. ~ $ eshell/sudo touch /etc/bugtestfile
> 5. ~ $ eshell/sudo rm /etc/bugtestfile
> Cannot move /etc/bugtestfile to trash: Permission denied
>  
>  
> Expected behavior:
>  
> I expect emacs to handle this situation by using
> /root/.local/share/Trash
> instead of /home/username/.local/share/Trash.

No. Without special preparation, remote files are moved to the local
trash directory, /home/username/.local/share/Trash this case. See the
Tramp manual (info "(tramp) Frequently Asked Questions")

--8<---------------cut here---------------start------------->8---
   • Where are remote files trashed to?

     Emacs can trash file instead of deleting them, *note Trashing:
     (emacs)Misc File Ops.  Remote files are always trashed to the local
     trash, except the user option
     ‘remote-file-name-inhibit-delete-by-moving-to-trash’ is non-‘nil’,
     or it is a remote encrypted file (*note Keeping files encrypted::),
     which are deleted anyway.
--8<---------------cut here---------------end--------------->8---

However, we could try to configure it. There is the variable
trash-directory, which is nil by default. We cannot give it another
(remote) directory name, because it would be used always. Instead, we
use connection-local variables. In your case, I recommend to set

--8<---------------cut here---------------start------------->8---
(connection-local-set-profile-variables
 'remote-trash-directory
 '((trash-directory . "/sudo::~/.local/share/Trash")))

(connection-local-set-profiles
 `(:application tramp :protocol "sudo" :machine ,system-name)
 'remote-trash-directory)
--8<---------------cut here---------------end--------------->8---

Now we must teach Emacs, that trash-directory should respect
connection-local values. The following patch of files.el does this for
us:

diff --git a/lisp/files.el b/lisp/files.el
index 1e11dd44bad..447661dba5b 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -8812,9 +8812,10 @@ move-file-to-trash
   ;; If `system-move-file-to-trash' is defined, use it.
   (cond ((fboundp 'system-move-file-to-trash)
 	 (system-move-file-to-trash filename))
-        (trash-directory
+        ((connection-local-value trash-directory)
 	 ;; If `trash-directory' is non-nil, move the file there.
-	 (let* ((trash-dir   (expand-file-name trash-directory))
+	 (let* ((trash-dir   (expand-file-name
+                              (connection-local-value trash-directory)))
 		(fn          (directory-file-name (expand-file-name filename)))
 		(new-fn      (concat (file-name-as-directory trash-dir)
 				     (file-name-nondirectory fn))))

Could you, please, check whether it works for you?

Best regards, Michael.