From cce84822f72e6cd4af7bfa351a4da2c9bdc5bb81 Mon Sep 17 00:00:00 2001 From: Michael Albinus Date: Tue, 13 Jul 2021 19:50:05 +0200 Subject: [PATCH] Add remote-file-name-inhibit-locks * doc/emacs/files.texi (Interlocking): * doc/lispref/files.texi (File Locks): * doc/misc/tramp.texi (Auto-save File Lock and Backup): Add remote-file-name-inhibit-locks. * etc/NEWS: New user option 'remote-file-name-inhibit-locks'. * lisp/files.el (remote-file-name-inhibit-locks): New defcustom. * lisp/net/tramp-adb.el (tramp-adb-file-name-handler-alist): * lisp/net/tramp-crypt.el (tramp-crypt-file-name-handler-alist): * lisp/net/tramp-gvfs.el (tramp-gvfs-file-name-handler-alist): * lisp/net/tramp-rclone.el (tramp-rclone-file-name-handler-alist): * lisp/net/tramp-sh.el (tramp-sh-file-name-handler-alist): * lisp/net/tramp-smb.el (tramp-smb-file-name-handler-alist): * lisp/net/tramp-sshfs.el (tramp-sshfs-file-name-handler-alist): * lisp/net/tramp-sudoedit.el (tramp-sudoedit-file-name-handler-alist): Use `tramp-handle-make-lock-file-name'. * lisp/net/tramp.el (tramp-allow-unsafe-temporary-files): Fix docstring. (tramp-handle-make-lock-file-name): New defun. * test/lisp/net/tramp-tests.el (tramp-test39-lock-file): Extend test. --- doc/emacs/files.texi | 4 +++ doc/lispref/files.texi | 5 +++ doc/misc/tramp.texi | 10 ++++-- etc/NEWS | 15 ++++++--- lisp/files.el | 6 ++++ lisp/net/tramp-adb.el | 2 +- lisp/net/tramp-crypt.el | 2 +- lisp/net/tramp-gvfs.el | 2 +- lisp/net/tramp-rclone.el | 2 +- lisp/net/tramp-sh.el | 2 +- lisp/net/tramp-smb.el | 2 +- lisp/net/tramp-sshfs.el | 2 +- lisp/net/tramp-sudoedit.el | 2 +- lisp/net/tramp.el | 26 ++++++++++++++- test/lisp/net/tramp-tests.el | 64 +++++++++++++++++++++++++----------- 15 files changed, 110 insertions(+), 36 deletions(-) diff --git a/doc/emacs/files.texi b/doc/emacs/files.texi index 98b6b194d2d..32a2f1bb815 100644 --- a/doc/emacs/files.texi +++ b/doc/emacs/files.texi @@ -836,6 +836,10 @@ warning message and asks for confirmation before saving; answer place, one way to compare the buffer to its file is the @kbd{M-x diff-buffer-with-file} command. @xref{Comparing Files}. +@vindex remote-file-name-inhibit-locks + You can prevent the creation of remote lock files by setting the +variable @code{remote-file-name-inhibit-locks} to @code{t}. + @node File Shadowing @subsection Shadowing Files @cindex shadow files diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi index b1b70a9f063..1f4049f715c 100644 --- a/doc/lispref/files.texi +++ b/doc/lispref/files.texi @@ -821,6 +821,11 @@ If you wish, you can replace the @code{ask-user-about-lock} function with your own version that makes the decision in another way. @end defun +@defopt remote-file-name-inhibit-locks +You can prevent the creation of remote lock files by setting the +variable @code{remote-file-name-inhibit-locks} to @code{t}. +@end defopt + @node Information about Files @section Information about Files @cindex file, information about diff --git a/doc/misc/tramp.texi b/doc/misc/tramp.texi index 8ba5f0118a3..088352e8a8a 100644 --- a/doc/misc/tramp.texi +++ b/doc/misc/tramp.texi @@ -2858,11 +2858,15 @@ to warn you, if a file is changed in parallel from different Emacs sessions, or via different remote connections. Be careful with such settings. +@vindex remote-file-name-inhibit-locks +Setting @code{remote-file-name-inhibit-locks} to non-@code{nil} +prevents the creation of remote lock files at all. + @vindex tramp-allow-unsafe-temporary-files Per default, @value{tramp} asks for confirmation if a -@samp{root}-owned backup or auto-save remote file has to be written to -your local temporary directory. If you want to suppress this -confirmation question, set user option +@samp{root}-owned remote backup, auto-save or lock file has to be +written to your local temporary directory. If you want to suppress +this confirmation question, set user option @code{tramp-allow-unsafe-temporary-files} to @code{t}. diff --git a/etc/NEWS b/etc/NEWS index 923cfcc4722..fd661a1e7a6 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1464,12 +1464,15 @@ buffer to a file under the "/tmp/" directory. This is useful, if (in rare cases) Tramp blocks Emacs, and we need further debug information. +++ -*** Writing sensitive auto-save or backup files to the local temporary -directory must be confirmed. In order to suppress this confirmation, -set user option 'tramp-allow-unsafe-temporary-files' to t. +*** Tramp supports lock files now. +In order to deactivate this, set user option +'remote-file-name-inhibit-locks' to t. +++ -*** Tramp supports file locks now. +*** Writing sensitive auto-save, backup or lock files to the local +temporary directory must be confirmed. In order to suppress this +confirmation, set user option 'tramp-allow-unsafe-temporary-files' to +t. ** Tempo @@ -2182,6 +2185,10 @@ summaries will include the failing condition. This option allows controlling where lock files are written. It uses the same syntax as 'auto-save-file-name-transforms'. ++++ +*** New user option 'remote-file-name-inhibit-locks'. +When non-nil, this option suppresses lock files for remote files. + +++ *** New user option 'kill-transform-function'. This can be used to transform (and suppress) strings from entering the diff --git a/lisp/files.el b/lisp/files.el index 0dfcab8f89b..ad02d373fd0 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -427,6 +427,12 @@ file it's locking, and it has the same name, but with \".#\" prepended." :initialize 'custom-initialize-delay :version "28.1") +(defcustom remote-file-name-inhibit-locks nil + "Whether to use file locks for remote files." + :group 'files + :version "28.1" + :type 'boolean) + (defvar auto-save--timer nil "Timer for `auto-save-visited-mode'.") (defcustom auto-save-visited-interval 5 diff --git a/lisp/net/tramp-adb.el b/lisp/net/tramp-adb.el index dbbbfe6a3f9..8138d9a3608 100644 --- a/lisp/net/tramp-adb.el +++ b/lisp/net/tramp-adb.el @@ -164,7 +164,7 @@ It is used for TCP/IP devices." (make-auto-save-file-name . tramp-handle-make-auto-save-file-name) (make-directory . tramp-adb-handle-make-directory) (make-directory-internal . ignore) - ;; `make-lock-file-name' performed by default handler. + (make-lock-file-name . tramp-handle-make-lock-file-name) (make-nearby-temp-file . tramp-handle-make-nearby-temp-file) (make-process . tramp-adb-handle-make-process) (make-symbolic-link . tramp-handle-make-symbolic-link) diff --git a/lisp/net/tramp-crypt.el b/lisp/net/tramp-crypt.el index 1b77fea7e18..109db3b1d7b 100644 --- a/lisp/net/tramp-crypt.el +++ b/lisp/net/tramp-crypt.el @@ -213,7 +213,7 @@ If NAME doesn't belong to a crypted remote directory, retun nil." (make-auto-save-file-name . tramp-handle-make-auto-save-file-name) (make-directory . tramp-crypt-handle-make-directory) (make-directory-internal . ignore) - ;; `make-lock-file-name' performed by default handler. + (make-lock-file-name . tramp-handle-make-lock-file-name) (make-nearby-temp-file . tramp-handle-make-nearby-temp-file) (make-process . ignore) (make-symbolic-link . tramp-handle-make-symbolic-link) diff --git a/lisp/net/tramp-gvfs.el b/lisp/net/tramp-gvfs.el index 04de5defb37..022fdeeb885 100644 --- a/lisp/net/tramp-gvfs.el +++ b/lisp/net/tramp-gvfs.el @@ -805,7 +805,7 @@ It has been changed in GVFS 1.14.") (make-auto-save-file-name . tramp-handle-make-auto-save-file-name) (make-directory . tramp-gvfs-handle-make-directory) (make-directory-internal . ignore) - ;; `make-lock-file-name' performed by default handler. + (make-lock-file-name . tramp-handle-make-lock-file-name) (make-nearby-temp-file . tramp-handle-make-nearby-temp-file) (make-process . ignore) (make-symbolic-link . tramp-handle-make-symbolic-link) diff --git a/lisp/net/tramp-rclone.el b/lisp/net/tramp-rclone.el index 473fa8a8f0e..49e366c01c6 100644 --- a/lisp/net/tramp-rclone.el +++ b/lisp/net/tramp-rclone.el @@ -127,7 +127,7 @@ (make-auto-save-file-name . tramp-handle-make-auto-save-file-name) (make-directory . tramp-fuse-handle-make-directory) (make-directory-internal . ignore) - ;; `make-lock-file-name' performed by default handler. + (make-lock-file-name . tramp-handle-make-lock-file-name) (make-nearby-temp-file . tramp-handle-make-nearby-temp-file) (make-process . ignore) (make-symbolic-link . tramp-handle-make-symbolic-link) diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el index 3595bd26557..760320d7ed4 100644 --- a/lisp/net/tramp-sh.el +++ b/lisp/net/tramp-sh.el @@ -993,7 +993,7 @@ Format specifiers \"%s\" are replaced before the script is used.") (make-auto-save-file-name . tramp-handle-make-auto-save-file-name) (make-directory . tramp-sh-handle-make-directory) ;; `make-directory-internal' performed by default handler. - ;; `make-lock-file-name' performed by default handler. + (make-lock-file-name . tramp-handle-make-lock-file-name) (make-nearby-temp-file . tramp-handle-make-nearby-temp-file) (make-process . tramp-sh-handle-make-process) (make-symbolic-link . tramp-sh-handle-make-symbolic-link) diff --git a/lisp/net/tramp-smb.el b/lisp/net/tramp-smb.el index 1c7ddee0086..4008c25d3af 100644 --- a/lisp/net/tramp-smb.el +++ b/lisp/net/tramp-smb.el @@ -278,7 +278,7 @@ See `tramp-actions-before-shell' for more info.") (make-auto-save-file-name . tramp-handle-make-auto-save-file-name) (make-directory . tramp-smb-handle-make-directory) (make-directory-internal . tramp-smb-handle-make-directory-internal) - ;; `make-lock-file-name' performed by default handler. + (make-lock-file-name . tramp-handle-make-lock-file-name) (make-nearby-temp-file . tramp-handle-make-nearby-temp-file) (make-process . ignore) (make-symbolic-link . tramp-smb-handle-make-symbolic-link) diff --git a/lisp/net/tramp-sshfs.el b/lisp/net/tramp-sshfs.el index 5f6807a0db7..99f4063988f 100644 --- a/lisp/net/tramp-sshfs.el +++ b/lisp/net/tramp-sshfs.el @@ -127,7 +127,7 @@ (make-auto-save-file-name . tramp-handle-make-auto-save-file-name) (make-directory . tramp-fuse-handle-make-directory) (make-directory-internal . ignore) - ;; `make-lock-file-name' performed by default handler. + (make-lock-file-name . tramp-handle-make-lock-file-name) (make-nearby-temp-file . tramp-handle-make-nearby-temp-file) (make-process . tramp-handle-make-process) (make-symbolic-link . tramp-handle-make-symbolic-link) diff --git a/lisp/net/tramp-sudoedit.el b/lisp/net/tramp-sudoedit.el index d68a5c1adf4..45d9fab986c 100644 --- a/lisp/net/tramp-sudoedit.el +++ b/lisp/net/tramp-sudoedit.el @@ -120,7 +120,7 @@ See `tramp-actions-before-shell' for more info.") (make-auto-save-file-name . tramp-handle-make-auto-save-file-name) (make-directory . tramp-sudoedit-handle-make-directory) (make-directory-internal . ignore) - ;; `make-lock-file-name' performed by default handler. + (make-lock-file-name . tramp-handle-make-lock-file-name) (make-nearby-temp-file . tramp-handle-make-nearby-temp-file) (make-process . ignore) (make-symbolic-link . tramp-sudoedit-handle-make-symbolic-link) diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el index 9e6bfceb49a..3f586c62170 100644 --- a/lisp/net/tramp.el +++ b/lisp/net/tramp.el @@ -3630,7 +3630,7 @@ User is always nil." (file-writable-p (file-name-directory filename))))))) (defcustom tramp-allow-unsafe-temporary-files nil - "Whether root-owned auto-save or backup files can be written to \"/tmp\"." + "Whether root-owned auto-save, backup or lock files can be written to \"/tmp\"." :version "28.1" :type 'boolean) @@ -3880,6 +3880,30 @@ Return nil when there is no lockfile." (write-region info nil lockname) (set-file-modes lockname #o0644)))))))) +(defun tramp-handle-make-lock-file-name (file) + "Like `make-lock-file-name' for Tramp files." + (when (and create-lockfiles + ;; This variable has been introduced with Emacs 28.1. + (not (bound-and-true-p remote-file-name-inhibit-locks))) + (with-parsed-tramp-file-name file nil + (let ((result + ;; Run plain `make-lock-file-name'. + (tramp-run-real-handler #'make-lock-file-name (list file)))) + ;; Protect against security hole. + (when (and (not tramp-allow-unsafe-temporary-files) + (file-in-directory-p result temporary-file-directory) + (zerop (or (tramp-compat-file-attribute-user-id + (file-attributes file 'integer)) + tramp-unknown-id-integer)) + (not (with-tramp-connection-property + (tramp-get-process v) "unsafe-temporary-file" + (yes-or-no-p + (concat + "Lock file on local temporary directory, " + "do you want to continue? "))))) + (tramp-error v 'file-error "Unsafe lock file name")) + result)))) + (defun tramp-handle-unlock-file (file) "Like `unlock-file' for Tramp files." (when-let ((lockname (tramp-compat-make-lock-file-name file))) diff --git a/test/lisp/net/tramp-tests.el b/test/lisp/net/tramp-tests.el index 44fd1b45b26..bc05db8095b 100644 --- a/test/lisp/net/tramp-tests.el +++ b/test/lisp/net/tramp-tests.el @@ -5751,8 +5751,10 @@ Use direct async.") (skip-unless (and (fboundp 'lock-file) (fboundp 'unlock-file))) (dolist (quoted (if (tramp--test-expensive-test) '(nil t) '(nil))) - (let ((tmp-name (tramp--test-make-temp-name nil quoted)) + (let ((tmp-name1 (tramp--test-make-temp-name nil quoted)) + (tmp-name2 (tramp--test-make-temp-name nil quoted)) (remote-file-name-inhibit-cache t) + (remote-file-name-inhibit-locks nil) (create-lockfiles t) (inhibit-message t) ;; tramp-rclone.el and tramp-sshfs.el cache the mounted files. @@ -5765,51 +5767,73 @@ Use direct async.") (unwind-protect (progn ;; A simple file lock. - (should-not (file-locked-p tmp-name)) - (lock-file tmp-name) - (should (eq (file-locked-p tmp-name) t)) + (should-not (file-locked-p tmp-name1)) + (lock-file tmp-name1) + (should (eq (file-locked-p tmp-name1) t)) ;; If it is locked already, nothing changes. - (lock-file tmp-name) - (should (eq (file-locked-p tmp-name) t)) + (lock-file tmp-name1) + (should (eq (file-locked-p tmp-name1) t)) ;; A new connection changes process id, and also the ;; lockname contents. (tramp-cleanup-connection tramp-test-vec 'keep-debug 'keep-password) - (should (stringp (file-locked-p tmp-name))) + (should (stringp (file-locked-p tmp-name1))) + + ;; When `remote-file-name-inhibit-locks' is set, nothing happens. + (tramp-cleanup-connection tramp-test-vec 'keep-debug 'keep-password) + (let ((remote-file-name-inhibit-locks t)) + (lock-file tmp-name1) + (should-not (file-locked-p tmp-name1))) + + ;; When `lock-file-name-transforms' is set, another lock + ;; file is used. + (tramp-cleanup-connection tramp-test-vec 'keep-debug 'keep-password) + (let ((lock-file-name-transforms `((".*" ,tmp-name2)))) + (should + (string-equal + (make-lock-file-name tmp-name1) + (make-lock-file-name tmp-name2))) + (lock-file tmp-name1) + (should (eq (file-locked-p tmp-name1) t)) + (unlock-file tmp-name1) + (should-not (file-locked-p tmp-name1))) ;; Steal the file lock. (tramp-cleanup-connection tramp-test-vec 'keep-debug 'keep-password) (cl-letf (((symbol-function #'read-char) (lambda (&rest _args) ?s))) - (lock-file tmp-name)) - (should (eq (file-locked-p tmp-name) t)) + (lock-file tmp-name1)) + (should (eq (file-locked-p tmp-name1) t)) ;; Ignore the file lock. (tramp-cleanup-connection tramp-test-vec 'keep-debug 'keep-password) (cl-letf (((symbol-function #'read-char) (lambda (&rest _args) ?p))) - (lock-file tmp-name)) - (should (stringp (file-locked-p tmp-name))) + (lock-file tmp-name1)) + (should (stringp (file-locked-p tmp-name1))) ;; Quit the file lock machinery. (tramp-cleanup-connection tramp-test-vec 'keep-debug 'keep-password) (cl-letf (((symbol-function #'read-char) (lambda (&rest _args) ?q))) - (should-error (lock-file tmp-name) :type 'file-locked) + (should-error (lock-file tmp-name1) :type 'file-locked) ;; The same for `write-region'. - (should-error (write-region "foo" nil tmp-name) :type 'file-locked) (should-error - (write-region "foo" nil tmp-name nil nil tmp-name) + (write-region "foo" nil tmp-name1) :type 'file-locked) + (should-error + (write-region "foo" nil tmp-name1 nil nil tmp-name1) :type 'file-locked) ;; The same for `set-visited-file-name'. (with-temp-buffer (should-error - (set-visited-file-name tmp-name) :type 'file-locked))) - (should (stringp (file-locked-p tmp-name))) - (should-not (file-exists-p tmp-name))) + (set-visited-file-name tmp-name1) :type 'file-locked))) + (should (stringp (file-locked-p tmp-name1))) + (should-not (file-exists-p tmp-name1))) ;; Cleanup. - (ignore-errors (delete-file tmp-name)) - (unlock-file tmp-name) - (should-not (file-locked-p tmp-name)))))) + (ignore-errors (delete-file tmp-name1)) + (unlock-file tmp-name1) + (unlock-file tmp-name2) + (should-not (file-locked-p tmp-name1)) + (should-not (file-locked-p tmp-name2)))))) ;; The functions were introduced in Emacs 26.1. (ert-deftest tramp-test40-make-nearby-temp-file () -- 2.39.2