1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-01-06 03:40:56 -08:00

Factor out some Tramp code

* lisp/net/tramp-compat.el (tramp-file-name-handler): Don't declare.

* lisp/net/tramp.el (tramp-skeleton-file-truename)
(tramp-skeleton-handle-make-symbolic-link): New defmacros.
(tramp-handle-file-truename):
* lisp/net/tramp-sh.el (tramp-sh-handle-make-symbolic-link)
(tramp-sh-handle-file-truename):
* lisp/net/tramp-smb.el (tramp-smb-handle-make-symbolic-link):
* lisp/net/tramp-sudoedit.el (tramp-sudoedit-handle-file-truename)
(tramp-sudoedit-handle-make-symbolic-link): Use them.

* lisp/net/tramp.el (tramp-call-process, tramp-call-process-region):
Let-bind `temporary-file-directory'.

* test/lisp/net/tramp-tests.el (tramp-action-yesno):
Suppress run in tests.
(tramp-test21-file-links, tramp-test29-start-file-process)
(tramp-test30-make-process, tramp-test42-utf8): Adapt tests.
This commit is contained in:
Michael Albinus 2023-01-23 11:02:56 +01:00
parent 26ef5c09e0
commit 85e3304332
6 changed files with 180 additions and 261 deletions

View file

@ -37,7 +37,6 @@
(require 'subr-x)
(declare-function tramp-error "tramp")
(declare-function tramp-file-name-handler "tramp")
(declare-function tramp-tramp-file-p "tramp")
(defvar tramp-temp-name-prefix)

View file

@ -1132,82 +1132,31 @@ Operations not mentioned here will be handled by the normal Emacs functions.")
(defun tramp-sh-handle-make-symbolic-link
(target linkname &optional ok-if-already-exists)
"Like `make-symbolic-link' for Tramp files.
If TARGET is a non-Tramp file, it is used verbatim as the target
of the symlink. If TARGET is a Tramp file, only the localname
component is used as the target of the symlink."
(with-parsed-tramp-file-name (expand-file-name linkname) nil
;; If TARGET is a Tramp name, use just the localname component.
;; Don't check for a proper method.
(let ((non-essential t))
(when (and (tramp-tramp-file-p target)
(tramp-file-name-equal-p v (tramp-dissect-file-name target)))
(setq target (tramp-file-local-name (expand-file-name target))))
;; There could be a cyclic link.
(tramp-flush-file-properties
v (expand-file-name target (tramp-file-local-name default-directory))))
;; If TARGET is still remote, quote it.
(if (tramp-tramp-file-p target)
(make-symbolic-link
(file-name-quote target 'top) linkname ok-if-already-exists)
(let ((ln (tramp-get-remote-ln v))
(cwd (tramp-run-real-handler
#'file-name-directory (list localname))))
(unless ln
"Like `make-symbolic-link' for Tramp files."
(let ((v (tramp-dissect-file-name (expand-file-name linkname))))
(unless (tramp-get-remote-ln v)
(tramp-error
v 'file-error
(concat "Making a symbolic link. "
"ln(1) does not exist on the remote host.")))
"ln(1) does not exist on the remote host."))))
;; Do the 'confirm if exists' thing.
(when (file-exists-p linkname)
;; What to do?
(if (or (null ok-if-already-exists) ; not allowed to exist
(and (numberp ok-if-already-exists)
(not
(yes-or-no-p
(format
"File %s already exists; make it a link anyway?"
localname)))))
(tramp-error v 'file-already-exists localname)
(delete-file linkname)))
(tramp-flush-file-properties v localname)
;; Right, they are on the same host, regardless of user,
;; method, etc. We now make the link on the remote machine.
;; This will occur as the user that TARGET belongs to.
(tramp-skeleton-handle-make-symbolic-link target linkname ok-if-already-exists
(and (tramp-send-command-and-check
v (format "cd %s" (tramp-shell-quote-argument cwd)))
v (format
"cd %s"
(tramp-shell-quote-argument (file-name-directory localname))))
(tramp-send-command-and-check
v (format
"%s -sf %s %s" ln
"%s -sf %s %s" (tramp-get-remote-ln v)
(tramp-shell-quote-argument target)
;; The command could exceed PATH_MAX, so we use
;; relative file names. However, relative file names
;; could start with "-".
;; `tramp-shell-quote-argument' does not handle this,
;; we must do it ourselves.
;; The command could exceed PATH_MAX, so we use relative
;; file names.
(tramp-shell-quote-argument
(concat "./" (file-name-nondirectory localname))))))))))
(concat "./" (file-name-nondirectory localname))))))))
(defun tramp-sh-handle-file-truename (filename)
"Like `file-truename' for Tramp files."
;; Preserve trailing "/".
(funcall
(if (directory-name-p filename) #'file-name-as-directory #'identity)
;; Quote properly.
(funcall
(if (file-name-quoted-p filename) #'file-name-quote #'identity)
(with-parsed-tramp-file-name
(file-name-unquote (expand-file-name filename)) nil
(tramp-make-tramp-file-name
v
(with-tramp-file-property v localname "file-truename"
(tramp-message v 4 "Finding true name for `%s'" filename)
(let ((result
(tramp-skeleton-file-truename filename
(cond
;; Use GNU readlink --canonicalize-missing where available.
((tramp-get-remote-readlink v)
@ -1233,19 +1182,6 @@ component is used as the target of the symlink."
(t (tramp-file-local-name
(tramp-handle-file-truename filename))))))
;; Detect cycle.
(when (and (file-symlink-p filename)
(string-equal result localname))
(tramp-error
v 'file-error
"Apparent cycle of symbolic links for %s" filename))
;; If the resulting localname looks remote, we must quote it
;; for security reasons.
(when (file-remote-p result)
(setq result (file-name-quote result 'top)))
(tramp-message v 4 "True name of `%s' is `%s'" localname result)
result)))))))
;; Basic functions.
(defun tramp-sh-handle-file-exists-p (filename)

View file

@ -1176,42 +1176,12 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored."
(defun tramp-smb-handle-make-symbolic-link
(target linkname &optional ok-if-already-exists)
"Like `make-symbolic-link' for Tramp files.
If TARGET is a non-Tramp file, it is used verbatim as the target
of the symlink. If TARGET is a Tramp file, only the localname
component is used as the target of the symlink."
(with-parsed-tramp-file-name linkname nil
;; If TARGET is a Tramp name, use just the localname component.
;; Don't check for a proper method.
(let ((non-essential t))
(when (and (tramp-tramp-file-p target)
(tramp-file-name-equal-p v (tramp-dissect-file-name target)))
(setq target (tramp-file-local-name (expand-file-name target)))))
;; If TARGET is still remote, quote it.
(if (tramp-tramp-file-p target)
(make-symbolic-link
(file-name-quote target 'top) linkname ok-if-already-exists)
;; Do the 'confirm if exists' thing.
(when (file-exists-p linkname)
;; What to do?
(if (or (null ok-if-already-exists) ; not allowed to exist
(and (numberp ok-if-already-exists)
(not (yes-or-no-p
(format
"File %s already exists; make it a link anyway?"
localname)))))
(tramp-error v 'file-already-exists localname)
(delete-file linkname)))
"Like `make-symbolic-link' for Tramp files."
(let ((v (tramp-dissect-file-name (expand-file-name linkname))))
(unless (tramp-smb-get-cifs-capabilities v)
(tramp-error v 'file-error "make-symbolic-link not supported"))
;; We must also flush the cache of the directory, because
;; `file-attributes' reads the values from there.
(tramp-flush-file-properties v localname)
(tramp-error v 'file-error "make-symbolic-link not supported")))
(tramp-skeleton-handle-make-symbolic-link target linkname ok-if-already-exists
(unless (tramp-smb-send-command
v (format "symlink %s %s"
(tramp-smb-shell-quote-argument target)
@ -1219,7 +1189,7 @@ component is used as the target of the symlink."
(tramp-error
v 'file-error
"error with make-symbolic-link, see buffer `%s' for details"
(tramp-get-connection-buffer v))))))
(tramp-get-connection-buffer v)))))
(defun tramp-smb-handle-process-file
(program &optional infile destination display &rest args)

View file

@ -568,33 +568,9 @@ the result will be a local, non-Tramp, file name."
(defun tramp-sudoedit-handle-file-truename (filename)
"Like `file-truename' for Tramp files."
;; Preserve trailing "/".
(funcall
(if (directory-name-p filename) #'file-name-as-directory #'identity)
;; Quote properly.
(funcall
(if (file-name-quoted-p filename) #'file-name-quote #'identity)
(with-parsed-tramp-file-name
(file-name-unquote (expand-file-name filename)) nil
(tramp-make-tramp-file-name
v
(with-tramp-file-property v localname "file-truename"
(let (result)
(tramp-message v 4 "Finding true name for `%s'" filename)
(setq result (tramp-sudoedit-send-command-string
v "readlink" "--canonicalize-missing" localname))
;; Detect cycle.
(when (and (file-symlink-p filename)
(string-equal result localname))
(tramp-error
v 'file-error
"Apparent cycle of symbolic links for %s" filename))
;; If the resulting localname looks remote, we must quote it
;; for security reasons.
(when (file-remote-p result)
(setq result (file-name-quote result 'top)))
(tramp-message v 4 "True name of `%s' is `%s'" localname result)
result)))))))
(tramp-skeleton-file-truename filename
(tramp-sudoedit-send-command-string
v "readlink" "--canonicalize-missing" localname)))
(defun tramp-sudoedit-handle-file-writable-p (filename)
"Like `file-writable-p' for Tramp files."
@ -622,41 +598,12 @@ the result will be a local, non-Tramp, file name."
(defun tramp-sudoedit-handle-make-symbolic-link
(target linkname &optional ok-if-already-exists)
"Like `make-symbolic-link' for Tramp files.
If TARGET is a non-Tramp file, it is used verbatim as the target
of the symlink. If TARGET is a Tramp file, only the localname
component is used as the target of the symlink."
(with-parsed-tramp-file-name (expand-file-name linkname) nil
;; If TARGET is a Tramp name, use just the localname component.
;; Don't check for a proper method.
(let ((non-essential t))
(when (and (tramp-tramp-file-p target)
(tramp-file-name-equal-p v (tramp-dissect-file-name target)))
(setq target (tramp-file-local-name (expand-file-name target)))))
;; If TARGET is still remote, quote it.
(if (tramp-tramp-file-p target)
(make-symbolic-link
(file-name-quote target 'top) linkname ok-if-already-exists)
;; Do the 'confirm if exists' thing.
(when (file-exists-p linkname)
;; What to do?
(if (or (null ok-if-already-exists) ; not allowed to exist
(and (numberp ok-if-already-exists)
(not
(yes-or-no-p
(format
"File %s already exists; make it a link anyway?"
localname)))))
(tramp-error v 'file-already-exists localname)
(delete-file linkname)))
(tramp-flush-file-properties v localname)
"Like `make-symbolic-link' for Tramp files."
(tramp-skeleton-handle-make-symbolic-link target linkname ok-if-already-exists
(tramp-sudoedit-send-command
v "ln" "-sf"
(file-name-unquote target)
(file-name-unquote localname)))))
(file-name-unquote localname))))
(defun tramp-sudoedit-handle-rename-file
(filename newname &optional ok-if-already-exists)

View file

@ -3529,6 +3529,35 @@ BODY is the backend specific code."
;; Trigger the `file-missing' error.
(signal 'error nil)))))
(defmacro tramp-skeleton-file-truename (filename &rest body)
"Skeleton for `tramp-*-handle-file-truename'.
BODY is the backend specific code."
(declare (indent 1) (debug (form body)))
;; Preserve trailing "/".
`(funcall
(if (directory-name-p ,filename) #'file-name-as-directory #'identity)
;; Quote properly.
(funcall
(if (file-name-quoted-p ,filename) #'file-name-quote #'identity)
(with-parsed-tramp-file-name
(file-name-unquote (expand-file-name ,filename)) nil
(tramp-make-tramp-file-name
v
(with-tramp-file-property v localname "file-truename"
(let (result)
(setq result (progn ,@body))
;; Detect cycle.
(when (and (file-symlink-p ,filename)
(string-equal result localname))
(tramp-error
v 'file-error
"Apparent cycle of symbolic links for %s" ,filename))
;; If the resulting localname looks remote, we must quote
;; it for security reasons.
(when (file-remote-p result)
(setq result (file-name-quote result 'top)))
result)))))))
(defmacro tramp-skeleton-make-directory (dir &optional parents &rest body)
"Skeleton for `tramp-*-handle-make-directory'.
BODY is the backend specific code."
@ -3550,6 +3579,49 @@ BODY is the backend specific code."
,@body
nil))))
(defmacro tramp-skeleton-handle-make-symbolic-link
(target linkname &optional ok-if-already-exists &rest body)
"Skeleton for `tramp-*-handle-make-symbolic-link'.
BODY is the backend specific code.
If TARGET is a non-Tramp file, it is used verbatim as the target
of the symlink. If TARGET is a Tramp file, only the localname
component is used as the target of the symlink if it is located
on the same host. Otherwise, TARGET is quoted."
(declare (indent 3) (debug t))
`(with-parsed-tramp-file-name (expand-file-name ,linkname) nil
;; If TARGET is a Tramp name, use just the localname component.
;; Don't check for a proper method.
(let ((non-essential t))
(when (and (tramp-tramp-file-p ,target)
(tramp-file-name-equal-p v (tramp-dissect-file-name ,target)))
(setq ,target (tramp-file-local-name (expand-file-name ,target))))
;; There could be a cyclic link.
(tramp-flush-file-properties
v (expand-file-name ,target (tramp-file-local-name default-directory))))
;; If TARGET is still remote, quote it.
(if (tramp-tramp-file-p ,target)
(make-symbolic-link
(file-name-quote ,target 'top) ,linkname ,ok-if-already-exists)
;; Do the 'confirm if exists' thing.
(when (file-exists-p ,linkname)
;; What to do?
(if (or (null ,ok-if-already-exists) ; not allowed to exist
(and (numberp ,ok-if-already-exists)
(not (yes-or-no-p
(format
"File %s already exists; make it a link anyway?"
localname)))))
(tramp-error v 'file-already-exists localname)
(delete-file ,linkname)))
;; We must also flush the cache of the directory, because
;; `file-attributes' reads the values from there.
(tramp-flush-file-properties v localname)
,@body)))
(defmacro tramp-skeleton-set-file-modes-times-uid-gid
(filename &rest body)
"Skeleton for `tramp-*-set-file-{modes,times,uid-gid}'.
@ -4091,13 +4163,8 @@ Let-bind it when necessary.")
(defun tramp-handle-file-truename (filename)
"Like `file-truename' for Tramp files."
;; Preserve trailing "/".
(funcall
(if (directory-name-p filename) #'file-name-as-directory #'identity)
;; Quote properly.
(funcall
(if (file-name-quoted-p filename) #'file-name-quote #'identity)
(let ((result (file-name-unquote (expand-file-name filename)))
(tramp-skeleton-file-truename filename
(let ((result (directory-file-name localname))
(numchase 0)
;; Don't make the following value larger than necessary.
;; People expect an error message in a timely fashion when
@ -4107,31 +4174,21 @@ Let-bind it when necessary.")
;; Unquoting could enable encryption.
tramp-crypt-enabled
symlink-target)
(with-parsed-tramp-file-name result v1
;; We cache only the localname.
(tramp-make-tramp-file-name
v1
(with-tramp-file-property v1 v1-localname "file-truename"
(while (and (setq symlink-target (file-symlink-p result))
(while (and (setq symlink-target
(file-symlink-p (tramp-make-tramp-file-name v result)))
(< numchase numchase-limit))
(setq numchase (1+ numchase)
result
(with-parsed-tramp-file-name (expand-file-name result) v2
(tramp-make-tramp-file-name
v2
(if (stringp symlink-target)
(if (file-remote-p symlink-target)
(file-name-quote symlink-target 'top)
(tramp-drop-volume-letter
(expand-file-name
symlink-target
(file-name-directory v2-localname))))
v2-localname))))
symlink-target (file-name-directory result)))))
(when (>= numchase numchase-limit)
(tramp-error
v1 'file-error
v 'file-error
"Maximum number (%d) of symlinks exceeded" numchase-limit)))
(tramp-file-local-name (directory-file-name result)))))))))
(directory-file-name result))))
(defun tramp-handle-file-writable-p (filename)
"Like `file-writable-p' for Tramp files."
@ -6346,6 +6403,7 @@ It always returns a return code. The Lisp error raised when
PROGRAM is nil is trapped also, returning 1. Furthermore, traces
are written with verbosity of 6."
(let ((default-directory tramp-compat-temporary-file-directory)
(temporary-file-directory tramp-compat-temporary-file-directory)
(process-environment (default-toplevel-value 'process-environment))
(destination (if (eq destination t) (current-buffer) destination))
(vec (or vec (car tramp-current-connection)))
@ -6378,6 +6436,7 @@ It always returns a return code. The Lisp error raised when
PROGRAM is nil is trapped also, returning 1. Furthermore, traces
are written with verbosity of 6."
(let ((default-directory tramp-compat-temporary-file-directory)
(temporary-file-directory tramp-compat-temporary-file-directory)
(process-environment (default-toplevel-value 'process-environment))
(buffer (if (eq buffer t) (current-buffer) buffer))
result)

View file

@ -165,6 +165,9 @@ A resource file is in the resource directory as per
;; Suppress nasty messages.
(fset #'shell-command-sentinel #'ignore)
;; We do not want to be interrupted.
(fset #'tramp-action-yesno
(lambda (_proc vec)
(tramp-send-string vec (concat "yes" tramp-local-end-of-line)) t))
(eval-after-load 'tramp-gvfs
'(fset 'tramp-gvfs-handler-askquestion
(lambda (_message _choices) '(t nil 0)))))
@ -4173,6 +4176,10 @@ This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'."
(should (file-symlink-p tmp-name1))
(should-not (file-regular-p tmp-name1))
(should-not (file-regular-p tmp-name2))
(should
(string-equal
(file-truename tmp-name1)
(file-truename tmp-name2)))
(if (tramp--test-smb-p)
;; The symlink command of "smbclient" detects the
;; cycle already.
@ -4180,10 +4187,15 @@ This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'."
(make-symbolic-link tmp-name1 tmp-name2)
:type 'file-error)
(make-symbolic-link tmp-name1 tmp-name2)
(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))))
;; Cleanup.
@ -4920,13 +4932,10 @@ This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'."
(while (accept-process-output proc 0 nil t))))
(should
(string-match-p
(if (and (memq process-connection-type '(nil pipe))
(not (tramp--test-macos-p)))
;; On macOS, there is always newline conversion.
;; "telnet" converts \r to <CR><NUL> if `crlf'
;; flag is FALSE. See telnet(1) man page.
(rx "66\n6F\n6F\n0D" (? "\n00") "\n0A\n")
(rx "66\n6F\n6F\n0A" (? "\n00") "\n0A\n"))
(rx "66\n" "6F\n" "6F\n" (| "0D\n" "0A\n") (? "00\n") "0A\n")
(buffer-string))))
;; Cleanup.
@ -5210,14 +5219,10 @@ If UNSTABLE is non-nil, the test is tagged as `:unstable'."
(while (accept-process-output proc 0 nil t))))
(should
(string-match-p
(if (and (memq (or connection-type process-connection-type)
'(nil pipe))
(not (tramp--test-macos-p)))
;; On macOS, there is always newline conversion.
;; "telnet" converts \r to <CR><NUL> if `crlf'
;; flag is FALSE. See telnet(1) man page.
(rx "66\n6F\n6F\n0D" (? "\n00") "\n0A\n")
(rx "66\n6F\n6F\n0A" (? "\n00") "\n0A\n"))
(rx "66\n" "6F\n" "6F\n" (| "0D\n" "0A\n") (? "00\n") "0A\n")
(buffer-string))))
;; Cleanup.
@ -7063,6 +7068,9 @@ This requires restrictions of file name syntax."
;; Use all available language specific snippets.
(lambda (x)
(and
;; The "Oriya" and "Odia" languages use some problematic
;; composition characters.
(not (member (car x) '("Oriya" "Odia")))
(stringp (setq x (eval (get-language-info (car x) 'sample-text) t)))
;; Filter out strings which use unencodable characters.
(not (and (or (tramp--test-gvfs-p) (tramp--test-smb-p))