emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] externals/tramp 3e19596: Tramp ELPA version 2.5.1.1 released


From: ELPA Syncer
Subject: [elpa] externals/tramp 3e19596: Tramp ELPA version 2.5.1.1 released
Date: Fri, 30 Jul 2021 10:57:18 -0400 (EDT)

branch: externals/tramp
commit 3e195960bed24ff5910752f839bcd56862292d67
Author: Michael Albinus <michael.albinus@gmx.de>
Commit: Michael Albinus <michael.albinus@gmx.de>

    Tramp ELPA version 2.5.1.1 released
---
 test/tramp-tests.el | 215 +++++++++++++++++++++++++++++++++++++++++++---------
 texi/tramp.texi     |  38 ++++++++--
 texi/trampver.texi  |   2 +-
 tramp-adb.el        |  39 +++++++---
 tramp-archive.el    |   4 +
 tramp-cache.el      |   6 +-
 tramp-compat.el     |  24 ++++++
 tramp-crypt.el      |  23 ++++++
 tramp-fuse.el       |  17 ++++-
 tramp-gvfs.el       |  24 ++++--
 tramp-rclone.el     |   8 ++
 tramp-sh.el         | 159 ++++++++++++++++++++++----------------
 tramp-smb.el        |  34 +++++++--
 tramp-sshfs.el      |  44 ++++++++---
 tramp-sudoedit.el   |  31 ++++++--
 tramp.el            | 187 +++++++++++++++++++++++++++++++++++++++------
 trampver.el         |   6 +-
 17 files changed, 683 insertions(+), 178 deletions(-)

diff --git a/test/tramp-tests.el b/test/tramp-tests.el
index 68caadb..98493bf 100644
--- a/test/tramp-tests.el
+++ b/test/tramp-tests.el
@@ -33,7 +33,7 @@
 ;; remote host, set this environment variable to "/dev/null" or
 ;; whatever is appropriate on your system.
 
-;; For slow remote connections, `tramp-test43-asynchronous-requests'
+;; For slow remote connections, `tramp-test44-asynchronous-requests'
 ;; might be too heavy.  Setting $REMOTE_PARALLEL_PROCESSES to a proper
 ;; value less than 10 could help.
 
@@ -63,6 +63,8 @@
 (declare-function tramp-smb-get-localname "tramp-smb")
 (defvar ange-ftp-make-backup-files)
 (defvar auto-save-file-name-transforms)
+(defvar lock-file-name-transforms)
+(defvar remote-file-name-inhibit-locks)
 (defvar tramp-connection-properties)
 (defvar tramp-copy-size-limit)
 (defvar tramp-display-escape-sequence-regexp)
@@ -122,6 +124,7 @@
 (setq auth-source-save-behavior nil
       password-cache-expiry nil
       remote-file-name-inhibit-cache nil
+      tramp-allow-unsafe-temporary-files t
       tramp-cache-read-persistent-data t ;; For auth-sources.
       tramp-copy-size-limit nil
       tramp-persistency-file-name nil
@@ -2463,6 +2466,9 @@ This checks also `file-name-as-directory', 
`file-name-directory',
                          "^\\'")
                        tramp--test-messages))))))))
 
+           ;; We do not test lockname here.  See
+           ;; `tramp-test39-make-lock-file-name'.
+
            ;; Do not overwrite if excluded.
            (cl-letf (((symbol-function #'y-or-n-p) #'tramp--test-always)
                      ;; Ange-FTP.
@@ -2833,8 +2839,7 @@ This tests also `file-directory-p' and 
`file-accessible-directory-p'."
           (delete-directory tmp-name1 nil 'trash)
           ;; tramp-rclone.el and tramp-sshfs.el call the local
           ;; `delete-directory'.  This raises another error.
-          :type (if (or (tramp--test-rclone-p) (tramp--test-sshfs-p))
-                    'error 'file-error))
+          :type (if (tramp--test-fuse-p) 'error 'file-error))
          (delete-directory tmp-name1 'recursive 'trash)
          (should-not (file-directory-p tmp-name1))
          (should
@@ -4091,7 +4096,7 @@ This tests also `make-symbolic-link', `file-truename' and 
`add-name-to-file'."
            (write-region "foo" nil tmp-name1)
            (should (file-exists-p tmp-name1))
            (should (file-selinux-context tmp-name1))
-           (copy-file tmp-name1 tmp-name2)
+           (copy-file tmp-name1 tmp-name2 nil nil nil 'preserve-permissions)
            (should (file-selinux-context tmp-name2))
            (should
             (equal
@@ -5480,7 +5485,8 @@ Use direct async.")
 
   (dolist (quoted (if (tramp--test-expensive-test) '(nil t) '(nil)))
     (let ((tmp-name1 (tramp--test-make-temp-name nil quoted))
-         (tmp-name2 (tramp--test-make-temp-name nil quoted)))
+         (tmp-name2 (tramp--test-make-temp-name nil quoted))
+         tramp-allow-unsafe-temporary-files)
 
       (unwind-protect
          (progn
@@ -5568,8 +5574,7 @@ Use direct async.")
 
            ;; Create temporary file.  This shall check for sensible
            ;; files, owned by root.
-           (let ((tramp-auto-save-directory temporary-file-directory)
-                 tramp-allow-unsafe-temporary-files)
+           (let ((tramp-auto-save-directory temporary-file-directory))
              (write-region "foo" nil tmp-name1)
              (when (zerop (or (tramp-compat-file-attribute-user-id
                                (file-attributes tmp-name1))
@@ -5605,6 +5610,7 @@ Use direct async.")
     (let ((tmp-name1 (tramp--test-make-temp-name nil quoted))
          (tmp-name2 (tramp--test-make-temp-name nil quoted))
          (ange-ftp-make-backup-files t)
+         tramp-allow-unsafe-temporary-files
          ;; These settings are not used by Tramp, so we ignore them.
          version-control delete-old-versions
          (kept-old-versions (default-toplevel-value 'kept-old-versions))
@@ -5715,7 +5721,6 @@ Use direct async.")
          ;; Create temporary file.  This shall check for sensible
          ;; files, owned by root.
          (let ((backup-directory-alist `(("." . ,temporary-file-directory)))
-               tramp-allow-unsafe-temporary-files
                tramp-backup-directory-alist)
            (write-region "foo" nil tmp-name1)
            (when (zerop (or (tramp-compat-file-attribute-user-id
@@ -5741,8 +5746,144 @@ Use direct async.")
        (ignore-errors (delete-file tmp-name1))
        (tramp-cleanup-connection tramp-test-vec 'keep-debug 'keep-password)))))
 
+;; The functions were introduced in Emacs 28.1.
+(ert-deftest tramp-test39-make-lock-file-name ()
+  "Check `make-lock-file-name', `lock-file', `unlock-file' and 
`file-locked-p'."
+  (skip-unless (tramp--test-enabled))
+  (skip-unless (not (tramp--test-ange-ftp-p)))
+  ;; Since Emacs 28.1.
+  (skip-unless (and (fboundp 'lock-file) (fboundp 'unlock-file)))
+  (skip-unless (and (fboundp 'file-locked-p) (fboundp 'make-lock-file-name)))
+
+  ;; `lock-file', `unlock-file', `file-locked-p' and
+  ;; `make-lock-file-name' exists since Emacs 28.1.  We don't want to
+  ;; see compiler warnings for older Emacsen.
+  (dolist (quoted (if (tramp--test-expensive-test) '(nil t) '(nil)))
+    (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)
+         tramp-allow-unsafe-temporary-files
+          (inhibit-message t)
+         ;; tramp-rclone.el and tramp-sshfs.el cache the mounted files.
+         (tramp-cleanup-connection-hook
+          (append
+           (and (tramp--test-fuse-p) '(tramp-fuse-unmount))
+           tramp-cleanup-connection-hook))
+          auto-save-default
+         noninteractive)
+
+      (unwind-protect
+         (progn
+           ;; A simple file lock.
+           (should-not (with-no-warnings (file-locked-p tmp-name1)))
+           (with-no-warnings (lock-file tmp-name1))
+           (should (eq (with-no-warnings (file-locked-p tmp-name1)) t))
+
+           ;; If it is locked already, nothing changes.
+           (with-no-warnings (lock-file tmp-name1))
+           (should (eq (with-no-warnings (file-locked-p tmp-name1)) t))
+
+            ;; `save-buffer' removes the lock.
+            (with-temp-buffer
+              (set-visited-file-name tmp-name1)
+              (insert "foo")
+              (save-buffer))
+            (should-not (with-no-warnings (file-locked-p tmp-name1)))
+           (with-no-warnings (lock-file tmp-name1))
+           (should (eq (with-no-warnings (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 (with-no-warnings (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))
+             (with-no-warnings (lock-file tmp-name1))
+             (should-not (with-no-warnings (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
+               (with-no-warnings (make-lock-file-name tmp-name1))
+               (with-no-warnings (make-lock-file-name tmp-name2))))
+             (with-no-warnings (lock-file tmp-name1))
+             (should (eq (with-no-warnings (file-locked-p tmp-name1)) t))
+             (with-no-warnings (unlock-file tmp-name1))
+             (should-not (with-no-warnings (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)))
+             (with-no-warnings (lock-file tmp-name1)))
+           (should (eq (with-no-warnings (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)))
+             (with-no-warnings (lock-file tmp-name1)))
+           (should (stringp (with-no-warnings (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)))
+             (with-no-warnings
+               (should-error
+                (lock-file tmp-name1)
+                :type 'file-locked))
+             ;; The same for `write-region'.
+             (should-error
+              (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-name1)
+                :type 'file-locked)))
+           (should (stringp (with-no-warnings (file-locked-p tmp-name1)))))
+
+       ;; Cleanup.
+       (ignore-errors (delete-file tmp-name1))
+       (with-no-warnings (unlock-file tmp-name1))
+       (with-no-warnings (unlock-file tmp-name2))
+       (should-not (with-no-warnings (file-locked-p tmp-name1)))
+       (should-not (with-no-warnings (file-locked-p tmp-name2))))
+
+      (unwind-protect
+         ;; Create temporary file.  This shall check for sensible
+         ;; files, owned by root.
+         (let ((lock-file-name-transforms auto-save-file-name-transforms))
+           (write-region "foo" nil tmp-name1)
+           (when (zerop (or (tramp-compat-file-attribute-user-id
+                             (file-attributes tmp-name1))
+                            tramp-unknown-id-integer))
+             (tramp-cleanup-connection
+              tramp-test-vec 'keep-debug 'keep-password)
+             (cl-letf (((symbol-function #'yes-or-no-p) #'ignore))
+               (should-error
+                (write-region "foo" nil tmp-name1)
+                :type 'file-error))
+             (tramp-cleanup-connection
+              tramp-test-vec 'keep-debug 'keep-password)
+             (cl-letf (((symbol-function #'yes-or-no-p)
+                        #'tramp--test-always))
+               (write-region "foo" nil tmp-name1))))
+
+       ;; Cleanup.
+       (ignore-errors (delete-file tmp-name1))
+       (tramp-cleanup-connection tramp-test-vec 'keep-debug 'keep-password)))))
+
 ;; The functions were introduced in Emacs 26.1.
-(ert-deftest tramp-test39-make-nearby-temp-file ()
+(ert-deftest tramp-test40-make-nearby-temp-file ()
   "Check `make-nearby-temp-file' and `temporary-file-directory'."
   (skip-unless (tramp--test-enabled))
   (skip-unless (not (tramp--test-ange-ftp-p)))
@@ -5825,6 +5966,10 @@ This does not support globbing characters in file names 
(yet)."
   (string-match-p
    "ftp$" (file-remote-p tramp-test-temporary-file-directory 'method)))
 
+(defun tramp--test-fuse-p ()
+  "Check, whether an FUSE file system isused."
+  (or (tramp--test-rclone-p) (tramp--test-sshfs-p)))
+
 (defun tramp--test-gdrive-p ()
   "Check, whether the gdrive method is used."
   (string-equal
@@ -5935,7 +6080,9 @@ This requires restrictions of file name syntax."
            (file-truename tramp-test-temporary-file-directory))
           (tmp-name1 (tramp--test-make-temp-name nil quoted))
           (tmp-name2 (tramp--test-make-temp-name 'local quoted))
-          (files (delq nil files))
+          (files
+            (delq
+             nil (mapcar (lambda (x) (unless (string-empty-p x) x)) files)))
           (process-environment process-environment)
           (sorted-files (sort (copy-sequence files) #'string-lessp))
           buffer)
@@ -5945,7 +6092,7 @@ This requires restrictions of file name syntax."
            (make-directory tmp-name2)
 
            (dolist (elt files)
-             ;(tramp--test-message "%s" elt)
+              ;(tramp--test-message "'%s'" elt)
              (let* ((file1 (expand-file-name elt tmp-name1))
                     (file2 (expand-file-name elt tmp-name2))
                     (file3 (expand-file-name (concat elt "foo") tmp-name1)))
@@ -6113,7 +6260,7 @@ This requires restrictions of file name syntax."
        (ignore-errors (delete-directory tmp-name2 'recursive))))))
 
 (defun tramp--test-special-characters ()
-  "Perform the test in `tramp-test40-special-characters*'."
+  "Perform the test in `tramp-test41-special-characters*'."
   ;; Newlines, slashes and backslashes in file names are not
   ;; supported.  So we don't test.  And we don't test the tab
   ;; character on Windows or Cygwin, because the backslash is
@@ -6171,7 +6318,7 @@ This requires restrictions of file name syntax."
               files (list (mapconcat #'identity files ""))))))
 
 ;; These tests are inspired by Bug#17238.
-(ert-deftest tramp-test40-special-characters ()
+(ert-deftest tramp-test41-special-characters ()
   "Check special characters in file names."
   (skip-unless (tramp--test-enabled))
   (skip-unless (not (tramp--test-rsync-p)))
@@ -6179,7 +6326,7 @@ This requires restrictions of file name syntax."
 
   (tramp--test-special-characters))
 
-(ert-deftest tramp-test40-special-characters-with-stat ()
+(ert-deftest tramp-test41-special-characters-with-stat ()
   "Check special characters in file names.
 Use the `stat' command."
   :tags '(:expensive-test)
@@ -6197,7 +6344,7 @@ Use the `stat' command."
          tramp-connection-properties)))
     (tramp--test-special-characters)))
 
-(ert-deftest tramp-test40-special-characters-with-perl ()
+(ert-deftest tramp-test41-special-characters-with-perl ()
   "Check special characters in file names.
 Use the `perl' command."
   :tags '(:expensive-test)
@@ -6218,7 +6365,7 @@ Use the `perl' command."
          tramp-connection-properties)))
     (tramp--test-special-characters)))
 
-(ert-deftest tramp-test40-special-characters-with-ls ()
+(ert-deftest tramp-test41-special-characters-with-ls ()
   "Check special characters in file names.
 Use the `ls' command."
   :tags '(:expensive-test)
@@ -6239,7 +6386,7 @@ Use the `ls' command."
     (tramp--test-special-characters)))
 
 (defun tramp--test-utf8 ()
-  "Perform the test in `tramp-test41-utf8*'."
+  "Perform the test in `tramp-test42-utf8*'."
   (let* ((utf8 (if (and (eq system-type 'darwin)
                        (memq 'utf-8-hfs (coding-system-list)))
                   'utf-8-hfs 'utf-8))
@@ -6285,7 +6432,7 @@ Use the `ls' command."
             (replace-regexp-in-string "[ \t\n/.?]" "" x)))
          language-info-alist)))))))
 
-(ert-deftest tramp-test41-utf8 ()
+(ert-deftest tramp-test42-utf8 ()
   "Check UTF8 encoding in file names and file contents."
   (skip-unless (tramp--test-enabled))
   (skip-unless (not (tramp--test-docker-p)))
@@ -6298,7 +6445,7 @@ Use the `ls' command."
 
   (tramp--test-utf8))
 
-(ert-deftest tramp-test41-utf8-with-stat ()
+(ert-deftest tramp-test42-utf8-with-stat ()
   "Check UTF8 encoding in file names and file contents.
 Use the `stat' command."
   :tags '(:expensive-test)
@@ -6320,7 +6467,7 @@ Use the `stat' command."
          tramp-connection-properties)))
     (tramp--test-utf8)))
 
-(ert-deftest tramp-test41-utf8-with-perl ()
+(ert-deftest tramp-test42-utf8-with-perl ()
   "Check UTF8 encoding in file names and file contents.
 Use the `perl' command."
   :tags '(:expensive-test)
@@ -6345,7 +6492,7 @@ Use the `perl' command."
          tramp-connection-properties)))
     (tramp--test-utf8)))
 
-(ert-deftest tramp-test41-utf8-with-ls ()
+(ert-deftest tramp-test42-utf8-with-ls ()
   "Check UTF8 encoding in file names and file contents.
 Use the `ls' command."
   :tags '(:expensive-test)
@@ -6369,7 +6516,7 @@ Use the `ls' command."
          tramp-connection-properties)))
     (tramp--test-utf8)))
 
-(ert-deftest tramp-test42-file-system-info ()
+(ert-deftest tramp-test43-file-system-info ()
   "Check that `file-system-info' returns proper values."
   (skip-unless (tramp--test-enabled))
   ;; Since Emacs 27.1.
@@ -6386,11 +6533,11 @@ Use the `ls' command."
                 (numberp (nth 1 fsi))
                 (numberp (nth 2 fsi))))))
 
-;; `tramp-test43-asynchronous-requests' could be blocked.  So we set a
+;; `tramp-test44-asynchronous-requests' could be blocked.  So we set a
 ;; timeout of 300 seconds, and we send a SIGUSR1 signal after 300
 ;; seconds.  Similar check is performed in the timer function.
 (defconst tramp--test-asynchronous-requests-timeout 300
-  "Timeout for `tramp-test43-asynchronous-requests'.")
+  "Timeout for `tramp-test44-asynchronous-requests'.")
 
 (defmacro tramp--test-with-proper-process-name-and-buffer (proc &rest body)
   "Set \"process-name\" and \"process-buffer\" connection properties.
@@ -6426,7 +6573,7 @@ This is needed in timer functions as well as process 
filters and sentinels."
         (tramp-flush-connection-property v "process-buffer")))))
 
 ;; This test is inspired by Bug#16928.
-(ert-deftest tramp-test43-asynchronous-requests ()
+(ert-deftest tramp-test44-asynchronous-requests ()
   "Check parallel asynchronous requests.
 Such requests could arrive from timers, process filters and
 process sentinels.  They shall not disturb each other."
@@ -6626,10 +6773,10 @@ process sentinels.  They shall not disturb each other."
         (ignore-errors (cancel-timer timer))
         (ignore-errors (delete-directory tmp-name 'recursive))))))
 
-;; (tramp--test--deftest-direct-async-process 
tramp-test43-asynchronous-requests
+;; (tramp--test--deftest-direct-async-process 
tramp-test44-asynchronous-requests
 ;;   "Check parallel direct asynchronous requests." 'unstable)
 
-(ert-deftest tramp-test44-threads ()
+(ert-deftest tramp-test45-threads ()
   "Check that Tramp cooperates with threads."
   (skip-unless (tramp--test-enabled))
   (skip-unless (featurep 'threads))
@@ -6738,7 +6885,7 @@ process sentinels.  They shall not disturb each other."
        (ignore-errors (thread-last-error 'cleanup)))))))
 
 ;; This test is inspired by Bug#29163.
-(ert-deftest tramp-test45-auto-load ()
+(ert-deftest tramp-test46-auto-load ()
   "Check that Tramp autoloads properly."
   ;; If we use another syntax but `default', Tramp is already loaded
   ;; due to the `tramp-change-syntax' call.
@@ -6763,7 +6910,7 @@ process sentinels.  They shall not disturb each other."
        (mapconcat #'shell-quote-argument load-path " -L ")
        (shell-quote-argument code)))))))
 
-(ert-deftest tramp-test45-delay-load ()
+(ert-deftest tramp-test46-delay-load ()
   "Check that Tramp is loaded lazily, only when needed."
   ;; The autoloaded Tramp objects are different since Emacs 26.1.  We
   ;; cannot test older Emacsen, therefore.
@@ -6796,7 +6943,7 @@ process sentinels.  They shall not disturb each other."
          (mapconcat #'shell-quote-argument load-path " -L ")
          (shell-quote-argument (format code tm)))))))))
 
-(ert-deftest tramp-test45-recursive-load ()
+(ert-deftest tramp-test46-recursive-load ()
   "Check that Tramp does not fail due to recursive load."
   (skip-unless (tramp--test-enabled))
 
@@ -6820,7 +6967,7 @@ process sentinels.  They shall not disturb each other."
          (mapconcat #'shell-quote-argument load-path " -L ")
          (shell-quote-argument code))))))))
 
-(ert-deftest tramp-test45-remote-load-path ()
+(ert-deftest tramp-test46-remote-load-path ()
   "Check that Tramp autoloads its packages with remote `load-path'."
   ;; The autoloaded Tramp objects are different since Emacs 26.1.  We
   ;; cannot test older Emacsen, therefore.
@@ -6849,7 +6996,7 @@ process sentinels.  They shall not disturb each other."
        (mapconcat #'shell-quote-argument load-path " -L ")
        (shell-quote-argument code)))))))
 
-(ert-deftest tramp-test46-unload ()
+(ert-deftest tramp-test47-unload ()
   "Check that Tramp and its subpackages unload completely.
 Since it unloads Tramp, it shall be the last test to run."
   :tags '(:expensive-test)
@@ -6932,8 +7079,8 @@ If INTERACTIVE is non-nil, the tests are run 
interactively."
 ;; * Implement `tramp-test31-interrupt-process' for `adb', `sshfs' and
 ;;   for direct async processes.
 ;; * Check, why direct async processes do not work for
-;;   `tramp-test43-asynchronous-requests'.
-;; * Fix `tramp-test44-threads'.
+;;   `tramp-test44-asynchronous-requests'.
+;; * Fix `tramp-test45-threads'.
 
 (provide 'tramp-tests)
 
diff --git a/texi/tramp.texi b/texi/tramp.texi
index 9c20b3d..6aa5260 100644
--- a/texi/tramp.texi
+++ b/texi/tramp.texi
@@ -142,7 +142,8 @@ Configuring @value{tramp} for use
 * Remote shell setup::          Remote shell setup hints.
 * FUSE setup::                  @acronym{FUSE} setup hints.
 * Android shell setup::         Android shell setup hints.
-* Auto-save and Backup::        Auto-save and Backup.
+* Auto-save File Lock and Backup::
+                                Auto-save, File Lock and Backup.
 * Keeping files encrypted::     Protect remote files by encryption.
 * Windows setup hints::         Issues with Cygwin ssh.
 
@@ -691,7 +692,8 @@ may be used in your init file:
 * Remote shell setup::          Remote shell setup hints.
 * FUSE setup::                  @acronym{FUSE} setup hints.
 * Android shell setup::         Android shell setup hints.
-* Auto-save and Backup::        Auto-save and Backup.
+* Auto-save File Lock and Backup::
+                                Auto-save, File Lock and Backup.
 * Keeping files encrypted::     Protect remote files by encryption.
 * Windows setup hints::         Issues with Cygwin ssh.
 @end menu
@@ -2745,9 +2747,10 @@ Open a remote connection with a more concise command 
@kbd{C-x C-f
 @end itemize
 
 
-@node Auto-save and Backup
-@section Auto-save and Backup configuration
+@node Auto-save File Lock and Backup
+@section Auto-save, File Lock and Backup configuration
 @cindex auto-save
+@cindex file-lock
 @cindex backup
 
 @vindex backup-directory-alist
@@ -2842,11 +2845,28 @@ auto-saved files to the same directory as the original 
file.
 Alternatively, set the user option @code{tramp-auto-save-directory}
 to direct all auto saves to that location.
 
+@vindex lock-file-name-transforms
+And still more issues to handle.  Since @w{Emacs 28}, file locks use a
+similar user option as auto-save files, called
+@code{lock-file-name-transforms}.  By default this user option is
+@code{nil}, meaning to keep file locks in the same directory as the
+original file.
+
+If you change @code{lock-file-name-transforms} in order to keep file
+locks for remote files somewhere else, you will loose Emacs' feature
+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}.
 
 
@@ -4235,7 +4255,9 @@ test, @ref{Cleanup remote connections}.  Alternatively, 
and often
 better for analysis, reproduce the problem in a clean Emacs session
 started with @command{emacs -Q}.  Then, @value{tramp} does not load
 the persistency file (@pxref{Connection caching}), and it does not use
-passwords from @file{auth-source.el} (@pxref{Password handling}).
+passwords from @file{auth-source.el} (@pxref{Password handling}).  The
+latter does not happen for the @option{sudoedit} method, otherwise it
+would be unusable.
 
 When including @value{tramp}'s messages in the bug report, increase
 the verbosity level to 6 (@pxref{Traces and Profiles, Traces}) in the
diff --git a/texi/trampver.texi b/texi/trampver.texi
index 10c951d..653ffab 100644
--- a/texi/trampver.texi
+++ b/texi/trampver.texi
@@ -8,7 +8,7 @@
 @c In the Tramp GIT, the version numbers are auto-frobbed from
 @c tramp.el, and the bug report address is auto-frobbed from
 @c configure.ac.
-@set trampver 2.5.1
+@set trampver 2.5.1.1
 @set trampurl https://www.gnu.org/software/tramp/
 @set tramp-bug-report-address tramp-devel@@gnu.org
 @set emacsver 25.1
diff --git a/tramp-adb.el b/tramp-adb.el
index 7fb0ff5..5e0accc 100644
--- a/tramp-adb.el
+++ b/tramp-adb.el
@@ -133,6 +133,7 @@ It is used for TCP/IP devices."
     (file-exists-p . tramp-handle-file-exists-p)
     (file-in-directory-p . tramp-handle-file-in-directory-p)
     (file-local-copy . tramp-adb-handle-file-local-copy)
+    (file-locked-p . tramp-handle-file-locked-p)
     (file-modes . tramp-handle-file-modes)
     (file-name-all-completions . tramp-adb-handle-file-name-all-completions)
     (file-name-as-directory . tramp-handle-file-name-as-directory)
@@ -159,9 +160,11 @@ It is used for TCP/IP devices."
     (insert-directory . tramp-handle-insert-directory)
     (insert-file-contents . tramp-handle-insert-file-contents)
     (load . tramp-handle-load)
+    (lock-file . tramp-handle-lock-file)
     (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 . 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)
@@ -180,6 +183,7 @@ It is used for TCP/IP devices."
     (tramp-get-remote-uid . ignore)
     (tramp-set-file-uid-gid . ignore)
     (unhandled-file-name-directory . ignore)
+    (unlock-file . tramp-handle-unlock-file)
     (vc-registered . ignore)
     (verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime)
     (write-region . tramp-adb-handle-write-region))
@@ -323,9 +327,9 @@ arguments to pass to the OPERATION."
                v (format "%s -d -a -l %s %s"
                          (tramp-adb-get-ls-command v)
                          (tramp-shell-quote-argument
-                          (concat (file-name-as-directory localname) "."))
+                          (tramp-compat-file-name-concat localname "."))
                          (tramp-shell-quote-argument
-                          (concat (file-name-as-directory localname) ".."))))
+                          (tramp-compat-file-name-concat localname ".."))))
               (widen)))
           (tramp-adb-sh-fix-ls-output)
           (let ((result (tramp-do-parse-file-attributes-with-ls
@@ -535,7 +539,8 @@ But handle the case, if the \"test\" command is not 
available."
 (defun tramp-adb-handle-write-region
   (start end filename &optional append visit lockname mustbenew)
   "Like `write-region' for Tramp files."
-  (setq filename (expand-file-name filename))
+  (setq filename (expand-file-name filename)
+       lockname (file-truename (or lockname filename)))
   (with-parsed-tramp-file-name filename nil
     (when (and mustbenew (file-exists-p filename)
               (or (eq mustbenew 'excl)
@@ -544,15 +549,26 @@ But handle the case, if the \"test\" command is not 
available."
                     (format "File %s exists; overwrite anyway? " filename)))))
       (tramp-error v 'file-already-exists filename))
 
-    (let* ((curbuf (current-buffer))
-          (tmpfile (tramp-compat-make-temp-file filename)))
+    (let ((file-locked (eq (file-locked-p lockname) t))
+         (curbuf (current-buffer))
+         (tmpfile (tramp-compat-make-temp-file filename)))
+
+      ;; Lock file.
+      (when (and (not (auto-save-file-name-p (file-name-nondirectory 
filename)))
+                (file-remote-p lockname)
+                (not file-locked))
+       (setq file-locked t)
+       ;; `lock-file' exists since Emacs 28.1.
+       (tramp-compat-funcall 'lock-file lockname))
+
       (when (and append (file-exists-p filename))
        (copy-file filename tmpfile 'ok)
        (set-file-modes tmpfile (logior (or (file-modes tmpfile) 0) #o0600)))
-      (write-region start end tmpfile append 'no-message lockname)
+      (let (create-lockfiles)
+        (write-region start end tmpfile append 'no-message))
       (with-tramp-progress-reporter
-        v 3 (format-message
-             "Moving tmp file `%s' to `%s'" tmpfile filename)
+         v 3 (format-message
+              "Moving tmp file `%s' to `%s'" tmpfile filename)
        (unwind-protect
            (unless (tramp-adb-execute-adb-command
                     v "push" tmpfile (tramp-compat-file-name-unquote 
localname))
@@ -575,6 +591,11 @@ But handle the case, if the \"test\" command is not 
available."
              (file-attributes filename))
             (current-time))))
 
+      ;; Unlock file.
+      (when file-locked
+       ;; `unlock-file' exists since Emacs 28.1.
+       (tramp-compat-funcall 'unlock-file lockname))
+
       ;; The end.
       (when (and (null noninteractive)
                 (or (eq visit t) (null visit) (stringp visit)))
@@ -782,7 +803,7 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are 
completely ignored."
   (when (and (numberp destination) (zerop destination))
     (error "Implementation does not handle immediate return"))
 
-  (with-parsed-tramp-file-name default-directory nil
+  (with-parsed-tramp-file-name (expand-file-name default-directory) nil
     (let (command input tmpinput stderr tmpstderr outbuf ret)
       ;; Compute command.
       (setq command (mapconcat #'tramp-shell-quote-argument
diff --git a/tramp-archive.el b/tramp-archive.el
index d723fd5..67798e8 100644
--- a/tramp-archive.el
+++ b/tramp-archive.el
@@ -236,6 +236,7 @@ It must be supported by libarchive(3).")
     (file-exists-p . tramp-handle-file-exists-p)
     (file-in-directory-p . tramp-handle-file-in-directory-p)
     (file-local-copy . tramp-archive-handle-file-local-copy)
+    (file-locked-p . ignore)
     (file-modes . tramp-handle-file-modes)
     (file-name-all-completions . 
tramp-archive-handle-file-name-all-completions)
     ;; `file-name-as-directory' performed by default handler.
@@ -262,9 +263,11 @@ It must be supported by libarchive(3).")
     (insert-directory . tramp-archive-handle-insert-directory)
     (insert-file-contents . tramp-archive-handle-insert-file-contents)
     (load . tramp-archive-handle-load)
+    (lock-file . ignore)
     (make-auto-save-file-name . ignore)
     (make-directory . tramp-archive-handle-not-implemented)
     (make-directory-internal . tramp-archive-handle-not-implemented)
+    (make-lock-file-name . ignore)
     (make-nearby-temp-file . tramp-handle-make-nearby-temp-file)
     (make-process . ignore)
     (make-symbolic-link . tramp-archive-handle-not-implemented)
@@ -283,6 +286,7 @@ It must be supported by libarchive(3).")
     (tramp-get-remote-uid . ignore)
     (tramp-set-file-uid-gid . ignore)
     (unhandled-file-name-directory . ignore)
+    (unlock-file . ignore)
     (vc-registered . ignore)
     (verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime)
     (write-region . tramp-archive-handle-not-implemented))
diff --git a/tramp-cache.el b/tramp-cache.el
index 2d11c89..7b735c8 100644
--- a/tramp-cache.el
+++ b/tramp-cache.el
@@ -49,6 +49,8 @@
 ;;   an open connection.  Examples: "scripts" keeps shell script
 ;;   definitions already sent to the remote shell, "last-cmd-time" is
 ;;   the time stamp a command has been sent to the remote process.
+;;   "lock-pid" is the timestamp a (network) process is created, it is
+;;   used instead of the pid in file locks.
 ;;
 ;; - The key is nil.  These are temporary properties related to the
 ;;   local machine.  Examples: "parse-passwd" and "parse-group" keep
@@ -70,8 +72,8 @@
 ;;   process key retrieved by `tramp-get-process' (the main connection
 ;;   process).  Other processes could reuse these properties, avoiding
 ;;   recomputation when a new asynchronous process is created by
-;;   `make-process'.  Examples are "remote-path",
-;;   "unsafe-temporary-file" or "device" (tramp-adb.el).
+;;   `make-process'.  Examples are "unsafe-temporary-file",
+;;   "remote-path", "device" (tramp-adb.el) or "share" (tramp-gvfs.el).
 
 ;;; Code:
 
diff --git a/tramp-compat.el b/tramp-compat.el
index f727b8e..37de988 100644
--- a/tramp-compat.el
+++ b/tramp-compat.el
@@ -378,6 +378,30 @@ A nil value for either argument stands for the current 
time."
     (lambda (fromstring tostring instring)
       (replace-regexp-in-string (regexp-quote fromstring) tostring instring))))
 
+;; Function `make-lock-file-name' is new in Emacs 28.1.
+(defalias 'tramp-compat-make-lock-file-name
+  (if (fboundp 'make-lock-file-name)
+      #'make-lock-file-name
+    (lambda (filename)
+      (expand-file-name
+       (concat
+        ".#" (file-name-nondirectory filename))
+       (file-name-directory filename)))))
+
+;; Function `file-name-concat' is new in Emacs 28.1.
+(defalias 'tramp-compat-file-name-concat
+  (if (fboundp 'file-name-concat)
+      #'file-name-concat
+    (lambda (directory &rest components)
+      (unless (null directory)
+       (let ((components (delq nil components))
+             file-name-handler-alist)
+         (if (null components)
+             directory
+           (tramp-compat-file-name-concat
+            (concat (file-name-as-directory directory) (car components))
+            (cdr components))))))))
+
 (dolist (elt (all-completions "tramp-compat-" obarray 'functionp))
   (put (intern elt) 'tramp-suppress-trace t))
 
diff --git a/tramp-crypt.el b/tramp-crypt.el
index 1d8c0ad..fdb2907 100644
--- a/tramp-crypt.el
+++ b/tramp-crypt.el
@@ -182,6 +182,7 @@ If NAME doesn't belong to a crypted remote directory, retun 
nil."
     (file-exists-p . tramp-handle-file-exists-p)
     (file-in-directory-p . tramp-handle-file-in-directory-p)
     (file-local-copy . tramp-handle-file-local-copy)
+    (file-locked-p . tramp-crypt-handle-file-locked-p)
     (file-modes . tramp-handle-file-modes)
     (file-name-all-completions . tramp-crypt-handle-file-name-all-completions)
     ;; `file-name-as-directory' performed by default handler.
@@ -208,9 +209,11 @@ If NAME doesn't belong to a crypted remote directory, 
retun nil."
     (insert-directory . tramp-crypt-handle-insert-directory)
     ;; `insert-file-contents' performed by default handler.
     (load . tramp-handle-load)
+    (lock-file . tramp-crypt-handle-lock-file)
     (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 . 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)
@@ -229,6 +232,7 @@ If NAME doesn't belong to a crypted remote directory, retun 
nil."
     ;; `tramp-get-remote-uid' performed by default handler.
     (tramp-set-file-uid-gid . tramp-crypt-handle-set-file-uid-gid)
     (unhandled-file-name-directory . ignore)
+    (unlock-file . tramp-crypt-handle-unlock-file)
     (vc-registered . ignore)
     (verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime)
     (write-region . tramp-handle-write-region))
@@ -734,6 +738,11 @@ absolute file names."
   (let (tramp-crypt-enabled)
     (file-executable-p (tramp-crypt-encrypt-file-name filename))))
 
+(defun tramp-crypt-handle-file-locked-p (filename)
+  "Like `file-locked-p' for Tramp files."
+  (let (tramp-crypt-enabled)
+    (file-locked-p (tramp-crypt-encrypt-file-name filename))))
+
 (defun tramp-crypt-handle-file-name-all-completions (filename directory)
   "Like `file-name-all-completions' for Tramp files."
   (all-completions
@@ -797,6 +806,13 @@ WILDCARD is not supported."
          (delete-region (prop-match-beginning match) (prop-match-end match))
          (insert (propertize string 'dired-filename t)))))))
 
+(defun tramp-crypt-handle-lock-file (filename)
+  "Like `lock-file' for Tramp files."
+  (let (tramp-crypt-enabled)
+    ;; `lock-file' exists since Emacs 28.1.
+    (tramp-compat-funcall
+     'lock-file (tramp-crypt-encrypt-file-name filename))))
+
 (defun tramp-crypt-handle-make-directory (dir &optional parents)
   "Like `make-directory' for Tramp files."
   (with-parsed-tramp-file-name (expand-file-name dir) nil
@@ -848,6 +864,13 @@ WILDCARD is not supported."
       (tramp-set-file-uid-gid
        (tramp-crypt-encrypt-file-name filename) uid gid))))
 
+(defun tramp-crypt-handle-unlock-file (filename)
+  "Like `unlock-file' for Tramp files."
+  (let (tramp-crypt-enabled)
+    ;; `unlock-file' exists since Emacs 28.1.
+    (tramp-compat-funcall
+     'unlock-file (tramp-crypt-encrypt-file-name filename))))
+
 (add-hook 'tramp-unload-hook
          (lambda ()
            (unload-feature 'tramp-crypt 'force)))
diff --git a/tramp-fuse.el b/tramp-fuse.el
index ec1db86..93b184a 100644
--- a/tramp-fuse.el
+++ b/tramp-fuse.el
@@ -164,10 +164,9 @@
     (or (tramp-get-connection-property
          (tramp-get-connection-process vec) "mounted" nil)
         (let* ((default-directory (tramp-compat-temporary-file-directory))
-               (fuse (concat "fuse." (tramp-file-name-method vec)))
-               (mount (shell-command-to-string (format "mount -t %s" fuse))))
-          (tramp-message vec 6 "%s %s" "mount -t" fuse)
-          (tramp-message vec 6 "\n%s" mount)
+               (command (format "mount -t fuse.%s" (tramp-file-name-method 
vec)))
+              (mount (shell-command-to-string command)))
+          (tramp-message vec 6 "%s\n%s" command mount)
           (tramp-set-connection-property
            (tramp-get-connection-process vec) "mounted"
            (when (string-match
@@ -176,6 +175,16 @@
                  mount)
              (match-string 1 mount)))))))
 
+(defun tramp-fuse-unmount (vec)
+  "Unmount fuse volume determined by VEC."
+  (let ((default-directory (tramp-compat-temporary-file-directory))
+        (command (format "fusermount3 -u %s" (tramp-fuse-mount-point vec))))
+    (tramp-message vec 6 "%s\n%s" command (shell-command-to-string command))
+    (tramp-flush-connection-property
+     (tramp-get-connection-process vec) "mounted")
+    ;; Give the caches a chance to expire.
+    (sleep-for 1)))
+
 (defun tramp-fuse-local-file-name (filename)
   "Return local mount name of FILENAME."
   (setq filename (tramp-compat-file-name-unquote (expand-file-name filename)))
diff --git a/tramp-gvfs.el b/tramp-gvfs.el
index f1d24dc..eff14a2 100644
--- a/tramp-gvfs.el
+++ b/tramp-gvfs.el
@@ -774,6 +774,7 @@ It has been changed in GVFS 1.14.")
     (file-exists-p . tramp-handle-file-exists-p)
     (file-in-directory-p . tramp-handle-file-in-directory-p)
     (file-local-copy . tramp-handle-file-local-copy)
+    (file-locked-p . tramp-handle-file-locked-p)
     (file-modes . tramp-handle-file-modes)
     (file-name-all-completions . tramp-gvfs-handle-file-name-all-completions)
     (file-name-as-directory . tramp-handle-file-name-as-directory)
@@ -800,9 +801,11 @@ It has been changed in GVFS 1.14.")
     (insert-directory . tramp-handle-insert-directory)
     (insert-file-contents . tramp-handle-insert-file-contents)
     (load . tramp-handle-load)
+    (lock-file . tramp-handle-lock-file)
     (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 . 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)
@@ -821,6 +824,7 @@ It has been changed in GVFS 1.14.")
     (tramp-get-remote-uid . tramp-gvfs-handle-get-remote-uid)
     (tramp-set-file-uid-gid . tramp-gvfs-handle-set-file-uid-gid)
     (unhandled-file-name-directory . ignore)
+    (unlock-file . tramp-handle-unlock-file)
     (vc-registered . ignore)
     (verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime)
     (write-region . tramp-handle-write-region))
@@ -1138,7 +1142,7 @@ file names."
   (when (zerop (length name)) (setq name "."))
   ;; Unless NAME is absolute, concat DIR and NAME.
   (unless (file-name-absolute-p name)
-    (setq name (concat (file-name-as-directory dir) name)))
+    (setq name (tramp-compat-file-name-concat dir name)))
   ;; If NAME is not a Tramp file, run the real handler.
   (if (not (tramp-tramp-file-p name))
       (tramp-run-real-handler #'expand-file-name (list name nil))
@@ -1629,8 +1633,10 @@ If FILE-SYSTEM is non-nil, return file system 
attributes."
 ID-FORMAT valid values are `string' and `integer'."
   (if (equal id-format 'string)
       (tramp-file-name-user vec)
-    (when-let
-       ((localname (tramp-get-connection-property vec "default-location" nil)))
+    (when-let ((localname
+               (tramp-get-connection-property
+                (tramp-get-process vec) "share"
+                (tramp-get-connection-property vec "default-location" nil))))
       (tramp-compat-file-attribute-user-id
        (file-attributes
        (tramp-make-tramp-file-name vec localname) id-format)))))
@@ -1638,8 +1644,10 @@ ID-FORMAT valid values are `string' and `integer'."
 (defun tramp-gvfs-handle-get-remote-gid (vec id-format)
   "The gid of the remote connection VEC, in ID-FORMAT.
 ID-FORMAT valid values are `string' and `integer'."
-  (when-let
-      ((localname (tramp-get-connection-property vec "default-location" nil)))
+  (when-let ((localname
+             (tramp-get-connection-property
+              (tramp-get-process vec) "share"
+              (tramp-get-connection-property vec "default-location" nil))))
     (tramp-compat-file-attribute-group-id
      (file-attributes
       (tramp-make-tramp-file-name vec localname) id-format))))
@@ -1993,6 +2001,9 @@ a downcased host name only."
           (tramp-set-file-property vec "/" "fuse-mountpoint" fuse-mountpoint)
           (tramp-set-connection-property
            vec "default-location" default-location)
+          (when share
+            (tramp-set-connection-property
+             (tramp-get-process vec) "share" (concat "/" share)))
           (throw 'mounted t)))))))
 
 (defun tramp-gvfs-unmount (vec)
@@ -2144,6 +2155,9 @@ connection if a previous connection has died for some 
reason."
       (process-put p 'vector vec)
       (set-process-query-on-exit-flag p nil)
 
+      ;; Mark process for filelock.
+      (tramp-set-connection-property p "lock-pid" (truncate (time-to-seconds)))
+
       ;; Set connection-local variables.
       (tramp-set-connection-local-variables vec)))
 
diff --git a/tramp-rclone.el b/tramp-rclone.el
index 3b6de3e..49e366c 100644
--- a/tramp-rclone.el
+++ b/tramp-rclone.el
@@ -96,6 +96,7 @@
     (file-exists-p . tramp-handle-file-exists-p)
     (file-in-directory-p . tramp-handle-file-in-directory-p)
     (file-local-copy . tramp-handle-file-local-copy)
+    (file-locked-p . tramp-handle-file-locked-p)
     (file-modes . tramp-handle-file-modes)
     (file-name-all-completions . tramp-fuse-handle-file-name-all-completions)
     (file-name-as-directory . tramp-handle-file-name-as-directory)
@@ -122,9 +123,11 @@
     (insert-directory . tramp-handle-insert-directory)
     (insert-file-contents . tramp-handle-insert-file-contents)
     (load . tramp-handle-load)
+    (lock-file . tramp-handle-lock-file)
     (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 . 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)
@@ -143,6 +146,7 @@
     (tramp-get-remote-uid . ignore)
     (tramp-set-file-uid-gid . ignore)
     (unhandled-file-name-directory . ignore)
+    (unlock-file . tramp-handle-unlock-file)
     (vc-registered . ignore)
     (verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime)
     (write-region . tramp-handle-write-region))
@@ -358,6 +362,10 @@ connection if a previous connection has died for some 
reason."
          (process-put p 'vector vec)
          (set-process-query-on-exit-flag p nil)
 
+         ;; Mark process for filelock.
+         (tramp-set-connection-property
+          p "lock-pid" (truncate (time-to-seconds)))
+
          ;; Set connection-local variables.
          (tramp-set-connection-local-variables vec)))
 
diff --git a/tramp-sh.el b/tramp-sh.el
index ebd0fbf..7cf90b9 100644
--- a/tramp-sh.el
+++ b/tramp-sh.el
@@ -519,6 +519,7 @@ shell from reading its init file."
     (tramp-yn-prompt-regexp tramp-action-yn)
     (tramp-terminal-prompt-regexp tramp-action-terminal)
     (tramp-antispoof-regexp tramp-action-confirm-message)
+    (tramp-yubikey-regexp tramp-action-show-and-confirm-message)
     (tramp-process-alive-regexp tramp-action-process-alive))
   "List of pattern/action pairs.
 Whenever a pattern matches, the corresponding action is performed.
@@ -536,6 +537,7 @@ corresponding PATTERN matches, the ACTION function is 
called.")
   '((tramp-password-prompt-regexp tramp-action-password)
     (tramp-wrong-passwd-regexp tramp-action-permission-denied)
     (tramp-copy-failed-regexp tramp-action-permission-denied)
+    (tramp-yubikey-regexp tramp-action-show-and-confirm-message)
     (tramp-process-alive-regexp tramp-action-out-of-band))
   "List of pattern/action pairs.
 This list is used for copying/renaming with out-of-band methods.
@@ -962,6 +964,7 @@ Format specifiers \"%s\" are replaced before the script is 
used.")
     (file-exists-p . tramp-sh-handle-file-exists-p)
     (file-in-directory-p . tramp-handle-file-in-directory-p)
     (file-local-copy . tramp-sh-handle-file-local-copy)
+    (file-locked-p . tramp-handle-file-locked-p)
     (file-modes . tramp-handle-file-modes)
     (file-name-all-completions . tramp-sh-handle-file-name-all-completions)
     (file-name-as-directory . tramp-handle-file-name-as-directory)
@@ -988,9 +991,11 @@ Format specifiers \"%s\" are replaced before the script is 
used.")
     (insert-directory . tramp-sh-handle-insert-directory)
     (insert-file-contents . tramp-handle-insert-file-contents)
     (load . tramp-handle-load)
+    (lock-file . tramp-handle-lock-file)
     (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 . 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)
@@ -1009,6 +1014,7 @@ Format specifiers \"%s\" are replaced before the script 
is used.")
     (tramp-get-remote-uid . tramp-sh-handle-get-remote-uid)
     (tramp-set-file-uid-gid . tramp-sh-handle-set-file-uid-gid)
     (unhandled-file-name-directory . ignore)
+    (unlock-file . tramp-handle-unlock-file)
     (vc-registered . tramp-sh-handle-vc-registered)
     (verify-visited-file-modtime . tramp-sh-handle-verify-visited-file-modtime)
     (write-region . tramp-sh-handle-write-region))
@@ -1940,7 +1946,7 @@ file names."
          (length (tramp-compat-file-attribute-size
                   (file-attributes (file-truename filename))))
          (attributes (and preserve-extended-attributes
-                          (apply #'file-extended-attributes (list filename))))
+                          (file-extended-attributes filename)))
          (msg-operation (if (eq op 'copy) "Copying" "Renaming")))
 
       (with-parsed-tramp-file-name (if t1 filename newname) nil
@@ -2016,7 +2022,7 @@ file names."
          ;; errors, because ACL strings could be incompatible.
          (when attributes
            (ignore-errors
-             (apply #'set-file-extended-attributes (list newname attributes))))
+             (set-file-extended-attributes newname attributes)))
 
          ;; In case of `rename', we must flush the cache of the source file.
          (when (and t1 (eq op 'rename))
@@ -2667,56 +2673,63 @@ the result will be a local, non-Tramp, file name."
   (setq dir (or dir default-directory "/"))
   ;; Handle empty NAME.
   (when (zerop (length name)) (setq name "."))
-  ;; Unless NAME is absolute, concat DIR and NAME.
-  (unless (file-name-absolute-p name)
-    (setq name (concat (file-name-as-directory dir) name)))
-  ;; If connection is not established yet, run the real handler.
-  (if (not (tramp-connectable-p name))
-      (tramp-run-real-handler #'expand-file-name (list name nil))
-    ;; Dissect NAME.
-    (with-parsed-tramp-file-name name nil
-      (unless (tramp-run-real-handler #'file-name-absolute-p (list localname))
-       (setq localname (concat "~/" localname)))
-      ;; Tilde expansion if necessary.  This needs a shell which
-      ;; groks tilde expansion!  The function `tramp-find-shell' is
-      ;; supposed to find such a shell on the remote host.  Please
-      ;; tell me about it when this doesn't work on your system.
-      (when (string-match "\\`\\(~[^/]*\\)\\(.*\\)\\'" localname)
-       (let ((uname (match-string 1 localname))
-             (fname (match-string 2 localname)))
-         ;; We cannot simply apply "~/", because under sudo "~/" is
-         ;; expanded to the local user home directory but to the
-         ;; root home directory.  On the other hand, using always
-         ;; the default user name for tilde expansion is not
-         ;; appropriate either, because ssh and companions might
-         ;; use a user name from the config file.
-         (when (and (string-equal uname "~")
-                    (string-match-p "\\`su\\(do\\)?\\'" method))
-           (setq uname (concat uname user)))
-         (setq uname
-               (with-tramp-connection-property v uname
-                 (tramp-send-command
-                  v (format "cd %s && pwd" (tramp-shell-quote-argument uname)))
-                 (with-current-buffer (tramp-get-buffer v)
-                   (goto-char (point-min))
-                   (buffer-substring (point) (point-at-eol)))))
-         (setq localname (concat uname fname))))
-      ;; There might be a double slash, for example when "~/"
-      ;; expands to "/".  Remove this.
-      (while (string-match "//" localname)
-       (setq localname (replace-match "/" t t localname)))
-      ;; Do not keep "/..".
-      (when (string-match-p "^/\\.\\.?$" localname)
-       (setq localname "/"))
-      ;; No tilde characters in file name, do normal
-      ;; `expand-file-name' (this does "/./" and "/../").
-      ;; `default-directory' is bound, because on Windows there would
-      ;; be problems with UNC shares or Cygwin mounts.
-      (let ((default-directory (tramp-compat-temporary-file-directory)))
-       (tramp-make-tramp-file-name
-        v (tramp-drop-volume-letter
-           (tramp-run-real-handler
-            #'expand-file-name (list localname))))))))
+  ;; On MS Windows, some special file names are not returned properly
+  ;; by `file-name-absolute-p'.
+  (if (and (eq system-type 'windows-nt)
+          (string-match-p
+           (concat "^\\([[:alpha:]]:\\|" null-device "$\\)") name))
+      (tramp-run-real-handler #'expand-file-name (list name dir))
+    ;; Unless NAME is absolute, concat DIR and NAME.
+    (unless (file-name-absolute-p name)
+      (setq name (tramp-compat-file-name-concat dir name)))
+    ;; If connection is not established yet, run the real handler.
+    (if (not (tramp-connectable-p name))
+       (tramp-run-real-handler #'expand-file-name (list name nil))
+      ;; Dissect NAME.
+      (with-parsed-tramp-file-name name nil
+       (unless (tramp-run-real-handler #'file-name-absolute-p (list localname))
+         (setq localname (concat "~/" localname)))
+       ;; Tilde expansion if necessary.  This needs a shell which
+       ;; groks tilde expansion!  The function `tramp-find-shell' is
+       ;; supposed to find such a shell on the remote host.  Please
+       ;; tell me about it when this doesn't work on your system.
+       (when (string-match "\\`\\(~[^/]*\\)\\(.*\\)\\'" localname)
+         (let ((uname (match-string 1 localname))
+               (fname (match-string 2 localname)))
+           ;; We cannot simply apply "~/", because under sudo "~/" is
+           ;; expanded to the local user home directory but to the
+           ;; root home directory.  On the other hand, using always
+           ;; the default user name for tilde expansion is not
+           ;; appropriate either, because ssh and companions might
+           ;; use a user name from the config file.
+           (when (and (string-equal uname "~")
+                      (string-match-p "\\`su\\(do\\)?\\'" method))
+             (setq uname (concat uname user)))
+           (setq uname
+                 (with-tramp-connection-property v uname
+                   (tramp-send-command
+                    v
+                    (format "cd %s && pwd" (tramp-shell-quote-argument uname)))
+                   (with-current-buffer (tramp-get-buffer v)
+                     (goto-char (point-min))
+                     (buffer-substring (point) (point-at-eol)))))
+           (setq localname (concat uname fname))))
+       ;; There might be a double slash, for example when "~/"
+       ;; expands to "/".  Remove this.
+       (while (string-match "//" localname)
+         (setq localname (replace-match "/" t t localname)))
+       ;; Do not keep "/..".
+       (when (string-match-p "^/\\.\\.?$" localname)
+         (setq localname "/"))
+       ;; No tilde characters in file name, do normal
+       ;; `expand-file-name' (this does "/./" and "/../").
+       ;; `default-directory' is bound, because on Windows there
+       ;; would be problems with UNC shares or Cygwin mounts.
+       (let ((default-directory (tramp-compat-temporary-file-directory)))
+         (tramp-make-tramp-file-name
+          v (tramp-drop-volume-letter
+             (tramp-run-real-handler
+              #'expand-file-name (list localname)))))))))
 
 ;;; Remote commands:
 
@@ -3018,7 +3031,7 @@ implementation will be used."
   (when (and (numberp destination) (zerop destination))
     (error "Implementation does not handle immediate return"))
 
-  (with-parsed-tramp-file-name default-directory nil
+  (with-parsed-tramp-file-name (expand-file-name default-directory) nil
     (let (command env uenv input tmpinput stderr tmpstderr outbuf ret)
       ;; Compute command.
       (setq command (mapconcat #'tramp-shell-quote-argument
@@ -3228,7 +3241,8 @@ implementation will be used."
 (defun tramp-sh-handle-write-region
   (start end filename &optional append visit lockname mustbenew)
   "Like `write-region' for Tramp files."
-  (setq filename (expand-file-name filename))
+  (setq filename (expand-file-name filename)
+       lockname (file-truename (or lockname filename)))
   (with-parsed-tramp-file-name filename nil
     (when (and mustbenew (file-exists-p filename)
               (or (eq mustbenew 'excl)
@@ -3237,23 +3251,31 @@ implementation will be used."
                     (format "File %s exists; overwrite anyway? " filename)))))
       (tramp-error v 'file-already-exists filename))
 
-    (let ((uid (or (tramp-compat-file-attribute-user-id
+    (let ((file-locked (eq (file-locked-p lockname) t))
+         (uid (or (tramp-compat-file-attribute-user-id
                    (file-attributes filename 'integer))
                   (tramp-get-remote-uid v 'integer)))
          (gid (or (tramp-compat-file-attribute-group-id
                    (file-attributes filename 'integer))
                   (tramp-get-remote-gid v 'integer))))
 
+      ;; Lock file.
+      (when (and (not (auto-save-file-name-p (file-name-nondirectory 
filename)))
+                (file-remote-p lockname)
+                (not file-locked))
+       (setq file-locked t)
+       ;; `lock-file' exists since Emacs 28.1.
+       (tramp-compat-funcall 'lock-file lockname))
+
       (if (and (tramp-local-host-p v)
               ;; `file-writable-p' calls `file-expand-file-name'.  We
               ;; cannot use `tramp-run-real-handler' therefore.
-              (let (file-name-handler-alist)
-                (and
-                 (file-writable-p (file-name-directory localname))
-                 (or (file-directory-p localname)
-                     (file-writable-p localname)))))
+              (file-writable-p (file-name-directory localname))
+              (or (file-directory-p localname)
+                  (file-writable-p localname)))
          ;; Short track: if we are on the local host, we can run directly.
-         (write-region start end localname append 'no-message lockname)
+         (let ((create-lockfiles (not file-locked)))
+           (write-region start end localname append 'no-message lockname))
 
        (let* ((modes (tramp-default-file-modes
                       filename (and (eq mustbenew 'excl) 'nofollow)))
@@ -3287,9 +3309,10 @@ implementation will be used."
          ;; on.  We must ensure that `file-coding-system-alist'
          ;; matches `tmpfile'.
          (let ((file-coding-system-alist
-                (tramp-find-file-name-coding-system-alist filename tmpfile)))
+                (tramp-find-file-name-coding-system-alist filename tmpfile))
+                create-lockfiles)
            (condition-case err
-               (write-region start end tmpfile append 'no-message lockname)
+               (write-region start end tmpfile append 'no-message)
              ((error quit)
               (setq tramp-temp-buffer-file-name nil)
               (delete-file tmpfile)
@@ -3458,6 +3481,12 @@ implementation will be used."
        ;; Set the ownership.
         (when need-chown
           (tramp-set-file-uid-gid filename uid gid))
+
+       ;; Unlock file.
+       (when file-locked
+         ;; `unlock-file' exists since Emacs 28.1.
+         (tramp-compat-funcall 'unlock-file lockname))
+
        (when (and (null noninteractive)
                   (or (eq visit t) (null visit) (stringp visit)))
          (tramp-message v 0 "Wrote %s" filename))
@@ -4755,7 +4784,9 @@ Goes through the list `tramp-inline-compress-commands'."
              (with-temp-buffer
                (tramp-call-process vec "scp" nil t nil "-T")
                (goto-char (point-min))
-               (unless (search-forward-regexp "unknown option -- T" nil t)
+               (unless
+                    (search-forward-regexp
+                     "\\(illegal\\|unknown\\) option -- T" nil t)
                  (setq tramp-scp-strict-file-name-checking "-T")))))))
       tramp-scp-strict-file-name-checking)))
 
diff --git a/tramp-smb.el b/tramp-smb.el
index 6fbf088..3d5be61 100644
--- a/tramp-smb.el
+++ b/tramp-smb.el
@@ -247,6 +247,7 @@ See `tramp-actions-before-shell' for more info.")
     (file-exists-p . tramp-handle-file-exists-p)
     (file-in-directory-p . tramp-handle-file-in-directory-p)
     (file-local-copy . tramp-smb-handle-file-local-copy)
+    (file-locked-p . tramp-handle-file-locked-p)
     (file-modes . tramp-handle-file-modes)
     (file-name-all-completions . tramp-smb-handle-file-name-all-completions)
     (file-name-as-directory . tramp-handle-file-name-as-directory)
@@ -273,9 +274,11 @@ See `tramp-actions-before-shell' for more info.")
     (insert-directory . tramp-smb-handle-insert-directory)
     (insert-file-contents . tramp-handle-insert-file-contents)
     (load . tramp-handle-load)
+    (lock-file . tramp-handle-lock-file)
     (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 . 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)
@@ -294,6 +297,7 @@ See `tramp-actions-before-shell' for more info.")
     (tramp-get-remote-uid . ignore)
     (tramp-set-file-uid-gid . ignore)
     (unhandled-file-name-directory . ignore)
+    (unlock-file . tramp-handle-unlock-file)
     (vc-registered . ignore)
     (verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime)
     (write-region . tramp-smb-handle-write-region))
@@ -532,7 +536,7 @@ arguments to pass to the OPERATION."
                      (tramp-process-actions p v nil tramp-smb-actions-with-tar)
 
                      (while (process-live-p p)
-                       (sit-for 0.1))
+                       (sleep-for 0.1))
                      (tramp-message v 6 "\n%s" (buffer-string))))
 
                ;; Reset the transfer process properties.
@@ -718,7 +722,7 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are 
completely ignored."
   (when (zerop (length name)) (setq name "."))
   ;; Unless NAME is absolute, concat DIR and NAME.
   (unless (file-name-absolute-p name)
-    (setq name (concat (file-name-as-directory dir) name)))
+    (setq name (tramp-compat-file-name-concat dir name)))
   ;; If NAME is not a Tramp file, run the real handler.
   (if (not (tramp-tramp-file-p name))
       (tramp-run-real-handler #'expand-file-name (list name nil))
@@ -1255,7 +1259,7 @@ component is used as the target of the symlink."
   (when (and (numberp destination) (zerop destination))
     (error "Implementation does not handle immediate return"))
 
-  (with-parsed-tramp-file-name default-directory nil
+  (with-parsed-tramp-file-name (expand-file-name default-directory) nil
     (let* ((name (file-name-nondirectory program))
           (name1 name)
           (i 0)
@@ -1575,7 +1579,8 @@ errors for shares like \"C$/\", which are common in 
Microsoft Windows."
 (defun tramp-smb-handle-write-region
   (start end filename &optional append visit lockname mustbenew)
   "Like `write-region' for Tramp files."
-  (setq filename (expand-file-name filename))
+  (setq filename (expand-file-name filename)
+       lockname (file-truename (or lockname filename)))
   (with-parsed-tramp-file-name filename nil
     (when (and mustbenew (file-exists-p filename)
               (or (eq mustbenew 'excl)
@@ -1584,15 +1589,25 @@ errors for shares like \"C$/\", which are common in 
Microsoft Windows."
                     (format "File %s exists; overwrite anyway? " filename)))))
       (tramp-error v 'file-already-exists filename))
 
-    (let ((curbuf (current-buffer))
+    (let ((file-locked (eq (file-locked-p lockname) t))
+         (curbuf (current-buffer))
          (tmpfile (tramp-compat-make-temp-file filename)))
+
+      ;; Lock file.
+      (when (and (not (auto-save-file-name-p (file-name-nondirectory 
filename)))
+                (file-remote-p lockname)
+                (not file-locked))
+       (setq file-locked t)
+       ;; `lock-file' exists since Emacs 28.1.
+       (tramp-compat-funcall 'lock-file lockname))
+
       (when (and append (file-exists-p filename))
        (copy-file filename tmpfile 'ok))
       ;; We say `no-message' here because we don't want the visited file
       ;; modtime data to be clobbered from the temp file.  We call
       ;; `set-visited-file-modtime' ourselves later on.
-      (tramp-run-real-handler
-       #'write-region (list start end tmpfile append 'no-message lockname))
+      (let (create-lockfiles)
+        (write-region start end tmpfile append 'no-message))
 
       (with-tramp-progress-reporter
          v 3 (format "Moving tmp file %s to %s" tmpfile filename)
@@ -1619,6 +1634,11 @@ errors for shares like \"C$/\", which are common in 
Microsoft Windows."
              (file-attributes filename))
             (current-time))))
 
+      ;; Unlock file.
+      (when file-locked
+       ;; `unlock-file' exists since Emacs 28.1.
+       (tramp-compat-funcall 'unlock-file lockname))
+
       ;; The end.
       (when (and (null noninteractive)
                 (or (eq visit t) (null visit) (stringp visit)))
diff --git a/tramp-sshfs.el b/tramp-sshfs.el
index c4a36fe..c5b84a6 100644
--- a/tramp-sshfs.el
+++ b/tramp-sshfs.el
@@ -96,6 +96,7 @@
     (file-exists-p . tramp-handle-file-exists-p)
     (file-in-directory-p . tramp-handle-file-in-directory-p)
     (file-local-copy . tramp-handle-file-local-copy)
+    (file-locked-p . tramp-handle-file-locked-p)
     (file-modes . tramp-handle-file-modes)
     (file-name-all-completions . tramp-fuse-handle-file-name-all-completions)
     (file-name-as-directory . tramp-handle-file-name-as-directory)
@@ -122,9 +123,11 @@
     (insert-directory . tramp-handle-insert-directory)
     (insert-file-contents . tramp-sshfs-handle-insert-file-contents)
     (load . tramp-handle-load)
+    (lock-file . tramp-handle-lock-file)
     (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 . 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)
@@ -143,6 +146,7 @@
     (tramp-get-remote-uid . ignore)
     (tramp-set-file-uid-gid . ignore)
     (unhandled-file-name-directory . ignore)
+    (unlock-file . tramp-handle-unlock-file)
     (vc-registered . ignore)
     (verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime)
     (write-region . tramp-sshfs-handle-write-region))
@@ -231,7 +235,7 @@ arguments to pass to the OPERATION."
   (when (and (numberp destination) (zerop destination))
     (error "Implementation does not handle immediate return"))
 
-  (with-parsed-tramp-file-name default-directory nil
+  (with-parsed-tramp-file-name (expand-file-name default-directory) nil
     (let ((command
           (format
            "cd %s && exec %s"
@@ -281,7 +285,8 @@ arguments to pass to the OPERATION."
 (defun tramp-sshfs-handle-write-region
   (start end filename &optional append visit lockname mustbenew)
   "Like `write-region' for Tramp files."
-  (setq filename (expand-file-name filename))
+  (setq filename (expand-file-name filename)
+       lockname (file-truename (or lockname filename)))
   (with-parsed-tramp-file-name filename nil
     (when (and mustbenew (file-exists-p filename)
               (or (eq mustbenew 'excl)
@@ -290,15 +295,31 @@ arguments to pass to the OPERATION."
                     (format "File %s exists; overwrite anyway? " filename)))))
       (tramp-error v 'file-already-exists filename))
 
-    (write-region
-     start end (tramp-fuse-local-file-name filename) append 'nomessage 
lockname)
-    (tramp-flush-file-properties v localname)
+    (let ((file-locked (eq (file-locked-p lockname) t)))
+
+      ;; Lock file.
+      (when (and (not (auto-save-file-name-p (file-name-nondirectory 
filename)))
+                (file-remote-p lockname)
+                (not file-locked))
+       (setq file-locked t)
+       ;; `lock-file' exists since Emacs 28.1.
+       (tramp-compat-funcall 'lock-file lockname))
+
+      (let (create-lockfiles)
+       (write-region
+        start end (tramp-fuse-local-file-name filename) append 'nomessage)
+       (tramp-flush-file-properties v localname))
 
-    ;; The end.
-    (when (and (null noninteractive)
-              (or (eq visit t) (null visit) (stringp visit)))
-      (tramp-message v 0 "Wrote %s" filename))
-    (run-hooks 'tramp-handle-write-region-hook)))
+      ;; Unlock file.
+      (when file-locked
+       ;; `unlock-file' exists since Emacs 28.1.
+       (tramp-compat-funcall 'unlock-file lockname))
+
+      ;; The end.
+      (when (and (null noninteractive)
+                (or (eq visit t) (null visit) (stringp visit)))
+       (tramp-message v 0 "Wrote %s" filename))
+      (run-hooks 'tramp-handle-write-region-hook))))
 
 
 ;; File name conversions.
@@ -321,6 +342,9 @@ connection if a previous connection has died for some 
reason."
       (process-put p 'vector vec)
       (set-process-query-on-exit-flag p nil)
 
+      ;; Mark process for filelock.
+      (tramp-set-connection-property p "lock-pid" (truncate (time-to-seconds)))
+
       ;; Set connection-local variables.
       (tramp-set-connection-local-variables vec)
 
diff --git a/tramp-sudoedit.el b/tramp-sudoedit.el
index d641709..5895f1d 100644
--- a/tramp-sudoedit.el
+++ b/tramp-sudoedit.el
@@ -88,6 +88,7 @@ See `tramp-actions-before-shell' for more info.")
     (file-exists-p . tramp-sudoedit-handle-file-exists-p)
     (file-in-directory-p . tramp-handle-file-in-directory-p)
     (file-local-copy . tramp-handle-file-local-copy)
+    (file-locked-p . tramp-handle-file-locked-p)
     (file-modes . tramp-handle-file-modes)
     (file-name-all-completions
      . tramp-sudoedit-handle-file-name-all-completions)
@@ -115,9 +116,11 @@ See `tramp-actions-before-shell' for more info.")
     (insert-directory . tramp-handle-insert-directory)
     (insert-file-contents . tramp-handle-insert-file-contents)
     (load . tramp-handle-load)
+    (lock-file . tramp-handle-lock-file)
     (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 . 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)
@@ -136,6 +139,7 @@ See `tramp-actions-before-shell' for more info.")
     (tramp-get-remote-uid . tramp-sudoedit-handle-get-remote-uid)
     (tramp-set-file-uid-gid . tramp-sudoedit-handle-set-file-uid-gid)
     (unhandled-file-name-directory . ignore)
+    (unlock-file . tramp-handle-unlock-file)
     (vc-registered . ignore)
     (verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime)
     (write-region . tramp-sudoedit-handle-write-region))
@@ -233,7 +237,7 @@ absolute file names."
                       (file-attributes filename)))
          (file-modes (tramp-default-file-modes filename))
          (attributes (and preserve-extended-attributes
-                          (apply #'file-extended-attributes (list filename))))
+                          (file-extended-attributes filename)))
          (sudoedit-operation
           (cond
            ((and (eq op 'copy) preserve-uid-gid) '("cp" "-f" "-p"))
@@ -289,7 +293,7 @@ absolute file names."
        ;; errors, because ACL strings could be incompatible.
        (when attributes
          (ignore-errors
-           (apply #'set-file-extended-attributes (list newname attributes))))
+           (set-file-extended-attributes newname attributes)))
 
        (when (and t1 (eq op 'rename))
          (with-parsed-tramp-file-name filename v1
@@ -349,7 +353,7 @@ the result will be a local, non-Tramp, file name."
   (when (zerop (length name)) (setq name "."))
   ;; Unless NAME is absolute, concat DIR and NAME.
   (unless (file-name-absolute-p name)
-    (setq name (concat (file-name-as-directory dir) name)))
+    (setq name (tramp-compat-file-name-concat dir name)))
   (with-parsed-tramp-file-name name nil
     ;; Tilde expansion if necessary.  We cannot accept "~/", because
     ;; under sudo "~/" is expanded to the local user home directory
@@ -713,6 +717,7 @@ ID-FORMAT valid values are `string' and `integer'."
 (defun tramp-sudoedit-handle-write-region
   (start end filename &optional append visit lockname mustbenew)
   "Like `write-region' for Tramp files."
+  (setq filename (expand-file-name filename))
   (with-parsed-tramp-file-name filename nil
     (let* ((uid (or (tramp-compat-file-attribute-user-id
                     (file-attributes filename 'integer))
@@ -721,13 +726,14 @@ ID-FORMAT valid values are `string' and `integer'."
                     (file-attributes filename 'integer))
                    (tramp-get-remote-gid v 'integer)))
           (flag (and (eq mustbenew 'excl) 'nofollow))
-          (modes (tramp-default-file-modes filename flag)))
+          (modes (tramp-default-file-modes filename flag))
+          (attributes (file-extended-attributes filename)))
       (prog1
          (tramp-handle-write-region
           start end filename append visit lockname mustbenew)
 
-       ;; Set the ownership and modes.  This is not performed in
-       ;; `tramp-handle-write-region'.
+       ;; Set the ownership, modes and extended attributes.  This is
+       ;; not performed in `tramp-handle-write-region'.
        (unless (and (= (tramp-compat-file-attribute-user-id
                         (file-attributes filename 'integer))
                        uid)
@@ -735,7 +741,12 @@ ID-FORMAT valid values are `string' and `integer'."
                         (file-attributes filename 'integer))
                        gid))
           (tramp-set-file-uid-gid filename uid gid))
-       (tramp-compat-set-file-modes filename modes flag)))))
+       (tramp-compat-set-file-modes filename modes flag)
+       ;; We ignore possible errors, because ACL strings could be
+       ;; incompatible.
+       (when attributes
+         (ignore-errors
+           (set-file-extended-attributes filename attributes)))))))
 
 
 ;; Internal functions.
@@ -776,6 +787,9 @@ connection if a previous connection has died for some 
reason."
       (process-put p 'vector vec)
       (set-process-query-on-exit-flag p nil)
 
+      ;; Mark process for filelock.
+      (tramp-set-connection-property p "lock-pid" (truncate (time-to-seconds)))
+
       ;; Set connection-local variables.
       (tramp-set-connection-local-variables vec)
 
@@ -803,6 +817,9 @@ in case of error, t otherwise."
                      (tramp-compat-flatten-tree args))))
           ;; We suppress the messages `Waiting for prompts from remote shell'.
           (tramp-verbose (if (= tramp-verbose 3) 2 tramp-verbose))
+          ;; The password shall be cached also in case of "emacs -Q".
+          ;; See `tramp-process-actions'.
+          (tramp-cache-read-persistent-data t)
           ;; We do not want to save the password.
           auth-source-save-behavior)
       (tramp-message vec 6 "%s" (string-join (process-command p) " "))
diff --git a/tramp.el b/tramp.el
index 10f4a2b..18cae06 100644
--- a/tramp.el
+++ b/tramp.el
@@ -586,8 +586,7 @@ Sometimes the prompt is reported to look like \"login 
as:\"."
 
 (defcustom tramp-shell-prompt-pattern
   ;; Allow a prompt to start right after a ^M since it indeed would be
-  ;; displayed at the beginning of the line (and Zsh uses it).  This
-  ;; regexp works only for GNU Emacs.
+  ;; displayed at the beginning of the line (and Zsh uses it).
   ;; Allow also [] style prompts.  They can appear only during
   ;; connection initialization; Tramp redefines the prompt afterwards.
   (concat "\\(?:^\\|\r\\)"
@@ -698,6 +697,15 @@ The regexp should match at end of buffer."
   :version "27.1"
   :type 'regexp)
 
+;; Yubikey requires the user physically to touch the device with their
+;; finger.  We must tell it to the user.
+(defcustom tramp-yubikey-regexp
+  "^\r*Confirm user presence for key .*[\r\n]*"
+  "Regular expression matching yubikey confirmation message.
+The regexp should match at end of buffer."
+  :version "28.1"
+  :type 'regexp)
+
 (defcustom tramp-operation-not-permitted-regexp
   (concat "\\(" "preserving times.*" "\\|" "set mode" "\\)" ":\\s-*"
          (regexp-opt '("Operation not permitted") t))
@@ -1905,7 +1913,7 @@ The outline level is equal to the verbosity of the Tramp 
message."
 (put #'tramp-trace-buffer-name 'tramp-suppress-trace t)
 
 (defvar tramp-trace-functions nil
-  "A list of non-Tramp functions to be trace with tramp-verbose > 10.")
+  "A list of non-Tramp functions to be traced with tramp-verbose > 10.")
 
 (defun tramp-debug-message (vec fmt-string &rest arguments)
   "Append message to debug buffer of VEC.
@@ -2459,6 +2467,8 @@ Must be handled by the callers."
              file-name-case-insensitive-p
              ;; Emacs 27+ only.
              file-system-info
+             ;; Emacs 28+ only.
+             file-locked-p lock-file make-lock-file-name unlock-file
              ;; Tramp internal magic file name function.
              tramp-set-file-uid-gid))
     (if (file-name-absolute-p (nth 0 args))
@@ -2644,14 +2654,7 @@ If Emacs is compiled --with-threads, the body is 
protected by a mutex."
 
       ;; When `tramp-mode' is not enabled, or the file name is quoted,
       ;; we don't do anything.
-      ;; When operation is `expand-file-name', and the first argument
-      ;; is a local absolute file name, we end also here.  Handle the
-      ;; MS Windows case.
-      (funcall
-       (if (and (eq operation 'expand-file-name)
-               (not (string-match-p "\\`[[:alpha:]]:/" (car args))))
-          #'tramp-drop-volume-letter #'identity)
-       (tramp-run-real-handler operation args)))))
+      (tramp-run-real-handler operation args))))
 
 (defun tramp-completion-file-name-handler (operation &rest args)
   "Invoke Tramp file name completion handler for OPERATION and ARGS.
@@ -3376,7 +3379,7 @@ User is always nil."
   (when (zerop (length name)) (setq name "."))
   ;; Unless NAME is absolute, concat DIR and NAME.
   (unless (file-name-absolute-p name)
-    (setq name (concat (file-name-as-directory dir) name)))
+    (setq name (tramp-compat-file-name-concat dir name)))
   ;; If NAME is not a Tramp file, run the real handler.
   (if (not (tramp-tramp-file-p name))
       (tramp-run-real-handler #'expand-file-name (list name nil))
@@ -3669,7 +3672,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)
 
@@ -3696,6 +3699,7 @@ User is always nil."
                 #'find-backup-file-name (list filename)))
         ;; Protect against security hole.
        (when (and (not tramp-allow-unsafe-temporary-files)
+                  (not backup-inhibited)
                   (file-in-directory-p (car result) temporary-file-directory)
                   (zerop (or (tramp-compat-file-attribute-user-id
                               (file-attributes filename 'integer))
@@ -3857,6 +3861,101 @@ User is always nil."
       ;; Result.
       (cons (expand-file-name filename) (cdr result)))))
 
+(defun tramp-get-lock-file (file)
+  "Read lockfile info of FILE.
+Return nil when there is no lockfile."
+  (when-let ((lockname (tramp-compat-make-lock-file-name file)))
+    (or (file-symlink-p lockname)
+       (and (file-readable-p lockname)
+            (with-temp-buffer
+              (insert-file-contents-literally lockname)
+              (buffer-string))))))
+
+(defun tramp-get-lock-pid (file)
+  "Determine pid for lockfile of FILE."
+  ;; Some Tramp methods do not offer a connection process, but just a
+  ;; network process as a place holder.  Those processes use the
+  ;; "lock-pid" connection property as fake pid, in fact it is the
+  ;; time stamp the process is created.
+  (let ((p (tramp-get-process  (tramp-dissect-file-name file))))
+    (number-to-string
+     (or (process-id p)
+        (tramp-get-connection-property p "lock-pid" (emacs-pid))))))
+
+(defconst tramp-lock-file-info-regexp
+  ;; USER@HOST.PID[:BOOT_TIME]
+  "\\`\\(.+\\)@\\(.+\\)\\.\\([[:digit:]]+\\)\\(?::\\([[:digit:]]+\\)\\)?\\'"
+  "The format of a lock file.")
+
+(defun tramp-handle-file-locked-p (file)
+  "Like `file-locked-p' for Tramp files."
+  (when-let ((info (tramp-get-lock-file file))
+            (match (string-match tramp-lock-file-info-regexp info)))
+    (or (and (string-equal (match-string 1 info) (user-login-name))
+            (string-equal (match-string 2 info) (system-name))
+            (string-equal (match-string 3 info) (tramp-get-lock-pid file)))
+       (match-string 1 info))))
+
+(defun tramp-handle-lock-file (file)
+  "Like `lock-file' for Tramp files."
+  ;; See if this file is visited and has changed on disk since it
+  ;; was visited.
+  (catch 'dont-lock
+    (unless (eq (file-locked-p file) t) ;; Locked by me.
+      (when-let ((info (tramp-get-lock-file file))
+                (match (string-match tramp-lock-file-info-regexp info)))
+       (unless (ask-user-about-lock
+                file (format
+                      "%s@%s (pid %s)" (match-string 1 info)
+                      (match-string 2 info) (match-string 3 info)))
+         (throw 'dont-lock nil)))
+
+      (when-let ((lockname (tramp-compat-make-lock-file-name file))
+                ;; USER@HOST.PID[:BOOT_TIME]
+                (info
+                 (format
+                  "%s@%s.%s" (user-login-name) (system-name)
+                  (tramp-get-lock-pid file))))
+
+       ;; Protect against security hole.
+       (with-parsed-tramp-file-name file nil
+         (when (and (not tramp-allow-unsafe-temporary-files)
+                    create-lockfiles
+                    (file-in-directory-p lockname 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")))
+
+       ;; Do the lock.
+        (let (create-lockfiles signal-hook-function)
+         (condition-case nil
+             (make-symbolic-link info lockname 'ok-if-already-exists)
+           (error
+            (with-file-modes #o0644
+               (write-region info nil lockname)))))))))
+
+(defun tramp-handle-make-lock-file-name (file)
+  "Like `make-lock-file-name' for Tramp files."
+  (and create-lockfiles
+       ;; This variable has been introduced with Emacs 28.1.
+       (not (bound-and-true-p remote-file-name-inhibit-locks))
+       (tramp-run-real-handler 'make-lock-file-name (list file))))
+
+(defun tramp-handle-unlock-file (file)
+  "Like `unlock-file' for Tramp files."
+  (when-let ((lockname (tramp-compat-make-lock-file-name file)))
+    (condition-case err
+        (delete-file lockname)
+      ;; `userlock--handle-unlock-error' exists since Emacs 28.1.
+      (error (tramp-compat-funcall 'userlock--handle-unlock-error err)))))
+
 (defun tramp-handle-load (file &optional noerror nomessage nosuffix 
must-suffix)
   "Like `load' for Tramp files."
   (with-parsed-tramp-file-name (expand-file-name file) nil
@@ -4398,7 +4497,8 @@ of."
 (defun tramp-handle-write-region
   (start end filename &optional append visit lockname mustbenew)
   "Like `write-region' for Tramp files."
-  (setq filename (expand-file-name filename))
+  (setq filename (expand-file-name filename)
+       lockname (file-truename (or lockname filename)))
   (with-parsed-tramp-file-name filename nil
     (when (and mustbenew (file-exists-p filename)
               (or (eq mustbenew 'excl)
@@ -4407,7 +4507,8 @@ of."
                     (format "File %s exists; overwrite anyway? " filename)))))
       (tramp-error v 'file-already-exists filename))
 
-    (let ((tmpfile (tramp-compat-make-temp-file filename))
+    (let ((file-locked (eq (file-locked-p lockname) t))
+         (tmpfile (tramp-compat-make-temp-file filename))
          (modes (tramp-default-file-modes
                  filename (and (eq mustbenew 'excl) 'nofollow)))
          (uid (or (tramp-compat-file-attribute-user-id
@@ -4416,6 +4517,15 @@ of."
          (gid (or (tramp-compat-file-attribute-group-id
                    (file-attributes filename 'integer))
                   (tramp-get-remote-gid v 'integer))))
+
+      ;; Lock file.
+      (when (and (not (auto-save-file-name-p (file-name-nondirectory 
filename)))
+                (file-remote-p lockname)
+                (not file-locked))
+       (setq file-locked t)
+       ;; `lock-file' exists since Emacs 28.1.
+       (tramp-compat-funcall 'lock-file lockname))
+
       (when (and append (file-exists-p filename))
        (copy-file filename tmpfile 'ok))
       ;; The permissions of the temporary file should be set.  If
@@ -4427,7 +4537,8 @@ of."
       ;; We say `no-message' here because we don't want the visited file
       ;; modtime data to be clobbered from the temp file.  We call
       ;; `set-visited-file-modtime' ourselves later on.
-      (write-region start end tmpfile append 'no-message lockname)
+      (let (create-lockfiles)
+        (write-region start end tmpfile append 'no-message))
       (condition-case nil
          (rename-file tmpfile filename 'ok-if-already-exists)
        (error
@@ -4445,13 +4556,18 @@ of."
             (current-time))))
 
       ;; Set the ownership.
-      (tramp-set-file-uid-gid filename uid gid))
+      (tramp-set-file-uid-gid filename uid gid)
+
+      ;; Unlock file.
+      (when file-locked
+       ;; `unlock-file' exists since Emacs 28.1.
+       (tramp-compat-funcall 'unlock-file lockname))
 
-    ;; The end.
-    (when (and (null noninteractive)
-              (or (eq visit t) (null visit) (stringp visit)))
-      (tramp-message v 0 "Wrote %s" filename))
-    (run-hooks 'tramp-handle-write-region-hook)))
+      ;; The end.
+      (when (and (null noninteractive)
+                (or (eq visit t) (null visit) (stringp visit)))
+       (tramp-message v 0 "Wrote %s" filename))
+      (run-hooks 'tramp-handle-write-region-hook))))
 
 ;; This is used in tramp-sh.el and tramp-sudoedit.el.
 (defconst tramp-stat-marker "/////"
@@ -4507,6 +4623,9 @@ of."
 ;; prompts from the remote host.  See the variable
 ;; `tramp-actions-before-shell' for usage of these functions.
 
+(defvar tramp-process-action-regexp nil
+  "The regexp used to invoke an action in `tramp-process-one-action'.")
+
 (defun tramp-action-login (_proc vec)
   "Send the login name."
   (let ((user (or (tramp-file-name-user vec)
@@ -4532,7 +4651,7 @@ of."
       (unless (tramp-get-connection-property vec "first-password-request" nil)
        (tramp-clear-passwd vec))
       (goto-char (point-min))
-      (tramp-check-for-regexp proc tramp-password-prompt-regexp)
+      (tramp-check-for-regexp proc tramp-process-action-regexp)
       (tramp-message vec 3 "Sending %s" (match-string 1))
       ;; We don't call `tramp-send-string' in order to hide the
       ;; password from the debug buffer and the traces.
@@ -4597,6 +4716,23 @@ The terminal type can be configured with 
`tramp-terminal-type'."
   (tramp-send-string vec tramp-local-end-of-line)
   t)
 
+(defun tramp-action-show-and-confirm-message (proc vec)
+  "Show the user a message for confirmation.
+Wait, until the connection buffer changes."
+  (with-current-buffer (process-buffer proc)
+    (let ((stimers (with-timeout-suspend)))
+      (tramp-message vec 6 "\n%s" (buffer-string))
+      (goto-char (point-min))
+      (tramp-check-for-regexp proc tramp-process-action-regexp)
+      (with-temp-message (replace-regexp-in-string "[\r\n]" "" (match-string 
0))
+       ;; Hide message in buffer.
+       (narrow-to-region (point-max) (point-max))
+       ;; Wait for new output.
+       (tramp-wait-for-regexp proc 30 "."))
+      ;; Reenable the timers.
+      (with-timeout-unsuspend stimers)))
+  t)
+
 (defun tramp-action-process-alive (proc _vec)
   "Check, whether a process has finished."
   (unless (process-live-p proc)
@@ -4634,6 +4770,7 @@ The terminal type can be configured with 
`tramp-terminal-type'."
   "Wait for output from the shell and perform one action.
 See `tramp-process-actions' for the format of ACTIONS."
   (let ((case-fold-search t)
+       tramp-process-action-regexp
        found todo item pattern action)
     (while (not found)
       ;; Reread output once all actions have been performed.
@@ -4642,7 +4779,8 @@ See `tramp-process-actions' for the format of ACTIONS."
       (setq todo actions)
       (while todo
        (setq item (pop todo)
-             pattern (format "\\(%s\\)\\'" (symbol-value (nth 0 item)))
+             tramp-process-action-regexp (symbol-value (nth 0 item))
+             pattern (format "\\(%s\\)\\'" tramp-process-action-regexp)
              action (nth 1 item))
        (tramp-message
         vec 5 "Looking for regexp \"%s\" from remote shell" pattern)
@@ -5323,6 +5461,7 @@ this file, if that variable is non-nil."
          (setq result (tramp-run-real-handler #'make-auto-save-file-name nil))
        ;; Protect against security hole.
        (when (and (not tramp-allow-unsafe-temporary-files)
+                  auto-save-default
                   (file-in-directory-p result temporary-file-directory)
                   (zerop (or (tramp-compat-file-attribute-user-id
                               (file-attributes filename 'integer))
diff --git a/trampver.el b/trampver.el
index e6cf4c6..d3e08be 100644
--- a/trampver.el
+++ b/trampver.el
@@ -7,7 +7,7 @@
 ;; Maintainer: Michael Albinus <michael.albinus@gmx.de>
 ;; Keywords: comm, processes
 ;; Package: tramp
-;; Version: 2.5.1
+;; Version: 2.5.1.1
 ;; Package-Requires: ((emacs "25.1"))
 ;; Package-Type: multi
 ;; URL: https://www.gnu.org/software/tramp/
@@ -40,7 +40,7 @@
 ;; ./configure" to change them.
 
 ;;;###tramp-autoload
-(defconst tramp-version "2.5.1"
+(defconst tramp-version "2.5.1.1"
   "This version of Tramp.")
 
 ;;;###tramp-autoload
@@ -76,7 +76,7 @@
 ;; Check for Emacs version.
 (let ((x   (if (not (string-lessp emacs-version "25.1"))
       "ok"
-    (format "Tramp 2.5.1 is not fit for %s"
+    (format "Tramp 2.5.1.1 is not fit for %s"
             (replace-regexp-in-string "\n" "" (emacs-version))))))
   (unless (string-equal "ok" x) (error "%s" x)))
 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]