branch: externals/tramp
commit cdbc051cd7f2c7d6fdc4f6d7b5a85a0ceb290787
Author: Michael Albinus <michael.albi...@gmx.de>
Commit: Michael Albinus <michael.albi...@gmx.de>

    Tramp ELPA version 2.7.2.3 released
---
 README              |   6 +-
 test/tramp-tests.el | 128 +++++++++++++++++++++-------
 texi/.gitignore     |   1 +
 texi/Makefile       |   4 +-
 texi/trampver.texi  |   2 +-
 tramp-adb.el        |   2 +-
 tramp-cmds.el       |   4 +-
 tramp-gvfs.el       | 198 ++++++++++++++++++++++---------------------
 tramp-sh.el         | 239 +++++++++++++++++++++++++++-------------------------
 tramp-smb.el        | 191 +++++++++++++++++++++--------------------
 tramp-sshfs.el      |  34 ++++----
 tramp-sudoedit.el   | 152 +++++++++++++++++----------------
 tramp.el            |  64 +++++++++-----
 trampver.el         |   6 +-
 14 files changed, 568 insertions(+), 463 deletions(-)

diff --git a/README b/README
index 8ce4391156..147214c4d9 100644
--- a/README
+++ b/README
@@ -32,11 +32,11 @@ Emacs 28 or older
 
    • Remove all byte-compiled Tramp files
 
-          $ rm -f ~/.emacs.d/elpa/tramp-2.7.2.2/tramp*.elc
+          $ rm -f ~/.emacs.d/elpa/tramp-2.7.2.3/tramp*.elc
 
    • Start Emacs with Tramp's source files
 
-          $ emacs -L ~/.emacs.d/elpa/tramp-2.7.2.2 -l tramp
+          $ emacs -L ~/.emacs.d/elpa/tramp-2.7.2.3 -l tramp
 
      This should not give you the error.
 
@@ -50,7 +50,7 @@ Mitigation of a bug in Emacs 29.1
 ---------------------------------
 
 Due to a bug in Emacs 29.1, you must apply the following change prior
-installation or upgrading Tramp 2.7.2.2 from GNU ELPA:
+installation or upgrading Tramp 2.7.2.3 from GNU ELPA:
 
      (when (string-equal emacs-version "29.1")
        (with-current-buffer
diff --git a/test/tramp-tests.el b/test/tramp-tests.el
index c1ef88937f..605b26206c 100644
--- a/test/tramp-tests.el
+++ b/test/tramp-tests.el
@@ -190,7 +190,7 @@ A resource file is in the resource directory as per
       tramp-error-show-message-timeout nil
       tramp-persistency-file-name nil
       tramp-verbose 0
-      vc-handled-backends nil)
+      vc-handled-backends (unless noninteractive vc-handled-backends))
 
 (defvar tramp--test-enabled-checked nil
   "Cached result of `tramp--test-enabled'.
@@ -2886,7 +2886,9 @@ This checks also `file-name-as-directory', 
`file-name-directory',
   (dolist (quoted (if (tramp--test-expensive-test-p) '(nil t) '(nil)))
     (let ((tmp-name1 (tramp--test-make-temp-name nil quoted))
          (tmp-name2 (tramp--test-make-temp-name nil quoted))
-         (tmp-name3 (tramp--test-make-temp-name 'local quoted)))
+         (tmp-name3 (tramp--test-make-temp-name 'local quoted))
+         (tmp-name4
+          (file-name-nondirectory (tramp--test-make-temp-name 'local quoted))))
       (dolist (source-target
               `(;; Copy on remote side.
                 (,tmp-name1 . ,tmp-name2)
@@ -2894,8 +2896,12 @@ This checks also `file-name-as-directory', 
`file-name-directory',
                 (,tmp-name1 . ,tmp-name3)
                 ;; Copy from local side to remote side.
                 (,tmp-name3 . ,tmp-name1)))
-       (let ((source (car source-target))
-             (target (cdr source-target)))
+       (let* ((source (car source-target))
+              (source-link
+               (expand-file-name tmp-name4 (file-name-directory source)))
+              (target (cdr source-target))
+              (target-link
+               (expand-file-name tmp-name4 (file-name-directory target))))
 
          ;; Copy simple file.
          (unwind-protect
@@ -2920,6 +2926,26 @@ This checks also `file-name-as-directory', 
`file-name-directory',
            (ignore-errors (delete-file source))
            (ignore-errors (delete-file target)))
 
+         ;; Copy symlinked file.
+         (unwind-protect
+             (tramp--test-ignore-make-symbolic-link-error
+              (write-region "foo" nil source-link)
+              (should (file-exists-p source-link))
+              (make-symbolic-link tmp-name4 source)
+              (should (file-exists-p source))
+              (should (string-equal (file-symlink-p source) tmp-name4))
+              (copy-file source target)
+              ;; Some backends like tramp-gvfs.el do not create the
+              ;; link on the target.
+              (when (file-symlink-p target)
+                (should (string-equal (file-symlink-p target) tmp-name4))))
+
+           ;; Cleanup.
+           (ignore-errors (delete-file source))
+           (ignore-errors (delete-file source-link))
+           (ignore-errors (delete-file target))
+           (ignore-errors (delete-file target-link)))
+
          ;; Copy file to directory.
          (unwind-protect
              ;; This doesn't work on FTP.
@@ -2995,7 +3021,9 @@ This checks also `file-name-as-directory', 
`file-name-directory',
   (dolist (quoted  (if (tramp--test-expensive-test-p) '(nil t) '(nil)))
     (let ((tmp-name1 (tramp--test-make-temp-name nil quoted))
          (tmp-name2 (tramp--test-make-temp-name nil quoted))
-         (tmp-name3 (tramp--test-make-temp-name 'local quoted)))
+         (tmp-name3 (tramp--test-make-temp-name 'local quoted))
+         (tmp-name4
+          (file-name-nondirectory (tramp--test-make-temp-name 'local quoted))))
       (dolist (source-target
               `(;; Rename on remote side.
                 (,tmp-name1 . ,tmp-name2)
@@ -3003,8 +3031,12 @@ This checks also `file-name-as-directory', 
`file-name-directory',
                 (,tmp-name1 . ,tmp-name3)
                 ;; Rename from local side to remote side.
                 (,tmp-name3 . ,tmp-name1)))
-       (let ((source (car source-target))
-             (target (cdr source-target)))
+       (let* ((source (car source-target))
+              (source-link
+               (expand-file-name tmp-name4 (file-name-directory source)))
+              (target (cdr source-target))
+              (target-link
+               (expand-file-name tmp-name4 (file-name-directory target))))
 
          ;; Rename simple file.
          (unwind-protect
@@ -3033,6 +3065,27 @@ This checks also `file-name-as-directory', 
`file-name-directory',
            (ignore-errors (delete-file source))
            (ignore-errors (delete-file target)))
 
+         ;; Rename symlinked file.
+         (unwind-protect
+             (tramp--test-ignore-make-symbolic-link-error
+              (write-region "foo" nil source-link)
+              (should (file-exists-p source-link))
+              (make-symbolic-link tmp-name4 source)
+              (should (file-exists-p source))
+              (should (string-equal (file-symlink-p source) tmp-name4))
+              (rename-file source target)
+              (should-not (file-exists-p source))
+              ;; Some backends like tramp-gvfs.el do not create the
+              ;; link on the target.
+              (when (file-symlink-p target)
+                (should (string-equal (file-symlink-p target) tmp-name4))))
+
+           ;; Cleanup.
+           (ignore-errors (delete-file source))
+           (ignore-errors (delete-file source-link))
+           (ignore-errors (delete-file target))
+           (ignore-errors (delete-file target-link)))
+
          ;; Rename file to directory.
          (unwind-protect
              (progn
@@ -3813,6 +3866,7 @@ This tests also `access-file', `file-readable-p',
            (should (stringp (file-attribute-user-id attr)))
            (should (stringp (file-attribute-group-id attr)))
 
+           ;; Symbolic links.
            (tramp--test-ignore-make-symbolic-link-error
              (should-error
               (access-file tmp-name2 "error")
@@ -3832,7 +3886,26 @@ This tests also `access-file', `file-readable-p',
                 (if quoted #'file-name-quote #'identity)
                 (file-attribute-type attr))
                (file-remote-p (file-truename tmp-name1) 'localname)))
-             (delete-file tmp-name2))
+             (delete-file tmp-name2)
+
+             ;; A non-existent or cyclic link target makes the file
+             ;; unaccessible.
+             (dolist (target
+                      `("does-not-exist" ,(file-name-nondirectory tmp-name2)))
+               (make-symbolic-link target tmp-name2)
+               (should (file-symlink-p tmp-name2))
+               (should-not (file-exists-p tmp-name2))
+               (should-not (file-directory-p tmp-name2))
+               (should-error
+                (access-file tmp-name2 "error")
+                :type
+                (if (string-equal target "does-not-exist")
+                    'file-missing 'file-error))
+               ;; `file-ownership-preserved-p' should return t for
+               ;; symlinked files to a non-existing or cyclic target.
+               (when test-file-ownership-preserved-p
+                 (should (file-ownership-preserved-p tmp-name2 'group)))
+               (delete-file tmp-name2)))
 
            ;; Check, that "//" in symlinks are handled properly.
            (with-temp-buffer
@@ -4488,13 +4561,7 @@ This tests also `make-symbolic-link', `file-truename' 
and `add-name-to-file'."
               (should (file-symlink-p tmp-name1))
               (should (file-symlink-p tmp-name2))
               (should-not (file-regular-p tmp-name1))
-              (should-not (file-regular-p tmp-name2))
-              (should-error
-               (file-truename tmp-name1)
-               :type 'file-error)
-              (should-error
-               (file-truename tmp-name2)
-               :type 'file-error))))
+              (should-not (file-regular-p tmp-name2)))))
 
        ;; Cleanup.
        (ignore-errors
@@ -5274,19 +5341,20 @@ This tests also `make-symbolic-link', `file-truename' 
and `add-name-to-file'."
            ;;  (delete-file tmp-name)))
 
            ;; Check remote and local STDERR.
-           (dolist (local '(nil t))
-             (setq tmp-name (tramp--test-make-temp-name local quoted))
-             (should-not
-              (zerop
-               (process-file "cat" nil `(t ,tmp-name) nil "/does-not-exist")))
-             (with-temp-buffer
-               (insert-file-contents tmp-name)
-               (should
-                (string-match-p
-                 (rx "cat:" (* nonl) " No such file or directory")
-                 (buffer-string)))
-               (should-not (get-buffer-window (current-buffer) t))
-               (delete-file tmp-name))))
+           (unless (tramp--test-sshfs-p)
+             (dolist (local '(nil t))
+               (setq tmp-name (tramp--test-make-temp-name local quoted))
+               (should-not
+                (zerop
+                 (process-file "cat" nil `(t ,tmp-name) nil 
"/does-not-exist")))
+               (with-temp-buffer
+                 (insert-file-contents tmp-name)
+                 (should
+                  (string-match-p
+                   (rx "cat:" (* nonl) " No such file or directory")
+                   (buffer-string)))
+                 (should-not (get-buffer-window (current-buffer) t))
+                 (delete-file tmp-name)))))
 
        ;; Cleanup.
        (ignore-errors (kill-buffer buffer))
@@ -7439,10 +7507,6 @@ This requires restrictions of file name syntax."
                     (if quoted #'file-name-quote #'identity)
                     (file-attribute-type (file-attributes file3)))
                    (file-remote-p (file-truename file1) 'localname)))
-                 ;; Check file contents.
-                 (with-temp-buffer
-                   (insert-file-contents file3)
-                   (should (string-equal (buffer-string) elt)))
                  (delete-file file3))))
 
            ;; Check file names.
diff --git a/texi/.gitignore b/texi/.gitignore
index 7dc56a1698..b9fc08e253 100644
--- a/texi/.gitignore
+++ b/texi/.gitignore
@@ -7,6 +7,7 @@
 *.html
 *.info
 *.ky
+*.kys
 *.log
 *.pdf
 *.pg
diff --git a/texi/Makefile b/texi/Makefile
index 79c05deb55..db8f86df76 100644
--- a/texi/Makefile
+++ b/texi/Makefile
@@ -23,8 +23,8 @@
 
 MAKEINFO       = makeinfo --no-warn --no-split
 TEXI_FILES     = $(wildcard *.texi)
-CLEAN_FILES    = .\\\#* \\\#* .*~ *~ *.aux *.cp *.cps *.diff *.fn *.fns \
-                 *.ky *.log *.pg *.tmp *.toc *.tp *.vr *.vrs ../tramp.info
+CLEAN_FILES    = .\\\#* \\\#* .*~ *~ *.aux *.cp *.cps *.diff *.fn *.fns *.ky \
+                 *.kys *.log *.pg *.tmp *.toc *.tp *.vr *.vrs ../tramp.info
 SOURCE_DIR     = ~/src/tramp
 
 .PHONY: all clean sync
diff --git a/texi/trampver.texi b/texi/trampver.texi
index f3d1241a49..5365fc3172 100644
--- a/texi/trampver.texi
+++ b/texi/trampver.texi
@@ -7,7 +7,7 @@
 
 @c In the  Tramp GIT, the version number and the bug report address
 @c are auto-frobbed from configure.ac.
-@set trampver 2.7.2.2
+@set trampver 2.7.2.3
 @set trampurl https://www.gnu.org/software/tramp/
 @set tramp-bug-report-address tramp-devel@@gnu.org
 @set emacsver 27.1
diff --git a/tramp-adb.el b/tramp-adb.el
index 705d86828c..1ecabd8165 100644
--- a/tramp-adb.el
+++ b/tramp-adb.el
@@ -998,7 +998,7 @@ error and non-nil on success."
       ;; 
<https://android.stackexchange.com/questions/226638/how-to-use-multibyte-file-names-in-adb-shell/232379#232379>
       ;; mksh uses UTF-8 internally, but is currently limited to the
       ;; BMP (basic multilingua plane), which means U+0000 to
-      ;; U+FFFD. If you want to use SMP codepoints (U-00010000 to
+      ;; U+FFFD.  If you want to use SMP codepoints (U-00010000 to
       ;; U-0010FFFD) on the input line, you currently have to disable
       ;; the UTF-8 mode (sorry).
       (tramp-adb-execute-adb-command vec "shell" command)
diff --git a/tramp-cmds.el b/tramp-cmds.el
index e31123ecd5..f03fa5cf40 100644
--- a/tramp-cmds.el
+++ b/tramp-cmds.el
@@ -611,7 +611,9 @@ If the buffer runs `dired', the buffer is reverted."
   (interactive)
   (cond
    ((buffer-file-name)
-    (find-alternate-file (tramp-file-name-with-sudo (buffer-file-name))))
+    (let ((pos (point)))
+      (find-alternate-file (tramp-file-name-with-sudo (buffer-file-name)))
+      (goto-char pos)))
    ((tramp-dired-buffer-p)
     (dired-unadvertise (expand-file-name default-directory))
     (setq default-directory (tramp-file-name-with-sudo default-directory)
diff --git a/tramp-gvfs.el b/tramp-gvfs.el
index f422d8e3d6..3df69d79fc 100644
--- a/tramp-gvfs.el
+++ b/tramp-gvfs.el
@@ -1042,105 +1042,113 @@ file names."
   (unless (memq op '(copy rename))
     (error "Unknown operation `%s', must be `copy' or `rename'" op))
 
-  (setq filename (file-truename filename))
+  ;; We cannot use `file-truename', this would fail for symlinks with
+  ;; non-existing target.
+  (setq filename (expand-file-name filename))
   (if (file-directory-p filename)
       (progn
        (copy-directory filename newname keep-date t)
        (when (eq op 'rename) (delete-directory filename 'recursive)))
+    (if (file-symlink-p filename)
+       (progn
+         (make-symbolic-link
+          (file-symlink-p filename) newname ok-if-already-exists)
+         (when (eq op 'rename) (delete-file filename)))
+
+      (let ((t1 (tramp-tramp-file-p filename))
+           (t2 (tramp-tramp-file-p newname))
+           (equal-remote (tramp-equal-remote filename newname))
+           (volatile
+            (and (eq op 'rename) (tramp-gvfs-file-name-p filename)
+                 (equal
+                  (cdr
+                   (assoc
+                    "standard::is-volatile"
+                    (tramp-gvfs-get-file-attributes filename)))
+                  "TRUE")))
+           ;; "gvfs-rename" is not trustworthy.
+           (gvfs-operation (if (eq op 'copy) "gvfs-copy" "gvfs-move"))
+           (msg-operation (if (eq op 'copy) "Copying" "Renaming")))
+
+       (with-parsed-tramp-file-name (if t1 filename newname) nil
+         (tramp-barf-if-file-missing v filename
+           (when (and (not ok-if-already-exists) (file-exists-p newname))
+             (tramp-error v 'file-already-exists newname))
+           (when (and (file-directory-p newname)
+                      (not (directory-name-p newname)))
+             (tramp-error v 'file-error "File is a directory %s" newname))
+           (when (file-regular-p newname)
+             (delete-file newname))
 
-    (let ((t1 (tramp-tramp-file-p filename))
-         (t2 (tramp-tramp-file-p newname))
-         (equal-remote (tramp-equal-remote filename newname))
-         (volatile
-          (and (eq op 'rename) (tramp-gvfs-file-name-p filename)
-               (equal
-                (cdr
-                 (assoc
-                  "standard::is-volatile"
-                  (tramp-gvfs-get-file-attributes filename)))
-                "TRUE")))
-         ;; "gvfs-rename" is not trustworthy.
-         (gvfs-operation (if (eq op 'copy) "gvfs-copy" "gvfs-move"))
-         (msg-operation (if (eq op 'copy) "Copying" "Renaming")))
-
-      (with-parsed-tramp-file-name (if t1 filename newname) nil
-       (tramp-barf-if-file-missing v filename
-         (when (and (not ok-if-already-exists) (file-exists-p newname))
-           (tramp-error v 'file-already-exists newname))
-         (when (and (file-directory-p newname)
-                    (not (directory-name-p newname)))
-           (tramp-error v 'file-error "File is a directory %s" newname))
-         (when (file-regular-p newname)
-           (delete-file newname))
-
-         (cond
-          ;; We cannot rename volatile files, as used by Google-drive.
-          ((and (not equal-remote) volatile)
-           (prog1 (copy-file
-                   filename newname ok-if-already-exists keep-date
-                   preserve-uid-gid preserve-extended-attributes)
-             (delete-file filename)))
-
-          ;; We cannot copy or rename directly.
-          ((or (and equal-remote
-                    (tramp-get-connection-property v "direct-copy-failed"))
-               (and t1 (not (tramp-gvfs-file-name-p filename)))
-               (and t2 (not (tramp-gvfs-file-name-p newname))))
-           (let ((tmpfile (tramp-compat-make-temp-file filename)))
-             (if (eq op 'copy)
-                 (copy-file
-                  filename tmpfile t keep-date preserve-uid-gid
-                  preserve-extended-attributes)
-               (rename-file filename tmpfile t))
-             (rename-file tmpfile newname ok-if-already-exists)))
-
-          ;; Direct action.
-          (t (with-tramp-progress-reporter
-                 v 0 (format "%s %s to %s" msg-operation filename newname)
-               (unless
-                   (and (apply
-                         #'tramp-gvfs-send-command v gvfs-operation
-                         (append
-                          (and (eq op 'copy) (or keep-date preserve-uid-gid)
-                               '("--preserve"))
-                          (list
-                           (tramp-gvfs-url-file-name filename)
-                           (tramp-gvfs-url-file-name newname))))
-                        ;; Some backends do not return a proper error
-                        ;; code in case of direct copy/move.  Apply
-                        ;; sanity checks.
-                        (or (not equal-remote)
-                            (and
-                             (tramp-gvfs-info newname)
-                             (or (eq op 'copy)
-                                 (not (tramp-gvfs-info filename))))))
-
-                 (if (or (not equal-remote)
-                         (and equal-remote
-                              (tramp-get-connection-property
-                               v "direct-copy-failed")))
-                     ;; Propagate the error.
-                     (with-current-buffer (tramp-get-connection-buffer v)
-                       (goto-char (point-min))
-                       (tramp-error-with-buffer
-                        nil v 'file-error
-                        "%s failed, see buffer `%s' for details"
-                        msg-operation (buffer-name)))
-
-                   ;; Some WebDAV server, like the one from QNAP, do
-                   ;; not support direct copy/move.  Try a fallback.
-                   (tramp-set-connection-property v "direct-copy-failed" t)
-                   (tramp-gvfs-do-copy-or-rename-file
-                    op filename newname ok-if-already-exists keep-date
-                    preserve-uid-gid preserve-extended-attributes))))
-
-             (when (and t1 (eq op 'rename))
-               (with-parsed-tramp-file-name filename nil
-                 (tramp-flush-file-properties v localname)))
-
-             (when t2
-               (with-parsed-tramp-file-name newname nil
-                 (tramp-flush-file-properties v localname))))))))))
+           (cond
+            ;; We cannot rename volatile files, as used by Google-drive.
+            ((and (not equal-remote) volatile)
+             (prog1 (copy-file
+                     filename newname ok-if-already-exists keep-date
+                     preserve-uid-gid preserve-extended-attributes)
+               (delete-file filename)))
+
+            ;; We cannot copy or rename directly.
+            ((or (and equal-remote
+                      (tramp-get-connection-property v "direct-copy-failed"))
+                 (and t1 (not (tramp-gvfs-file-name-p filename)))
+                 (and t2 (not (tramp-gvfs-file-name-p newname))))
+             (let ((tmpfile (tramp-compat-make-temp-file filename)))
+               (if (eq op 'copy)
+                   (copy-file
+                    filename tmpfile t keep-date preserve-uid-gid
+                    preserve-extended-attributes)
+                 (rename-file filename tmpfile t))
+               (rename-file tmpfile newname ok-if-already-exists)))
+
+            ;; Direct action.
+            (t (with-tramp-progress-reporter
+                   v 0 (format "%s %s to %s" msg-operation filename newname)
+                 (unless
+                     (and (apply
+                           #'tramp-gvfs-send-command v gvfs-operation
+                           (append
+                            (and (eq op 'copy) (or keep-date preserve-uid-gid)
+                                 '("--preserve"))
+                            (list
+                             (tramp-gvfs-url-file-name filename)
+                             (tramp-gvfs-url-file-name newname))))
+                          ;; Some backends do not return a proper
+                          ;; error code in case of direct copy/move.
+                          ;; Apply sanity checks.
+                          (or (not equal-remote)
+                              (and
+                               (tramp-gvfs-info newname)
+                               (or (eq op 'copy)
+                                   (not (tramp-gvfs-info filename))))))
+
+                   (if (or (not equal-remote)
+                           (and equal-remote
+                                (tramp-get-connection-property
+                                 v "direct-copy-failed")))
+                       ;; Propagate the error.
+                       (with-current-buffer (tramp-get-connection-buffer v)
+                         (goto-char (point-min))
+                         (tramp-error-with-buffer
+                          nil v 'file-error
+                          "%s failed, see buffer `%s' for details"
+                          msg-operation (buffer-name)))
+
+                     ;; Some WebDAV server, like the one from QNAP,
+                     ;; do not support direct copy/move.  Try a
+                     ;; fallback.
+                     (tramp-set-connection-property v "direct-copy-failed" t)
+                     (tramp-gvfs-do-copy-or-rename-file
+                      op filename newname ok-if-already-exists keep-date
+                      preserve-uid-gid preserve-extended-attributes))))
+
+               (when (and t1 (eq op 'rename))
+                 (with-parsed-tramp-file-name filename nil
+                   (tramp-flush-file-properties v localname)))
+
+               (when t2
+                 (with-parsed-tramp-file-name newname nil
+                   (tramp-flush-file-properties v localname)))))))))))
 
 (defun tramp-gvfs-handle-copy-file
   (filename newname &optional ok-if-already-exists keep-date
@@ -2215,7 +2223,7 @@ connection if a previous connection has died for some 
reason."
                    method '(("smb" . "smb-share")
                             ("davs" . "dav")
                             ("nextcloud" . "dav")
-                            ("afp". "afp-volume")
+                            ("afp" . "afp-volume")
                             ("gdrive" . "google-drive")))
                   method)
               tramp-gvfs-mounttypes)
diff --git a/tramp-sh.el b/tramp-sh.el
index 98a4c7d90f..ef4ddee8a5 100644
--- a/tramp-sh.el
+++ b/tramp-sh.el
@@ -1853,7 +1853,10 @@ ID-FORMAT valid values are `string' and `integer'."
                  ;; test.
                  (tramp-check-remote-uname v tramp-bsd-unames)
                 (= (file-attribute-group-id attributes)
-                   (tramp-get-remote-gid v 'integer)))))))))
+                   (tramp-get-remote-gid v 'integer))
+                ;; FIXME: `file-ownership-preserved-p' tests also the
+                ;; ownership of the parent directory.  We don't.
+                )))))))
 
 ;; Directory listings.
 
@@ -2125,123 +2128,129 @@ file names."
       (progn
        (copy-directory filename newname keep-date t)
        (when (eq op 'rename) (delete-directory filename 'recursive)))
+    (if (file-symlink-p filename)
+       (progn
+         (make-symbolic-link
+          (file-symlink-p filename) newname ok-if-already-exists)
+         (when (eq op 'rename) (delete-file filename)))
+
+      ;; FIXME: This should be optimized.  Computing `file-attributes'
+      ;; checks already, whether the file exists.
+      (let ((t1 (tramp-tramp-file-p filename))
+           (t2 (tramp-tramp-file-p newname))
+           (length (or (file-attribute-size
+                        (file-attributes (file-truename filename)))
+                       ;; `filename' doesn't exist, for example due
+                       ;; to non-existent symlink target.
+                       0))
+           (file-times (file-attribute-modification-time
+                        (file-attributes filename)))
+           (file-modes (tramp-default-file-modes filename))
+           (msg-operation (if (eq op 'copy) "Copying" "Renaming"))
+            copy-keep-date)
+
+       (with-parsed-tramp-file-name (if t1 filename newname) nil
+         (tramp-barf-if-file-missing v filename
+           (when (and (not ok-if-already-exists) (file-exists-p newname))
+             (tramp-error v 'file-already-exists newname))
+           (when (and (file-directory-p newname)
+                      (not (directory-name-p newname)))
+             (tramp-error v 'file-error "File is a directory %s" newname))
 
-    ;; FIXME: This should be optimized.  Computing `file-attributes'
-    ;; checks already, whether the file exists.
-    (let ((t1 (tramp-tramp-file-p filename))
-         (t2 (tramp-tramp-file-p newname))
-         (length (file-attribute-size
-                  (file-attributes (file-truename filename))))
-         (file-times (file-attribute-modification-time
-                      (file-attributes filename)))
-         (file-modes (tramp-default-file-modes filename))
-         (msg-operation (if (eq op 'copy) "Copying" "Renaming"))
-          copy-keep-date)
-
-      (with-parsed-tramp-file-name (if t1 filename newname) nil
-       (unless length
-         (tramp-error v 'file-missing filename))
-       (tramp-barf-if-file-missing v filename
-         (when (and (not ok-if-already-exists) (file-exists-p newname))
-           (tramp-error v 'file-already-exists newname))
-         (when (and (file-directory-p newname)
-                    (not (directory-name-p newname)))
-           (tramp-error v 'file-error "File is a directory %s" newname))
-
-         (with-tramp-progress-reporter
-             v 0 (format "%s %s to %s" msg-operation filename newname)
+           (with-tramp-progress-reporter
+               v 0 (format "%s %s to %s" msg-operation filename newname)
 
-           (cond
-            ;; Both are Tramp files.
-            ((and t1 t2)
-             (with-parsed-tramp-file-name filename v1
-               (with-parsed-tramp-file-name newname v2
-                 (cond
-                  ;; Shortcut: if method, host, user are the same for
-                  ;; both files, we invoke `cp' or `mv' on the remote
-                  ;; host directly.
-                  ((tramp-equal-remote filename newname)
-                   (setq copy-keep-date
-                         (or (eq op 'rename) keep-date preserve-uid-gid))
-                   (tramp-do-copy-or-rename-file-directly
-                    op filename newname
-                    ok-if-already-exists keep-date preserve-uid-gid))
-
-                  ;; Try out-of-band operation.
-                  ((and
-                    (tramp-method-out-of-band-p v1 length)
-                    (tramp-method-out-of-band-p v2 length))
-                   (setq copy-keep-date
-                          (tramp-get-method-parameter v 'tramp-copy-keep-date))
-                   (tramp-do-copy-or-rename-file-out-of-band
-                    op filename newname ok-if-already-exists keep-date))
-
-                  ;; No shortcut was possible.  So we copy the file
-                  ;; first.  If the operation was `rename', we go
-                  ;; back and delete the original file (if the copy
-                  ;; was successful).  The approach is simple-minded:
-                  ;; we create a new buffer, insert the contents of
-                  ;; the source file into it, then write out the
-                  ;; buffer to the target file.  The advantage is
-                  ;; that it doesn't matter which file name handlers
-                  ;; are used for the source and target file.
-                  (t
-                   (tramp-do-copy-or-rename-file-via-buffer
-                    op filename newname ok-if-already-exists keep-date))))))
-
-            ;; One file is a Tramp file, the other one is local.
-            ((or t1 t2)
              (cond
-              ;; Fast track on local machine.
-              ((tramp-local-host-p v)
-               (setq copy-keep-date
-                     (or (eq op 'rename) keep-date preserve-uid-gid))
-               (tramp-do-copy-or-rename-file-directly
-                op filename newname
-                ok-if-already-exists keep-date preserve-uid-gid))
-
-              ;; If the Tramp file has an out-of-band method, the
-              ;; corresponding copy-program can be invoked.
-              ((tramp-method-out-of-band-p v length)
-               (setq copy-keep-date
-                      (tramp-get-method-parameter v 'tramp-copy-keep-date))
-               (tramp-do-copy-or-rename-file-out-of-band
-                op filename newname ok-if-already-exists keep-date))
+              ;; Both are Tramp files.
+              ((and t1 t2)
+               (with-parsed-tramp-file-name filename v1
+                 (with-parsed-tramp-file-name newname v2
+                   (cond
+                    ;; Shortcut: if method, host, user are the same
+                    ;; for both files, we invoke `cp' or `mv' on the
+                    ;; remote host directly.
+                    ((tramp-equal-remote filename newname)
+                     (setq copy-keep-date
+                           (or (eq op 'rename) keep-date preserve-uid-gid))
+                     (tramp-do-copy-or-rename-file-directly
+                      op filename newname
+                      ok-if-already-exists keep-date preserve-uid-gid))
+
+                    ;; Try out-of-band operation.
+                    ((and
+                      (tramp-method-out-of-band-p v1 length)
+                      (tramp-method-out-of-band-p v2 length))
+                     (setq copy-keep-date
+                            (tramp-get-method-parameter v 
'tramp-copy-keep-date))
+                     (tramp-do-copy-or-rename-file-out-of-band
+                      op filename newname ok-if-already-exists keep-date))
+
+                    ;; No shortcut was possible.  So we copy the file
+                    ;; first.  If the operation was `rename', we go
+                    ;; back and delete the original file (if the copy
+                    ;; was successful).  The approach is simple-minded:
+                    ;; we create a new buffer, insert the contents of
+                    ;; the source file into it, then write out the
+                    ;; buffer to the target file.  The advantage is
+                    ;; that it doesn't matter which file name handlers
+                    ;; are used for the source and target file.
+                    (t
+                     (tramp-do-copy-or-rename-file-via-buffer
+                      op filename newname ok-if-already-exists keep-date))))))
+
+              ;; One file is a Tramp file, the other one is local.
+              ((or t1 t2)
+               (cond
+                ;; Fast track on local machine.
+                ((tramp-local-host-p v)
+                 (setq copy-keep-date
+                       (or (eq op 'rename) keep-date preserve-uid-gid))
+                 (tramp-do-copy-or-rename-file-directly
+                  op filename newname
+                  ok-if-already-exists keep-date preserve-uid-gid))
+
+                ;; If the Tramp file has an out-of-band method, the
+                ;; corresponding copy-program can be invoked.
+                ((tramp-method-out-of-band-p v length)
+                 (setq copy-keep-date
+                       (tramp-get-method-parameter v 'tramp-copy-keep-date))
+                 (tramp-do-copy-or-rename-file-out-of-band
+                  op filename newname ok-if-already-exists keep-date))
+
+                ;; Use the inline method via a Tramp buffer.
+                (t (tramp-do-copy-or-rename-file-via-buffer
+                    op filename newname ok-if-already-exists keep-date))))
+
+              (t
+               ;; One of them must be a Tramp file.
+               (error "Tramp implementation says this cannot happen")))
+
+             ;; In case of `rename', we must flush the cache of the source 
file.
+             (when (and t1 (eq op 'rename))
+               (with-parsed-tramp-file-name filename v1
+                 (tramp-flush-file-properties v1 v1-localname)))
+
+             ;; NEWNAME has wrong cached values.
+             (when t2
+               (with-parsed-tramp-file-name newname v2
+                 (tramp-flush-file-properties v2 v2-localname)))
 
-              ;; Use the inline method via a Tramp buffer.
-              (t (tramp-do-copy-or-rename-file-via-buffer
-                  op filename newname ok-if-already-exists keep-date))))
+             ;; Handle `preserve-extended-attributes'.  We ignore
+             ;; possible errors, because ACL strings could be
+             ;; incompatible.
+             (when-let* ((attributes (and preserve-extended-attributes
+                                          (file-extended-attributes 
filename))))
+               (ignore-errors
+                 (set-file-extended-attributes newname attributes)))
 
-            (t
-             ;; One of them must be a Tramp file.
-             (error "Tramp implementation says this cannot happen")))
-
-           ;; In case of `rename', we must flush the cache of the source file.
-           (when (and t1 (eq op 'rename))
-             (with-parsed-tramp-file-name filename v1
-               (tramp-flush-file-properties v1 v1-localname)))
-
-           ;; NEWNAME has wrong cached values.
-           (when t2
-             (with-parsed-tramp-file-name newname v2
-               (tramp-flush-file-properties v2 v2-localname)))
-
-           ;; Handle `preserve-extended-attributes'.  We ignore
-           ;; possible errors, because ACL strings could be
-           ;; incompatible.
-           (when-let* ((attributes (and preserve-extended-attributes
-                                        (file-extended-attributes filename))))
-             (ignore-errors
-               (set-file-extended-attributes newname attributes)))
-
-            ;; KEEP-DATE handling.
-            (when (and keep-date (not copy-keep-date))
-              (tramp-compat-set-file-times
-               newname file-times (unless ok-if-already-exists 'nofollow)))
-
-            ;; Set the mode.
-            (unless (and keep-date copy-keep-date)
-              (set-file-modes newname file-modes))))))))
+              ;; KEEP-DATE handling.
+              (when (and keep-date (not copy-keep-date))
+               (tramp-compat-set-file-times
+                newname file-times (unless ok-if-already-exists 'nofollow)))
+
+              ;; Set the mode.
+              (unless (and keep-date copy-keep-date)
+               (set-file-modes newname file-modes)))))))))
 
 (defun tramp-do-copy-or-rename-file-via-buffer
     (op filename newname _ok-if-already-exists _keep-date)
@@ -3117,7 +3126,7 @@ will be used."
                          ;; character to read.  When a process does
                          ;; not read from stdin, like magit, it
                          ;; should set a timeout
-                         ;; instead. See`tramp-pipe-stty-settings'.
+                         ;; instead.  See `tramp-pipe-stty-settings'.
                          ;; (Bug#62093)
                          ;; FIXME: Shall we rather use "stty raw"?
                          (tramp-send-command
@@ -5653,7 +5662,7 @@ Nonexistent directories are removed from spec."
           (lambda (x) (not (tramp-get-file-property vec x "file-directory-p")))
           remote-path))))))
 
-;; The PIPE_BUF in POSIX [1] can be as low as 512 [2]. Here are the values
+;; The PIPE_BUF in POSIX [1] can be as low as 512 [2].  Here are the values
 ;; on various platforms:
 ;;   - 512 on macOS, FreeBSD, NetBSD, OpenBSD, MirBSD, native Windows.
 ;;   - 4 KiB on Linux, OSF/1, Cygwin, Haiku.
diff --git a/tramp-smb.el b/tramp-smb.el
index 716c6245f0..57fdd61a4c 100644
--- a/tramp-smb.el
+++ b/tramp-smb.el
@@ -114,6 +114,7 @@ this variable \"client min protocol=NT1\"."
        "Read from server failed, maybe it closed the connection"
        "Call timed out: server did not respond"
        (: (+ (not blank)) ": command not found")
+       (: (+ (not blank)) " does not exist")
        "Server doesn't support UNIX CIFS calls"
        (| ;; Samba.
        "ERRDOS"
@@ -597,66 +598,63 @@ KEEP-DATE has no effect in case NEWNAME resides on an SMB 
server.
 PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored."
   (setq filename (expand-file-name filename)
        newname (expand-file-name newname))
-  (with-tramp-progress-reporter
-      (tramp-dissect-file-name
-       (if (tramp-tramp-file-p filename) filename newname))
-      0 (format "Copying %s to %s" filename newname)
 
-    (if (file-directory-p filename)
-       (copy-directory filename newname keep-date 'parents 'copy-contents)
+  (with-parsed-tramp-file-name
+      (if (tramp-tramp-file-p filename) filename newname) nil
+    (with-tramp-progress-reporter
+       v 0 (format "Copying %s to %s" filename newname)
+
+      (if (file-directory-p filename)
+         (copy-directory filename newname keep-date 'parents 'copy-contents)
+
+       (tramp-barf-if-file-missing v filename
+         ;; `file-local-copy' returns a file name also for a local
+         ;; file with `jka-compr-handler', so we cannot trust its
+         ;; result as indication for a remote file name.
+         (if-let* ((tmpfile
+                    (and (tramp-tramp-file-p filename)
+                         (file-local-copy filename))))
+             ;; Remote filename.
+             (condition-case err
+                 (rename-file tmpfile newname ok-if-already-exists)
+               ((error quit)
+                (delete-file tmpfile)
+                (signal (car err) (cdr err))))
+
+           ;; Remote newname.
+           (when (and (file-directory-p newname)
+                      (directory-name-p newname))
+             (setq newname
+                   (expand-file-name
+                    (file-name-nondirectory filename) newname)))
+
+           (when (and (not ok-if-already-exists) (file-exists-p newname))
+             (tramp-error v 'file-already-exists newname))
+           (when (and (file-directory-p newname)
+                      (not (directory-name-p newname)))
+             (tramp-error v 'file-error "File is a directory %s" newname))
+
+           (unless (tramp-smb-get-share v)
+             (tramp-error
+              v 'file-error "Target `%s' must contain a share name" newname))
+           (unless (tramp-smb-send-command
+                    v (format "put %s %s"
+                              (tramp-smb-shell-quote-argument filename)
+                              (tramp-smb-shell-quote-localname v)))
+             (tramp-error
+              v 'file-error "Cannot copy `%s' to `%s'" filename newname))
 
-      (unless (file-exists-p filename)
-       (tramp-error
-        (tramp-dissect-file-name
-         (if (tramp-tramp-file-p filename) filename newname))
-        'file-missing filename))
-
-      ;; `file-local-copy' returns a file name also for a local file
-      ;; with `jka-compr-handler', so we cannot trust its result as
-      ;; indication for a remote file name.
-      (if-let* ((tmpfile
-                (and (tramp-tramp-file-p filename) (file-local-copy 
filename))))
-         ;; Remote filename.
-         (condition-case err
-             (rename-file tmpfile newname ok-if-already-exists)
-           ((error quit)
-            (delete-file tmpfile)
-            (signal (car err) (cdr err))))
-
-       ;; Remote newname.
-       (when (and (file-directory-p newname)
-                  (directory-name-p newname))
-         (setq newname
-               (expand-file-name (file-name-nondirectory filename) newname)))
-
-       (with-parsed-tramp-file-name newname nil
-         (when (and (not ok-if-already-exists) (file-exists-p newname))
-           (tramp-error v 'file-already-exists newname))
-         (when (and (file-directory-p newname)
-                    (not (directory-name-p newname)))
-           (tramp-error v 'file-error "File is a directory %s" newname))
-
-         (unless (tramp-smb-get-share v)
-           (tramp-error
-            v 'file-error "Target `%s' must contain a share name" newname))
-         (unless (tramp-smb-send-command
-                  v (format "put %s %s"
-                            (tramp-smb-shell-quote-argument filename)
-                            (tramp-smb-shell-quote-localname v)))
-           (tramp-error
-            v 'file-error "Cannot copy `%s' to `%s'" filename newname))
-
-         ;; When newname did exist, we have wrong cached values.
-         (when (tramp-tramp-file-p newname)
-           (with-parsed-tramp-file-name newname v2
-             (tramp-flush-file-properties v2 v2-localname))))))
-
-    ;; KEEP-DATE handling.
-    (when keep-date
-      (tramp-compat-set-file-times
-       newname
-       (file-attribute-modification-time (file-attributes filename))
-       (unless ok-if-already-exists 'nofollow)))))
+           ;; When newname did exist, we have wrong cached values.
+           (when (tramp-tramp-file-p newname)
+             (with-parsed-tramp-file-name newname v2
+               (tramp-flush-file-properties v2 v2-localname))))))
+
+      ;; KEEP-DATE handling.
+      (when keep-date
+       (tramp-compat-set-file-times
+        newname
+        (file-attribute-modification-time (file-attributes filename))
+        (unless ok-if-already-exists 'nofollow))))))
 
 (defun tramp-smb-handle-delete-directory (directory &optional recursive trash)
   "Like `delete-directory' for Tramp files."
@@ -1308,46 +1306,45 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are 
completely ignored."
 
   (with-parsed-tramp-file-name
       (if (tramp-tramp-file-p filename) filename newname) nil
-    (unless (file-exists-p filename)
-      (tramp-error v 'file-missing filename))
-    (when (and (not ok-if-already-exists) (file-exists-p newname))
-      (tramp-error v 'file-already-exists newname))
-    (when (and (file-directory-p newname)
-              (not (directory-name-p newname)))
-      (tramp-error v 'file-error "File is a directory %s" newname))
+    (tramp-barf-if-file-missing v filename
+      (when (and (not ok-if-already-exists) (file-exists-p newname))
+       (tramp-error v 'file-already-exists newname))
+      (when (and (file-directory-p newname)
+                (not (directory-name-p newname)))
+       (tramp-error v 'file-error "File is a directory %s" newname))
 
-    (with-tramp-progress-reporter
-       v 0 (format "Renaming %s to %s" filename newname)
-
-      (if (and (not (file-exists-p newname))
-              (tramp-equal-remote filename newname)
-              (string-equal
-               (tramp-smb-get-share (tramp-dissect-file-name filename))
-               (tramp-smb-get-share (tramp-dissect-file-name newname))))
-         ;; We can rename directly.
-         (with-parsed-tramp-file-name filename v1
-           (with-parsed-tramp-file-name newname v2
-
-             ;; We must also flush the cache of the directory, because
-             ;; `file-attributes' reads the values from there.
-             (tramp-flush-file-properties v1 v1-localname)
-             (tramp-flush-file-properties v2 v2-localname)
-             (unless (tramp-smb-get-share v2)
-               (tramp-error
-                v2 'file-error
-                "Target `%s' must contain a share name" newname))
-             (unless (tramp-smb-send-command
-                      v2 (format "rename %s %s"
-                                 (tramp-smb-shell-quote-localname v1)
-                                 (tramp-smb-shell-quote-localname v2)))
-               (tramp-error v2 'file-error "Cannot rename `%s'" filename))))
-
-       ;; We must rename via copy.
-       (copy-file
-        filename newname ok-if-already-exists 'keep-time 'preserve-uid-gid)
-       (if (file-directory-p filename)
-           (delete-directory filename 'recursive)
-         (delete-file filename))))))
+      (with-tramp-progress-reporter
+         v 0 (format "Renaming %s to %s" filename newname)
+
+       (if (and (not (file-exists-p newname))
+                (tramp-equal-remote filename newname)
+                (string-equal
+                 (tramp-smb-get-share (tramp-dissect-file-name filename))
+                 (tramp-smb-get-share (tramp-dissect-file-name newname))))
+           ;; We can rename directly.
+           (with-parsed-tramp-file-name filename v1
+             (with-parsed-tramp-file-name newname v2
+
+               ;; We must also flush the cache of the directory, because
+               ;; `file-attributes' reads the values from there.
+               (tramp-flush-file-properties v1 v1-localname)
+               (tramp-flush-file-properties v2 v2-localname)
+               (unless (tramp-smb-get-share v2)
+                 (tramp-error
+                  v2 'file-error
+                  "Target `%s' must contain a share name" newname))
+               (unless (tramp-smb-send-command
+                        v2 (format "rename %s %s"
+                                   (tramp-smb-shell-quote-localname v1)
+                                   (tramp-smb-shell-quote-localname v2)))
+                 (tramp-error v2 'file-error "Cannot rename `%s'" filename))))
+
+         ;; We must rename via copy.
+         (copy-file
+          filename newname ok-if-already-exists 'keep-time 'preserve-uid-gid)
+         (if (file-directory-p filename)
+             (delete-directory filename 'recursive)
+           (delete-file filename)))))))
 
 (defun tramp-smb-action-set-acl (proc vec)
   "Set ACL data."
diff --git a/tramp-sshfs.el b/tramp-sshfs.el
index eb46258fcc..0efa7bd53f 100644
--- a/tramp-sshfs.el
+++ b/tramp-sshfs.el
@@ -250,6 +250,9 @@ arguments to pass to the OPERATION."
 (defun tramp-sshfs-handle-process-file
   (program &optional infile destination display &rest args)
   "Like `process-file' for Tramp files."
+  ;; STDERR is not impelmemted.
+  (when (consp destination)
+    (setcdr destination `(,tramp-cache-undefined)))
   (tramp-skeleton-process-file program infile destination display args
     (let ((coding-system-for-read 'utf-8-dos)) ; Is this correct?
 
@@ -259,25 +262,18 @@ arguments to pass to the OPERATION."
            (tramp-unquote-shell-quote-argument localname)
            (mapconcat #'tramp-shell-quote-argument (cons program args) " ")))
       (when input (setq command (format "%s <%s" command input)))
-      (when stderr (setq command (format "%s 2>%s" command stderr)))
-
-      (unwind-protect
-         (setq ret
-               (apply
-                #'tramp-call-process
-                v (tramp-get-method-parameter v 'tramp-login-program)
-                nil outbuf display
-                (tramp-expand-args
-                 v 'tramp-login-args nil
-                 ?h (or (tramp-file-name-host v) "")
-                 ?u (or (tramp-file-name-user v) "")
-                 ?p (or (tramp-file-name-port v) "")
-                 ?a "-t" ?l command)))
-
-       ;; Synchronize stderr.
-       (when tmpstderr
-         (tramp-cleanup-connection v 'keep-debug 'keep-password)
-         (tramp-fuse-unmount v))))))
+
+      (setq ret
+           (apply
+            #'tramp-call-process
+            v (tramp-get-method-parameter v 'tramp-login-program)
+            nil outbuf display
+            (tramp-expand-args
+             v 'tramp-login-args nil
+             ?h (or (tramp-file-name-host v) "")
+             ?u (or (tramp-file-name-user v) "")
+             ?p (or (tramp-file-name-port v) "")
+             ?a "-t" ?l command))))))
 
 (defun tramp-sshfs-handle-rename-file
     (filename newname &optional ok-if-already-exists)
diff --git a/tramp-sudoedit.el b/tramp-sudoedit.el
index bd902a45fc..ff01eac5b9 100644
--- a/tramp-sudoedit.el
+++ b/tramp-sudoedit.el
@@ -244,84 +244,88 @@ absolute file names."
   (unless (memq op '(copy rename))
     (error "Unknown operation `%s', must be `copy' or `rename'" op))
 
-  (setq filename (file-truename filename))
   (if (file-directory-p filename)
       (progn
        (copy-directory filename newname keep-date t)
        (when (eq op 'rename) (delete-directory filename 'recursive)))
-
-    ;; FIXME: This should be optimized.  Computing `file-attributes'
-    ;; checks already, whether the file exists.
-    (let ((t1 (tramp-sudoedit-file-name-p filename))
-         (t2 (tramp-sudoedit-file-name-p newname))
-         (file-times (file-attribute-modification-time
-                      (file-attributes filename)))
-         (file-modes (tramp-default-file-modes filename))
-         (attributes (and preserve-extended-attributes
-                          (file-extended-attributes filename)))
-         (sudoedit-operation
-          (cond
-           ((and (eq op 'copy) preserve-uid-gid) '("cp" "-f" "-p"))
-           ((eq op 'copy) '("cp" "-f"))
-           ((eq op 'rename) '("mv" "-f"))))
-         (msg-operation (if (eq op 'copy) "Copying" "Renaming")))
-
-      (with-parsed-tramp-file-name (if t1 filename newname) nil
-       (tramp-barf-if-file-missing v filename
-         (when (and (not ok-if-already-exists) (file-exists-p newname))
-           (tramp-error v 'file-already-exists newname))
-         (when (and (file-directory-p newname)
-                    (not (directory-name-p newname)))
-           (tramp-error v 'file-error "File is a directory %s" newname))
-
-         (if (or (and (tramp-tramp-file-p filename) (not t1))
-                 (and (tramp-tramp-file-p newname)  (not t2)))
-             ;; We cannot copy or rename directly.
-             (let ((tmpfile (tramp-compat-make-temp-file filename)))
-               (if (eq op 'copy)
-                   (copy-file filename tmpfile t)
-                 (rename-file filename tmpfile t))
-               (rename-file tmpfile newname ok-if-already-exists))
-
-           ;; Direct action.
-           (with-tramp-progress-reporter
-               v 0 (format "%s %s to %s" msg-operation filename newname)
-             (unless (tramp-sudoedit-send-command
-                      v sudoedit-operation
-                      (tramp-unquote-file-local-name filename)
-                      (tramp-unquote-file-local-name newname))
-               (tramp-error
-                v 'file-error
-                "Error %s `%s' `%s'" msg-operation filename newname))))
-
-         ;; When `newname' is local, we must change the ownership to
-         ;; the local user.
-         (unless (tramp-tramp-file-p newname)
-           (tramp-set-file-uid-gid
-            (concat (file-remote-p filename) newname)
-            (tramp-get-local-uid 'integer)
-            (tramp-get-local-gid 'integer)))
-
-         ;; Set the time and mode. Mask possible errors.
-         (when keep-date
-           (ignore-errors
-             (tramp-compat-set-file-times
-              newname file-times (unless ok-if-already-exists 'nofollow))
-             (set-file-modes newname file-modes)))
-
-         ;; Handle `preserve-extended-attributes'.  We ignore possible
-         ;; errors, because ACL strings could be incompatible.
-         (when attributes
-           (ignore-errors
-             (set-file-extended-attributes newname attributes)))
-
-         (when (and t1 (eq op 'rename))
-           (with-parsed-tramp-file-name filename v1
-             (tramp-flush-file-properties v1 v1-localname)))
-
-         (when t2
-           (with-parsed-tramp-file-name newname v2
-             (tramp-flush-file-properties v2 v2-localname))))))))
+    (if (file-symlink-p filename)
+       (progn
+         (make-symbolic-link
+          (file-symlink-p filename) newname ok-if-already-exists)
+         (when (eq op 'rename) (delete-file filename)))
+
+      ;; FIXME: This should be optimized.  Computing `file-attributes'
+      ;; checks already, whether the file exists.
+      (let ((t1 (tramp-sudoedit-file-name-p filename))
+           (t2 (tramp-sudoedit-file-name-p newname))
+           (file-times (file-attribute-modification-time
+                        (file-attributes filename)))
+           (file-modes (tramp-default-file-modes filename))
+           (attributes (and preserve-extended-attributes
+                            (file-extended-attributes filename)))
+           (sudoedit-operation
+            (cond
+             ((and (eq op 'copy) preserve-uid-gid) '("cp" "-f" "-p"))
+             ((eq op 'copy) '("cp" "-f"))
+             ((eq op 'rename) '("mv" "-f"))))
+           (msg-operation (if (eq op 'copy) "Copying" "Renaming")))
+
+       (with-parsed-tramp-file-name (if t1 filename newname) nil
+         (tramp-barf-if-file-missing v filename
+           (when (and (not ok-if-already-exists) (file-exists-p newname))
+             (tramp-error v 'file-already-exists newname))
+           (when (and (file-directory-p newname)
+                      (not (directory-name-p newname)))
+             (tramp-error v 'file-error "File is a directory %s" newname))
+
+           (if (or (and (tramp-tramp-file-p filename) (not t1))
+                   (and (tramp-tramp-file-p newname)  (not t2)))
+               ;; We cannot copy or rename directly.
+               (let ((tmpfile (tramp-compat-make-temp-file filename)))
+                 (if (eq op 'copy)
+                     (copy-file filename tmpfile t)
+                   (rename-file filename tmpfile t))
+                 (rename-file tmpfile newname ok-if-already-exists))
+
+             ;; Direct action.
+             (with-tramp-progress-reporter
+                 v 0 (format "%s %s to %s" msg-operation filename newname)
+               (unless (tramp-sudoedit-send-command
+                        v sudoedit-operation
+                        (tramp-unquote-file-local-name filename)
+                        (tramp-unquote-file-local-name newname))
+                 (tramp-error
+                  v 'file-error
+                  "Error %s `%s' `%s'" msg-operation filename newname))))
+
+           ;; When `newname' is local, we must change the ownership
+           ;; to the local user.
+           (unless (tramp-tramp-file-p newname)
+             (tramp-set-file-uid-gid
+              (concat (file-remote-p filename) newname)
+              (tramp-get-local-uid 'integer)
+              (tramp-get-local-gid 'integer)))
+
+           ;; Set the time and mode.  Mask possible errors.
+           (when keep-date
+             (ignore-errors
+               (tramp-compat-set-file-times
+                newname file-times (unless ok-if-already-exists 'nofollow))
+               (set-file-modes newname file-modes)))
+
+           ;; Handle `preserve-extended-attributes'.  We ignore possible
+           ;; errors, because ACL strings could be incompatible.
+           (when attributes
+             (ignore-errors
+               (set-file-extended-attributes newname attributes)))
+
+           (when (and t1 (eq op 'rename))
+             (with-parsed-tramp-file-name filename v1
+               (tramp-flush-file-properties v1 v1-localname)))
+
+           (when t2
+             (with-parsed-tramp-file-name newname v2
+               (tramp-flush-file-properties v2 v2-localname)))))))))
 
 (defun tramp-sudoedit-handle-copy-file
   (filename newname &optional ok-if-already-exists keep-date
diff --git a/tramp.el b/tramp.el
index f34bb80dc6..ff34c5356d 100644
--- a/tramp.el
+++ b/tramp.el
@@ -7,13 +7,15 @@
 ;; Maintainer: Michael Albinus <michael.albi...@gmx.de>
 ;; Keywords: comm, processes
 ;; Package: tramp
-;; Version: 2.7.2.2
+;; Version: 2.7.2.3
 ;; Package-Requires: ((emacs 27.1))
 ;; Package-Type: multi
 ;; URL: https://www.gnu.org/software/tramp/
 
-;; This is a GNU ELPA :core package.  Avoid functionality that is not
+;; This is also a GNU ELPA package.  Avoid functionality that is not
 ;; compatible with the version of Emacs recorded in trampver.el.
+;; Version and Package-Requires are place holders.  They are updated
+;; when the GNU ELPA package is released.
 
 ;; This file is part of GNU Emacs.
 
@@ -124,9 +126,8 @@
   :version "22.1"
   :link '(custom-manual "(tramp)Top"))
 
-;; On MS-DOS, there is no process support.
 ;;;###autoload
-(defcustom tramp-mode (not (eq system-type 'ms-dos))
+(defcustom tramp-mode (fboundp 'make-process) ; Disable on MS-DOS.
   "Whether Tramp is enabled.
 If it is set to nil, all remote file names are used literally."
   :type 'boolean)
@@ -2099,7 +2100,7 @@ does not exist, otherwise propagate the error."
     `(condition-case ,err
          (progn ,@body)
        (error
-       (if (not (file-exists-p ,filename))
+       (if (not (or (file-exists-p ,filename) (file-symlink-p ,filename)))
            (tramp-error ,vec 'file-missing ,filename)
          (signal (car ,err) (cdr ,err)))))))
 
@@ -3538,12 +3539,19 @@ BODY is the backend specific code."
        (when (tramp-connectable-p ,filename)
         (with-parsed-tramp-file-name (expand-file-name ,filename) nil
           (with-tramp-file-property v localname "file-exists-p"
-            ;; Examine `file-attributes' cache to see if request can
-            ;; be satisfied without remote operation.
-            (if (tramp-file-property-p v localname "file-attributes")
-                (not
-                 (null (tramp-get-file-property v localname 
"file-attributes")))
-              ,@body))))))
+            (cond
+             ;; Examine `file-attributes' cache to see if request can
+             ;; be satisfied without remote operation.
+             ((and-let*
+                  (((tramp-file-property-p v localname "file-attributes"))
+                   (fa (tramp-get-file-property v localname "file-attributes"))
+                   ((not (stringp (car fa)))))))
+             ;; Symlink to a non-existing target counts as nil.
+             ;; Protect against cyclic symbolic links.
+             ((file-symlink-p ,filename)
+              (ignore-errors
+                (file-exists-p (file-truename ,filename))))
+             (t ,@body)))))))
 
 (defmacro tramp-skeleton-file-local-copy (filename &rest body)
   "Skeleton for `tramp-*-handle-file-local-copy'.
@@ -3767,10 +3775,13 @@ BODY is the backend specific code."
                   tmpstderr (tramp-make-tramp-file-name v stderr))))
          ;; stderr to be discarded.
          ((null (cadr ,destination))
-          (setq stderr (tramp-get-remote-null-device v)))))
+          (setq stderr (tramp-get-remote-null-device v)))
+         ((eq (cadr ,destination) tramp-cache-undefined)
+          ;; stderr is not impelmemted.
+          (tramp-warning v "%s" "STDERR not supported"))))
        ;; t
        (,destination
-       (setq outbuf (current-buffer))))
+        (setq outbuf (current-buffer))))
 
        ,@body
 
@@ -3806,7 +3817,7 @@ BODY is the backend specific code."
         ;; We cannot add "file-attributes", "file-executable-p",
         ;; "file-ownership-preserved-p", "file-readable-p",
         ;; "file-writable-p".
-        '("file-directory-p" "file-exists-p" "file-symlinkp" "file-truename")
+        '("file-directory-p" "file-exists-p" "file-symlink-p" "file-truename")
        (tramp-flush-file-properties v localname))
      (condition-case err
         (progn ,@body)
@@ -4148,10 +4159,9 @@ Let-bind it when necessary.")
 (defun tramp-handle-file-directory-p (filename)
   "Like `file-directory-p' for Tramp files."
   ;; `file-truename' could raise an error, for example due to a cyclic
-  ;; symlink.  We don't protect this despite it, because other errors
-  ;; might be worth to be visible, for example impossibility to mount
-  ;; in tramp-gvfs.el.
-  (eq (file-attribute-type (file-attributes (file-truename filename))) t))
+  ;; symlink.
+  (ignore-errors
+    (eq (file-attribute-type (file-attributes (file-truename filename))) t)))
 
 (defun tramp-handle-file-equal-p (filename1 filename2)
   "Like `file-equalp-p' for Tramp files."
@@ -5462,8 +5472,22 @@ support symbolic links."
                         (insert-file-contents-literally
                          error-file nil nil nil 'replace))
                       (delete-file error-file)))))
-               (display-buffer output-buffer '(nil (allow-no-window . t)))))
-
+                (if async-shell-command-display-buffer
+                    ;; Display buffer immediately.
+                    (display-buffer output-buffer '(nil (allow-no-window . t)))
+                  ;; Defer displaying buffer until first process output.
+                  ;; Use disposable named advice so that the buffer is
+                  ;; displayed at most once per process lifetime.
+                  (let ((nonce (make-symbol "nonce")))
+                    (add-function
+                    :before (process-filter p)
+                     (lambda (proc _string)
+                       (let ((buf (process-buffer proc)))
+                         (when (buffer-live-p buf)
+                           (remove-function (process-filter proc)
+                                            nonce)
+                           (display-buffer buf '(nil (allow-no-window . t))))))
+                     `((name . ,nonce)))))))
            ;; Insert error messages if they were separated.
            (when (and error-file (not (process-live-p p)))
              (ignore-errors
diff --git a/trampver.el b/trampver.el
index af22093e04..2c5095debc 100644
--- a/trampver.el
+++ b/trampver.el
@@ -7,7 +7,7 @@
 ;; Maintainer: Michael Albinus <michael.albi...@gmx.de>
 ;; Keywords: comm, processes
 ;; Package: tramp
-;; Version: 2.7.2.2
+;; Version: 2.7.2.3
 ;; Package-Requires: ((emacs "27.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.7.2.2"
+(defconst tramp-version "2.7.2.3"
   "This version of Tramp.")
 
 ;;;###tramp-autoload
@@ -76,7 +76,7 @@
 ;; Check for Emacs version.
 (let ((x   (if (not (string-version-lessp emacs-version "27.1"))
       "ok"
-    (format "Tramp 2.7.2.2 is not fit for %s"
+    (format "Tramp 2.7.2.3 is not fit for %s"
             (replace-regexp-in-string "\n" "" (emacs-version))))))
   (unless (string-equal "ok" x) (error "%s" x)))
 

Reply via email to