summaryrefslogtreecommitdiff
path: root/lisp/net/tramp-sudoedit.el
diff options
context:
space:
mode:
authorMichael Albinus <michael.albinus@gmx.de>2025-03-16 14:17:38 +0100
committerMichael Albinus <michael.albinus@gmx.de>2025-03-16 14:17:38 +0100
commitb8104dadbf285d12c356d4cddd28ac3eaf05f263 (patch)
treefdee9ed3209a12b0957d4a7699db51e0e2d05e8e /lisp/net/tramp-sudoedit.el
parent03e33cbef3e33aa1ec843388d1671f7116a7347b (diff)
downloademacs-b8104dadbf285d12c356d4cddd28ac3eaf05f263.tar.gz
emacs-b8104dadbf285d12c356d4cddd28ac3eaf05f263.tar.bz2
emacs-b8104dadbf285d12c356d4cddd28ac3eaf05f263.zip
Tramp: Handle symlinks to non-existing targets better
* lisp/net/tramp-gvfs.el (tramp-gvfs-do-copy-or-rename-file): Don't use the truename. * lisp/net/tramp-sh.el (tramp-do-copy-or-rename-file): Refactor. Handle symlinks. (Bug#76678) * lisp/net/tramp-smb.el (tramp-smb-errors): Add string. (tramp-smb-handle-copy-file, tramp-smb-handle-rename-file): Refactor. * lisp/net/tramp-sudoedit.el (tramp-sudoedit-do-copy-or-rename-file): Don't use the truename. Handle symlinks. * lisp/net/tramp.el (tramp-barf-if-file-missing): Accept also symlinks. (tramp-skeleton-file-exists-p): Handle non-existing symlink targets. (tramp-skeleton-set-file-modes-times-uid-gid): Fix typo. * test/lisp/net/tramp-tests.el (vc-handled-backends): Suppress only if noninteractive. (tramp-test11-copy-file, tramp-test12-rename-file) (tramp-test18-file-attributes, tramp-test21-file-links) (tramp--test-check-files): Adapt tests.
Diffstat (limited to 'lisp/net/tramp-sudoedit.el')
-rw-r--r--lisp/net/tramp-sudoedit.el152
1 files changed, 78 insertions, 74 deletions
diff --git a/lisp/net/tramp-sudoedit.el b/lisp/net/tramp-sudoedit.el
index 0202f933b74..517bd85736a 100644
--- a/lisp/net/tramp-sudoedit.el
+++ b/lisp/net/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
- (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
+ (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