branch: externals/tramp commit 2a040ff9c74729017d1e93cef26a94fe80ef5f02 Author: Michael Albinus <michael.albi...@gmx.de> Commit: Michael Albinus <michael.albi...@gmx.de>
Tramp ELPA version 2.8.0.1 released --- README | 6 +- test/tramp-tests.el | 97 ++++++++++++++++++++------ texi/tramp.texi | 12 +++- texi/trampver.texi | 2 +- tramp-adb.el | 11 ++- tramp-androidsu.el | 19 +++--- tramp-archive.el | 2 +- tramp-cmds.el | 67 +++++++++--------- tramp-gvfs.el | 8 +-- tramp-integration.el | 3 +- tramp-message.el | 2 +- tramp-sh.el | 60 +++++++--------- tramp-smb.el | 188 +++++++++++++++++++++++++++++++++++++++++---------- tramp-sudoedit.el | 18 ++--- tramp.el | 84 ++++++++++++++++------- trampver.el | 14 ++-- 16 files changed, 405 insertions(+), 188 deletions(-) diff --git a/README b/README index 18ff6e6a4d..25aca1bb1b 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.8.0/tramp*.elc + $ rm -f ~/.emacs.d/elpa/tramp-2.8.0.1/tramp*.elc • Start Emacs with Tramp's source files - $ emacs -L ~/.emacs.d/elpa/tramp-2.8.0 -l tramp + $ emacs -L ~/.emacs.d/elpa/tramp-2.8.0.1 -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.8.0 from GNU ELPA: +installation or upgrading Tramp 2.8.0.1 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 f67a33467d..d0ce17d249 100644 --- a/test/tramp-tests.el +++ b/test/tramp-tests.el @@ -45,8 +45,8 @@ ;; might be too heavy. Setting $REMOTE_PARALLEL_PROCESSES to a proper ;; value less than 10 could help. -;; This test suite obeys the environment variables $EMACS_HYDRA_CI and -;; $EMACS_EMBA_CI, used on the Emacs CI/CD platforms. +;; This test suite obeys the environment variable $EMACS_EMBA_CI, used +;; on the Emacs CI/CD platforms. ;; The following test tags are used: `:expensive-test', ;; `:tramp-asynchronous-processes' and `:unstable'. @@ -60,6 +60,7 @@ (require 'dired-aux) (require 'tramp) (require 'ert-x) +(require 'filenotify) (require 'tar-mode) (require 'trace) (require 'vc) @@ -121,11 +122,7 @@ (unless (and (null noninteractive) (file-directory-p "~/")) (setenv "HOME" temporary-file-directory)) (format "/mock::%s" temporary-file-directory))) - "Temporary directory for remote file tests.") - - ;; This should happen on hydra only. - (when (getenv "EMACS_HYDRA_CI") - (add-to-list 'tramp-remote-path 'tramp-own-remote-path)))) + "Temporary directory for remote file tests."))) ;; Beautify batch mode. (when noninteractive @@ -2436,8 +2433,7 @@ This checks also `file-name-as-directory', `file-name-directory', ;; Bug#10085. (when (tramp--test-enabled) ;; Packages like tramp-gvfs.el might be disabled. (dolist (non-essential '(nil t)) - ;; We must clear `tramp-default-method'. On hydra, it is "ftp", - ;; which ruins the tests. + ;; We must clear `tramp-default-method'. (let ((tramp-default-method (file-remote-p ert-remote-temporary-file-directory 'method)) (host-port @@ -3906,7 +3902,11 @@ This tests also `access-file', `file-readable-p', (when test-file-ownership-preserved-p (should (file-ownership-preserved-p tmp-name1 'group))) (setq attr (file-attributes tmp-name1)) - (should (eq (file-attribute-type attr) t))) + (should (eq (file-attribute-type attr) t)) + ;; A trailing slash shouldn't harm. + (should + (equal (file-attributes tmp-name1) + (file-attributes (file-name-as-directory tmp-name1))))) ;; Cleanup. (ignore-errors (delete-directory tmp-name1 'recursive)) @@ -4142,7 +4142,12 @@ They might differ only in time attributes or directory size." ;; Check the COUNT arg. (setq attr (directory-files-and-attributes tmp-name2 nil (rx bos "b") nil nil 1)) - (should (equal (mapcar #'car attr) '("bar")))) + (should (equal (mapcar #'car attr) '("bar"))) + + ;; A trailing slash shouldn't harm. + (should + (equal (file-attributes tmp-name2) + (file-attributes (file-name-as-directory tmp-name2))))) ;; Cleanup. (ignore-errors (delete-directory tmp-name1 'recursive)))))) @@ -5222,7 +5227,7 @@ This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'." (unwind-protect (progn ;; We cannot use "/bin/true" and "/bin/false"; those paths - ;; do not exist on hydra and on MS Windows. + ;; do not exist on MS Windows. (should (zerop (process-file "true"))) (should-not (zerop (process-file "false"))) (should-not (zerop (process-file "binary-does-not-exist"))) @@ -7665,7 +7670,6 @@ This requires restrictions of file name syntax." (ert-deftest tramp-test41-special-characters () "Check special characters in file names." (skip-unless (tramp--test-enabled)) - (skip-unless (not (getenv "EMACS_HYDRA_CI"))) ; SLOW ~ 245s (skip-unless (not (tramp--test-rsync-p))) (skip-unless (not (tramp--test-rclone-p))) (skip-unless (not (or (eq system-type 'darwin) (tramp--test-macos-p)))) @@ -7744,7 +7748,6 @@ This requires restrictions of file name syntax." (ert-deftest tramp-test42-utf8 () "Check UTF8 encoding in file names and file contents." (skip-unless (tramp--test-enabled)) - (skip-unless (not (getenv "EMACS_HYDRA_CI"))) ; SLOW ~ 620s (skip-unless (not (tramp--test-container-p))) (skip-unless (not (tramp--test-rsync-p))) (skip-unless (not (tramp--test-scp-p))) @@ -7902,17 +7905,12 @@ process sentinels. They shall not disturb each other." (cond ((ignore-errors (string-to-number (getenv "REMOTE_PARALLEL_PROCESSES")))) - ((getenv "EMACS_HYDRA_CI") 5) (t 10))) ;; PuTTY-based methods can only share up to 10 connections. (tramp-use-connection-share (if (and (tramp--test-putty-p) (>= number-proc 10)) 'suppress (bound-and-true-p tramp-use-connection-share))) - ;; On hydra, timings are bad. - (timer-repeat - (cond - ((getenv "EMACS_HYDRA_CI") 10) - (t 1))) + (timer-repeat 1) ;; This is when all timers start. We check inside the ;; timer function, that we don't exceed timeout. (timer-start (current-time)) @@ -8102,6 +8100,62 @@ process sentinels. They shall not disturb each other." (delete-directory tmp-name) (delete-file (concat tmp-name ".tar.gz")))) +;; More exhaustive tests are performed in filenotify-tests.el, +;; selector "remote". +(ert-deftest tramp-test46-file-notifications () + "Check that Tramp handles file notifications." + :tags '(:unstable) + (skip-unless (tramp--test-enabled)) + ;; filenotify.el was reworked in Emacs 31. + (skip-unless (tramp--test-emacs31-p)) + + (let* ((tmp-name (tramp--test-make-temp-name)) + ;(file-notify-debug t) + (desc1 + (ignore-error file-notify-error + (file-notify-add-watch + tmp-name '(change attribute-change) #'ignore))) + (desc2 + (ignore-error file-notify-error + (file-notify-add-watch + ert-remote-temporary-file-directory + '(change attribute-change) #'ignore)))) + (skip-unless (and desc1 desc2)) + + (unwind-protect + (progn + (tramp--test-message "%S" desc1) + (should-not (file-exists-p tmp-name)) + (should (file-notify-valid-p desc2)) + + ;; Create the file. `file-notify-valid-p' requires that the + ;; watched file exists, so we cannot check it earlier for `desc1'. + (write-region "foo" nil tmp-name) + (should (file-exists-p tmp-name)) + (should (file-notify-valid-p desc1)) + ;; Modify. + (write-region "foo" nil tmp-name) + (should (file-exists-p tmp-name)) + ;; Delete. + (delete-file tmp-name) + (should-not (file-exists-p tmp-name)) + + (while (read-event nil nil 0.1)) + ;; This has been stopped because the file was deleted. + (should-not (file-notify-valid-p desc1)) + + ;; This is still valid. + (should (file-notify-valid-p desc2)) + (file-notify-rm-watch desc2) + (should-not (file-notify-valid-p desc2))) + + ;; Cleanup. + (ignore-errors (delete-file tmp-name)) + ;; `file-notify-rm-all-watches' exists since Emacs 30.1. + ;; We don't want to see compiler warnings for older Emacsen. + (when (fboundp 'file-notify-rm-all-watches) + (with-no-warnings (file-notify-rm-all-watches)))))) + (ert-deftest tramp-test47-read-password () "Check Tramp password handling." :tags '(:expensive-test) @@ -8656,6 +8710,9 @@ If INTERACTIVE is non-nil, the tests are run interactively." ;; * Check, why direct async processes do not work for ;; `tramp-test45-asynchronous-requests'. +;; Starting with Emacs 29, use `ert-with-temp-file' and +;; `ert-with-temp-directory'. + (provide 'tramp-tests) ;;; tramp-tests.el ends here diff --git a/texi/tramp.texi b/texi/tramp.texi index 5f848733de..4170a35ad4 100644 --- a/texi/tramp.texi +++ b/texi/tramp.texi @@ -3949,7 +3949,7 @@ This is performed with @option{sudo} permissions. The method used in @code{tramp-revert-buffer-with-sudo} and @code{tramp-dired-find-file-with-sudo}. It defaults to @option{sudo}, other valid methods are @option{su}, @option{doas}, @option{run0}, and -@option{ksu}. +@option{ksu}. The value can be set connection-locally. If a command is called with a prefix argument @kbd{C-u}, the option's value is read interactively. @@ -5426,6 +5426,16 @@ connections, apply the following code. @end group @end lisp +@vindex auto-revert-remote-files +@item +Disable auto reverting of remote files. Set +@code{auto-revert-remote-files} to @code{nil}. This is the default. + +@vindex enable-remote-dir-locals +@item +Disable directory-local variables for remote files. Set +@code{enable-remote-dir-locals} to @code{nil}. This is the default. + @vindex dired-check-symlinks @item Disable check for symbolic link validity in @code{dired} buffers. diff --git a/texi/trampver.texi b/texi/trampver.texi index a6e305534a..5f2b2fad36 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.8.0 +@set trampver 2.8.0.1 @set trampurl https://www.gnu.org/software/tramp/ @set tramp-bug-report-address tramp-devel@@gnu.org @set emacsver 28.1 diff --git a/tramp-adb.el b/tramp-adb.el index 44808a80dc..2d240e8756 100644 --- a/tramp-adb.el +++ b/tramp-adb.el @@ -1117,17 +1117,14 @@ connection if a previous connection has died for some reason." (process-connection-type tramp-process-connection-type) (args (tramp-expand-args vec 'tramp-login-args nil ?d (or device ""))) - (p (let ((default-directory - tramp-compat-temporary-file-directory)) - (apply - #'start-process (tramp-get-connection-name vec) buf - tramp-adb-program args))) + (p (apply + #'tramp-start-process vec (tramp-get-connection-name vec) + buf tramp-adb-program args)) (prompt (md5 (concat (prin1-to-string process-environment) (current-time-string))))) - ;; Set sentinel. Initialize variables. + ;; Set sentinel. (set-process-sentinel p #'tramp-process-sentinel) - (tramp-post-process-creation p vec) ;; Wait for initial prompt. On some devices, it needs ;; an initial RET, in order to get it. diff --git a/tramp-androidsu.el b/tramp-androidsu.el index dcf487c3df..a593833a83 100644 --- a/tramp-androidsu.el +++ b/tramp-androidsu.el @@ -131,19 +131,18 @@ multibyte mode and waits for the shell prompt to appear." ;; The executable loader cannot execute setuid ;; binaries, such as su. (android-use-exec-loader nil) - (p (start-process (tramp-get-connection-name vec) - (tramp-get-connection-buffer vec) - ;; Disregard - ;; `tramp-encoding-shell', as - ;; there's no guarantee that it's - ;; possible to execute it with - ;; `android-use-exec-loader' off. - tramp-androidsu-local-shell-name "-i")) + (p (tramp-start-process + vec (tramp-get-connection-name vec) + (tramp-get-connection-buffer vec) + ;; Disregard `tramp-encoding-shell', as + ;; there's no guarantee that it's possible to + ;; execute it with `android-use-exec-loader' + ;; off. + tramp-androidsu-local-shell-name "-i")) (user (tramp-file-name-user vec)) su-binary path command) - ;; Set sentinel. Initialize variables. + ;; Set sentinel. (set-process-sentinel p #'tramp-process-sentinel) - (tramp-post-process-creation p vec) ;; Replace `login-args' place holders. `PATH' must be ;; set to `tramp-androidsu-remote-path', as some `su' ;; implementations propagate their callers' environments diff --git a/tramp-archive.el b/tramp-archive.el index 0a1c44d367..3de909911b 100644 --- a/tramp-archive.el +++ b/tramp-archive.el @@ -557,7 +557,7 @@ A variable `foo-archive' (or `archive') will be bound to the archive name part of FILENAME, assuming `foo' (or nil) is the value of VAR. OTOH, the variable `foo-hop' (or `hop') won't be offered." - (declare (debug (form symbolp body)) + (declare (debug (form symbolp &rest body)) (indent 2)) (let ((bindings (mapcar diff --git a/tramp-cmds.el b/tramp-cmds.el index 62dff3d8d4..6042209d4f 100644 --- a/tramp-cmds.el +++ b/tramp-cmds.el @@ -639,6 +639,10 @@ For details, see `tramp-rename-files'." (const "ksu")) :link '(tramp-info-link :tag "Tramp manual" tramp-file-name-with-method)) +(defun tramp-get-file-name-with-method () + "Return connection-local value of `tramp-file-name-with-method'." + (tramp-compat-connection-local-value tramp-file-name-with-method)) + (defmacro with-tramp-file-name-with-method (&rest body) "Ask user for `tramp-file-name-with-method' if needed. Run BODY." @@ -647,42 +651,43 @@ Run BODY." (if current-prefix-arg (completing-read "Tramp method: " - (mapcar #'cadr (cdr (get 'tramp-file-name-with-method 'custom-type))) - nil t tramp-file-name-with-method) - tramp-file-name-with-method))) + (mapcar + #'cadr (cdr (get 'tramp-file-name-with-method 'custom-type))) + nil t (tramp-get-file-name-with-method)) + (tramp-get-file-name-with-method)))) ,@body)) (defun tramp-file-name-with-sudo (filename) "Convert FILENAME into a multi-hop file name with \"sudo\". An alternative method could be chosen with `tramp-file-name-with-method'." (setq filename (expand-file-name filename)) - (if (tramp-tramp-file-p filename) - (with-parsed-tramp-file-name filename nil - (cond - ;; Remote file with proper method. - ((string-equal method tramp-file-name-with-method) - filename) - ;; Remote file on the local host. - ((and - (stringp tramp-local-host-regexp) (stringp host) - (string-match-p tramp-local-host-regexp host)) - (tramp-make-tramp-file-name - (make-tramp-file-name - :method tramp-file-name-with-method :localname localname))) - ;; Remote file with multi-hop capable method. - ((tramp-multi-hop-p v) - (tramp-make-tramp-file-name - (make-tramp-file-name - :method (tramp-find-method tramp-file-name-with-method nil host) - :user (tramp-find-user tramp-file-name-with-method nil host) - :host (tramp-find-host tramp-file-name-with-method nil host) - :localname localname :hop (tramp-make-tramp-hop-name v)))) - ;; Other remote file. - (t (tramp-user-error v "Multi-hop with `%s' not applicable" method)))) - ;; Local file. - (tramp-make-tramp-file-name - (make-tramp-file-name - :method tramp-file-name-with-method :localname filename)))) + (let ((default-method (tramp-get-file-name-with-method))) + (if (tramp-tramp-file-p filename) + (with-parsed-tramp-file-name filename nil + (cond + ;; Remote file with proper method. + ((string-equal method default-method) + filename) + ;; Remote file on the local host. + ((and + (stringp tramp-local-host-regexp) (stringp host) + (string-match-p tramp-local-host-regexp host)) + (tramp-make-tramp-file-name + (make-tramp-file-name + :method default-method :localname localname))) + ;; Remote file with multi-hop capable method. + ((tramp-multi-hop-p v) + (tramp-make-tramp-file-name + (make-tramp-file-name + :method (tramp-find-method default-method nil host) + :user (tramp-find-user default-method nil host) + :host (tramp-find-host default-method nil host) + :localname localname :hop (tramp-make-tramp-hop-name v)))) + ;; Other remote file. + (t (tramp-user-error v "Multi-hop with `%s' not applicable" method)))) + ;; Local file. + (tramp-make-tramp-file-name + (make-tramp-file-name :method default-method :localname filename))))) ;; FIXME: We would like to rename this for Emacs 31.1 to a name that ;; does not encode the default method. It is intended as a generic @@ -736,7 +741,7 @@ They are completed by `M-x TAB' only in Dired buffers." "Visit the file or directory named on this line as the superuser. By default this is done using the \"sudo\" Tramp method. -YOu can customize `tramp-file-name-with-method' to change this. +You can customize `tramp-file-name-with-method' to change this. Interactively, with a prefix argument, prompt for a different method." ;; (declare (completion tramp-dired-buffer-command-completion-p)) diff --git a/tramp-gvfs.el b/tramp-gvfs.el index 3dba7b1bad..b5f1135a60 100644 --- a/tramp-gvfs.el +++ b/tramp-gvfs.el @@ -1510,26 +1510,26 @@ If FILE-SYSTEM is non-nil, return file system attributes." '(created changed changes-done-hint moved deleted unmounted)) ((memq 'attribute-change flags) '(attribute-changed unmounted)))) (p (apply - #'start-process + #'tramp-start-process v "gvfs-monitor" (generate-new-buffer " *gvfs-monitor*") `("gio" "monitor" ,(tramp-gvfs-url-file-name file-name))))) (if (not (processp p)) (tramp-error v 'file-notify-error "Monitoring not supported for `%s'" file-name) + ;; Needed for process filter. (process-put p 'tramp-events events) (process-put p 'tramp-watch-name localname) (set-process-filter p #'tramp-gvfs-monitor-process-filter) (set-process-sentinel p #'tramp-file-notify-process-sentinel) - (tramp-post-process-creation p v) ;; There might be an error if the monitor is not supported. ;; Give the filter a chance to read the output. (while (tramp-accept-process-output p)) (unless (process-live-p p) (tramp-error p 'file-notify-error "Monitoring not supported for `%s'" file-name)) - ;; Set "gio-file-monitor" property. We believe, that "gio + ;; Set "file-monitor" property. We believe, that "gio ;; monitor" uses polling when applied for mounted files. - (tramp-set-connection-property p "gio-file-monitor" 'GPollFileMonitor) + (tramp-set-connection-property p "file-monitor" 'GPollFileMonitor) p)))) (defun tramp-gvfs-monitor-process-filter (proc string) diff --git a/tramp-integration.el b/tramp-integration.el index a28a88dd1e..7b34ce08bd 100644 --- a/tramp-integration.el +++ b/tramp-integration.el @@ -359,7 +359,8 @@ It's value must be a Tramp user option, indexed in the Tramp manual via (defconst tramp-connection-local-default-system-variables '((path-separator . ":") - (null-device . "/dev/null")) + (null-device . "/dev/null") + (exec-suffixes . (""))) "Default connection-local system variables for remote connections.") (connection-local-set-profile-variables diff --git a/tramp-message.el b/tramp-message.el index d87c62a4b5..5131e5fced 100644 --- a/tramp-message.el +++ b/tramp-message.el @@ -454,7 +454,7 @@ an input event arrives. The other arguments are passed to `tramp-error'." BODY is executed like wrapped by `with-demoted-errors'. FORMAT is a format-string containing a %-sequence meaning to substitute the resulting error message." - (declare (indent 2) (debug (symbolp form body))) + (declare (indent 2) (debug (symbolp form &rest body))) (let ((err (make-symbol "err"))) `(condition-case-unless-debug ,err (progn ,@body) diff --git a/tramp-sh.el b/tramp-sh.el index 0a6731c0fb..576f09b764 100644 --- a/tramp-sh.el +++ b/tramp-sh.el @@ -2618,14 +2618,11 @@ The method used must be an out-of-band method." ;; can be handled. We don't set a timeout, because ;; the copying of large files can last longer than 60 ;; secs. - p (let ((default-directory - tramp-compat-temporary-file-directory)) - (apply - #'start-process - (tramp-get-connection-name v) - (tramp-get-connection-buffer v) - copy-program copy-args))) - (tramp-post-process-creation p v) + p (apply + #'tramp-start-process v + (tramp-get-connection-name v) + (tramp-get-connection-buffer v) + copy-program copy-args)) ;; We must adapt `tramp-local-end-of-line' for sending ;; the password. Also, we indicate that perhaps @@ -3622,8 +3619,9 @@ will be used." (defun tramp-bundle-read-file-names (vec files) "Read file attributes of FILES and with one command fill the cache. -FILES must be the local names only. The cache attributes to be -filled are described in `tramp-bundle-read-file-names'." +FILES must be the local names only. The cache attributes to be filled +are \"file-exists-p\", \"file-readable-p\", \"file-directory-p\" and +\"file-executable-p\"." (when files (tramp-maybe-send-script vec tramp-bundle-read-file-names "tramp_bundle_read_file_names") @@ -3864,8 +3862,6 @@ Fall back to normal file name handler if no Tramp handler exists." (defun tramp-sh-gio-monitor-process-filter (proc string) "Read output from \"gio monitor\" and add corresponding `file-notify' events." (let ((events (process-get proc 'tramp-events)) - (remote-prefix - (file-remote-p (tramp-get-default-directory (process-buffer proc)))) (rest-string (process-get proc 'tramp-rest-string)) pos) (when rest-string @@ -3880,14 +3876,17 @@ Fall back to normal file name handler if no Tramp handler exists." (catch 'doesnt-work ;; https://bugs.launchpad.net/bugs/1742946 (when (string-match-p - (rx (| "Monitoring not supported" "No locations given")) string) + (rx (| "Monitoring not supported" + "No locations given" + "Unable to find default local file monitor type")) + string) (delete-process proc) (throw 'doesnt-work nil)) ;; Determine monitor name. - (unless (tramp-connection-property-p proc "gio-file-monitor") + (unless (tramp-connection-property-p proc "file-monitor") (tramp-set-connection-property - proc "gio-file-monitor" + proc "file-monitor" (cond ;; We have seen this on cygwin gio and on emba. Let's make ;; some assumptions. @@ -3917,7 +3916,7 @@ Fall back to normal file name handler if no Tramp handler exists." bol (+ (not ":")) ":" blank (group (+ (not ":"))) ":" blank (group (regexp (regexp-opt tramp-gio-events))) - (? blank (group (+ (not ":")))) eol) + (? blank (group (+ (not (any "\r\n:"))))) eol) string) (let* ((file (match-string 1 string)) @@ -3927,10 +3926,7 @@ Fall back to normal file name handler if no Tramp handler exists." proc (list (intern-soft (match-string 2 string))) - ;; File names are returned as absolute paths. We - ;; must add the remote prefix. - (concat remote-prefix file) - (when file1 (concat remote-prefix file1))))) + file file1))) (setq string (replace-match "" nil nil string)) ;; Add an Emacs event now. ;; `insert-special-event' exists since Emacs 31. @@ -5219,7 +5215,6 @@ connection if a previous connection has died for some reason." ;; Start new process. (when (and p (processp p)) (delete-process p)) - (setenv "TERM" tramp-terminal-type) (setenv "LC_ALL" (tramp-get-local-locale vec)) (if (stringp tramp-histfile-override) (setenv "HISTFILE" tramp-histfile-override) @@ -5228,8 +5223,6 @@ connection if a previous connection has died for some reason." (setenv "HISTFILE") (setenv "HISTFILESIZE" "0") (setenv "HISTSIZE" "0")))) - (setenv "PROMPT_COMMAND") - (setenv "PS1" tramp-initial-end-of-output) (unless (stringp tramp-encoding-shell) (tramp-error vec 'file-error "`tramp-encoding-shell' not set")) (let* ((current-host tramp-system-name) @@ -5247,21 +5240,18 @@ connection if a previous connection has died for some reason." (extra-args (tramp-get-sh-extra-args tramp-encoding-shell)) ;; This must be done in order to avoid our file ;; name handler. - (p (let ((default-directory - tramp-compat-temporary-file-directory)) - (apply - #'start-process - (tramp-get-connection-name vec) - (tramp-get-connection-buffer vec) - (append - `(,tramp-encoding-shell) - (and extra-args (split-string extra-args)) - (and tramp-encoding-command-interactive - `(,tramp-encoding-command-interactive))))))) + (p (apply + #'tramp-start-process vec + (tramp-get-connection-name vec) + (tramp-get-connection-buffer vec) + (append + `(,tramp-encoding-shell) + (and extra-args (split-string extra-args)) + (and tramp-encoding-command-interactive + `(,tramp-encoding-command-interactive)))))) ;; Set sentinel. Initialize variables. (set-process-sentinel p #'tramp-process-sentinel) - (tramp-post-process-creation p vec) (setq tramp-current-connection (cons vec (current-time))) ;; Set connection-local variables. diff --git a/tramp-smb.el b/tramp-smb.el index db961c9752..28364de83f 100644 --- a/tramp-smb.el +++ b/tramp-smb.el @@ -127,10 +127,10 @@ this variable \"client min protocol=NT1\"." "ERRnomem" "ERRnosuchshare" ;; See /usr/include/samba-4.0/core/ntstatus.h. - ;; Windows 4.0 (Windows NT), Windows 5.0 (Windows 2000), - ;; Windows 5.1 (Windows XP), Windows 5.2 (Windows Server 2003), - ;; Windows 6.0 (Windows Vista), Windows 6.1 (Windows 7), - ;; Windows 6.3 (Windows Server 2012, Windows 10). + ;; <https://learn.microsoft.com/en-us/windows/win32/sysinfo/operating-system-version> + ;; Tested with Windows NT, Windows 2000, Windows XP, Windows + ;; Server 2003, Windows Vista, Windows 7, Windows Server 2012, + ;; Windows 10, Windows 11. "NT_STATUS_ACCESS_DENIED" "NT_STATUS_ACCOUNT_LOCKED_OUT" "NT_STATUS_BAD_NETWORK_NAME" @@ -261,7 +261,7 @@ See `tramp-actions-before-shell' for more info.") (file-name-nondirectory . tramp-handle-file-name-nondirectory) ;; `file-name-sans-versions' performed by default handler. (file-newer-than-file-p . tramp-handle-file-newer-than-file-p) - (file-notify-add-watch . tramp-handle-file-notify-add-watch) + (file-notify-add-watch . tramp-smb-handle-file-notify-add-watch) (file-notify-rm-watch . tramp-handle-file-notify-rm-watch) (file-notify-valid-p . tramp-handle-file-notify-valid-p) (file-ownership-preserved-p . ignore) @@ -552,13 +552,11 @@ arguments to pass to the OPERATION." ;; Use an asynchronous processes. By this, ;; password can be handled. - (let* ((default-directory tmpdir) - (p (apply - #'start-process - (tramp-get-connection-name v) - (tramp-get-connection-buffer v) - tramp-smb-program args))) - (tramp-post-process-creation p v) + (let ((p (apply + #'tramp-start-process v + (tramp-get-connection-name v) + (tramp-get-connection-buffer v) + tramp-smb-program args))) (tramp-process-actions p v nil tramp-smb-actions-with-tar) @@ -686,8 +684,10 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored." (tramp-error v 'file-error "%s `%s'" (match-string 0) directory))) ;; "rmdir" does not report an error. So we check ourselves. - (when (file-exists-p directory) - (tramp-error v 'file-error "`%s' not removed" directory))))) + ;; Deletion of a watched directory could be pending. + (when (and (not (tramp-directory-watched directory)) + (file-exists-p directory)) + (tramp-error v 'file-error "`%s' not removed" directory))))) (defun tramp-smb-handle-delete-file (filename &optional trash) "Like `delete-file' for Tramp files." @@ -811,11 +811,10 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored." ;; Use an asynchronous process. By this, password ;; can be handled. (let ((p (apply - #'start-process + #'tramp-start-process v (tramp-get-connection-name v) (tramp-get-connection-buffer v) tramp-smb-acl-program args))) - (tramp-post-process-creation p v) (tramp-process-actions p v nil tramp-smb-actions-get-acl) (when (> (point-max) (point-min)) (substring-no-properties (buffer-string)))))))))))) @@ -823,6 +822,7 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored." (defun tramp-smb-handle-file-attributes (filename &optional id-format) "Like `file-attributes' for Tramp files." ;; The result is cached in `tramp-convert-file-attributes'. + (setq filename (directory-file-name (expand-file-name filename))) (with-parsed-tramp-file-name filename nil (tramp-convert-file-attributes v localname id-format (ignore-errors @@ -964,6 +964,107 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored." (tramp-error v 'file-error "Cannot make local copy of file `%s'" filename))))) +;; The "notify" command has been added to smbclient 4.3.0. +(defun tramp-smb-handle-file-notify-add-watch (file-name flags _callback) + "Like `file-notify-add-watch' for Tramp files." + (setq file-name (expand-file-name file-name)) + (with-parsed-tramp-file-name file-name nil + (let ((default-directory (file-name-directory file-name)) + (command (format "notify %s" (tramp-smb-shell-quote-localname v))) + (events + (cond + ((memq 'change flags) + '(added removed modified renamed-from renamed-to)) + ((memq 'attribute-change flags) '(modified)))) + p) + ;; Start process. + (with-tramp-saved-connection-properties + v '(" process-name" " process-buffer") + ;; Set the new process properties. + (tramp-set-connection-property + v " process-name" (tramp-get-unique-process-name "smb-notify")) + (tramp-set-connection-property + v " process-buffer" (generate-new-buffer " *smb-notify*")) + (tramp-flush-connection-property v " process-exit-status") + (tramp-smb-send-command v command 'nooutput) + (setq p (tramp-get-connection-process v)) + ;; Return the process object as watch-descriptor. + (if (not (processp p)) + (tramp-error + v 'file-notify-error + "`%s' failed to start on remote host" command) + ;; Needed for process filter. + (process-put p 'tramp-events events) + (process-put p 'tramp-watch-name localname) + (set-process-filter p #'tramp-smb-notify-process-filter) + (set-process-sentinel p #'tramp-file-notify-process-sentinel) + ;; There might be an error if the monitor is not supported. + ;; Give the filter a chance to read the output. + (while (tramp-accept-process-output p)) + (unless (process-live-p p) + (tramp-error + p 'file-notify-error "Monitoring not supported for `%s'" file-name)) + ;; Set "file-monitor" property. The existence of the "ADMIN$" + ;; share is an indication for a remote MS Windows host. + (tramp-set-connection-property + p "file-monitor" + (if (member + "ADMIN$" (directory-files (tramp-make-tramp-file-name v "/"))) + 'SMBWindows 'SMBSamba)) + p))))) + +;; FileChangeNotify subsystem was added to Smaba 4.3.0. +;; <https://www.samba.org/samba/history/samba-4.3.0.html> +(defun tramp-smb-notify-process-filter (proc string) + "Read output from \"notify\" and add corresponding `file-notify' events." + (let ((events (process-get proc 'tramp-events))) + (tramp-message proc 6 "%S\n%s" proc string) + (dolist (line (split-string string (rx (+ (any "\r\n"))) 'omit)) + (catch 'next + ;; Watched directory is removed. + (when (string-match-p "NT_STATUS_DELETE_PENDING" line) + (setq line (concat "0002 " (process-get proc 'tramp-watch-name)))) + ;; Stopped. + (when (string-match-p tramp-smb-prompt line) + (throw 'next 'next)) + + ;; Check, whether there is a problem. + (unless (string-match + (rx bol (group (+ digit)) + (+ blank) (group (+ (not (any "\r\n"))))) + line) + (tramp-error proc 'file-notify-error line)) + + ;; See libsmbclient.h. + ;; #define SMBC_NOTIFY_ACTION_ADDED 1 + ;; #define SMBC_NOTIFY_ACTION_REMOVED 2 + ;; #define SMBC_NOTIFY_ACTION_MODIFIED 3 + ;; #define SMBC_NOTIFY_ACTION_OLD_NAME 4 + ;; #define SMBC_NOTIFY_ACTION_NEW_NAME 5 + ;; #define SMBC_NOTIFY_ACTION_ADDED_STREAM 6 + ;; #define SMBC_NOTIFY_ACTION_REMOVED_STREAM 7 + ;; #define SMBC_NOTIFY_ACTION_MODIFIED_STREAM 8 + (let ((object + (list + proc + (pcase (string-to-number (match-string 1 line)) + (1 '(added)) + (2 '(removed)) + (3 '(modified)) + (4 '(renamed-from)) + (5 '(renamed-to)) + ;; Ignore stream events. + (_ (throw 'next 'next))) + (string-replace "\\" "/" (match-string 2 line))))) + ;; Add an Emacs event now. + ;; `insert-special-event' exists since Emacs 31. + (when (member (caadr object) events) + (tramp-compat-funcall + (if (fboundp 'insert-special-event) + 'insert-special-event + (lookup-key special-event-map [file-notify])) + `(file-notify ,object file-notify-callback)))))))) + ;; This function should return "foo/" for directories and "bar" for ;; files. (defun tramp-smb-handle-file-name-all-completions (filename directory) @@ -1401,11 +1502,10 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored." ;; Use an asynchronous process. By this, password ;; can be handled. (let ((p (apply - #'start-process + #'tramp-start-process v (tramp-get-connection-name v) (tramp-get-connection-buffer v) tramp-smb-acl-program args))) - (tramp-post-process-creation p v) (tramp-process-actions p v nil tramp-smb-actions-set-acl) ;; This is meant for traces, and returning from ;; the function. No error is propagated outside, @@ -1823,13 +1923,14 @@ are listed. Result is the list (LOCALNAME MODE SIZE MTIME)." ;; Connection functions. -(defun tramp-smb-send-command (vec command) +(defun tramp-smb-send-command (vec command &optional nooutput) "Send the COMMAND to connection VEC. -Returns nil if there has been an error message from smbclient." +Returns nil if there has been an error message from smbclient. The +function waits for output unless NOOUTPUT is set." (tramp-smb-maybe-open-connection vec) (tramp-message vec 6 "%s" command) (tramp-send-string vec command) - (tramp-smb-wait-for-output vec)) + (unless nooutput (tramp-smb-wait-for-output vec))) (defun tramp-smb-maybe-open-connection (vec &optional argument) "Maybe open a connection to HOST, log in as USER, using `tramp-smb-program'. @@ -1937,18 +2038,12 @@ If ARGUMENT is non-nil, use it as argument for (let* (coding-system-for-read (process-connection-type tramp-process-connection-type) - (p (let ((default-directory - tramp-compat-temporary-file-directory) - (process-environment - (cons (concat "TERM=" tramp-terminal-type) - process-environment))) - (apply #'start-process - (tramp-get-connection-name vec) - (tramp-get-connection-buffer vec) - (if argument - tramp-smb-winexe-program tramp-smb-program) - args)))) - (tramp-post-process-creation p vec) + (p (apply #'tramp-start-process vec + (tramp-get-connection-name vec) + (tramp-get-connection-buffer vec) + (if argument + tramp-smb-winexe-program tramp-smb-program) + args))) ;; Set connection-local variables. (tramp-set-connection-local-variables vec) @@ -2003,7 +2098,7 @@ Removes smb prompt. Returns nil if an error message has appeared." (while (not (search-forward-regexp tramp-smb-prompt nil t)) (while (tramp-accept-process-output p)) (goto-char (point-min))) - (tramp-message vec 6 "\n%s" (buffer-string)) + (tramp-message vec 6 "%S\n%s" p (buffer-string)) ;; Remove prompt. (goto-char (point-min)) @@ -2067,6 +2162,29 @@ Removes smb prompt. Returns nil if an error message has appeared." "Call `tramp-smb-shell-quote-argument' on localname of VEC." (tramp-smb-shell-quote-argument (tramp-smb-get-localname vec))) +;;; Default connection-local variables for Tramp. + +(defconst tramp-smb-connection-local-default-system-variables + '((path-separator . ";") + (null-device . "NUL") + ;; This the default value of %PATHEXT% in MS Windows 11, plus ".py" + ;; for Python. Once we have remote processes, we might set this + ;; host-specific using that remote environment variable. + ;; The suffix "" is added for the benefit of local processes, + ;; started in a remote buffer. (Bug#78886) + (exec-suffixes + . (".com" ".exe" ".bat" ".cmd" ".vbs" ".vbe" + ".js" ".jse" ".wsf" ".wsh" ".msc" ".py" ""))) + "Default connection-local system variables for remote smb connections.") + +(connection-local-set-profile-variables + 'tramp-smb-connection-local-default-system-profile + tramp-smb-connection-local-default-system-variables) + +(connection-local-set-profiles + `(:application tramp :protocol ,tramp-smb-method) + 'tramp-smb-connection-local-default-system-profile) + (add-hook 'tramp-unload-hook (lambda () (unload-feature 'tramp-smb 'force))) @@ -2084,4 +2202,6 @@ Removes smb prompt. Returns nil if an error message has appeared." ;; ;; * Keep a permanent connection process for `process-file'. +;; * Implement "scopy" (since Samba 4.3.0). + ;;; tramp-smb.el ends here diff --git a/tramp-sudoedit.el b/tramp-sudoedit.el index 4cf1ef8c22..279b7e610b 100644 --- a/tramp-sudoedit.el +++ b/tramp-sudoedit.el @@ -769,14 +769,15 @@ in case of error, t otherwise." (erase-buffer) (let* ((delete-exited-processes t) (process-connection-type tramp-process-connection-type) - (p (apply #'start-process - (tramp-get-connection-name vec) (current-buffer) - (append - (tramp-expand-args - vec 'tramp-sudo-login nil - ?h (or (tramp-file-name-host vec) "") - ?u (or (tramp-file-name-user vec) "")) - (flatten-tree args)))) + (p (apply + #'tramp-start-process vec + (tramp-get-connection-name vec) (current-buffer) + (append + (tramp-expand-args + vec 'tramp-sudo-login nil + ?h (or (tramp-file-name-host vec) "") + ?u (or (tramp-file-name-user vec) "")) + (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". @@ -786,7 +787,6 @@ in case of error, t otherwise." auth-source-save-behavior) ;; Avoid process status message in output buffer. (set-process-sentinel p #'ignore) - (tramp-post-process-creation p vec) (tramp-set-connection-property p "pw-vector" tramp-sudoedit-null-hop) (tramp-process-actions p vec nil tramp-sudoedit-sudo-actions) (tramp-message vec 6 "%s\n%s" (process-exit-status p) (buffer-string)) diff --git a/tramp.el b/tramp.el index 05aba28480..b58acea229 100644 --- a/tramp.el +++ b/tramp.el @@ -7,7 +7,7 @@ ;; Maintainer: Michael Albinus <michael.albi...@gmx.de> ;; Keywords: comm, processes ;; Package: tramp -;; Version: 2.8.0 +;; Version: 2.8.0.1 ;; Package-Requires: ((emacs "28.1")) ;; Package-Type: multi ;; URL: https://www.gnu.org/software/tramp/ @@ -2102,7 +2102,7 @@ of `current-buffer'." "Execute BODY and return the result. In case of an error, raise a `file-missing' error if FILENAME does not exist, otherwise propagate the error." - (declare (indent 2) (debug (symbolp form body))) + (declare (indent 2) (debug (tramp-file-name-p form &rest body))) (let ((err (make-symbol "err"))) `(condition-case ,err (let (signal-hook-function) ,@body) @@ -2141,7 +2141,7 @@ Remaining args are Lisp expressions to be evaluated (inside an implicit If VAR is nil, then we bind `v' to the structure and `method', `user', `domain', `host', `port', `localname', `hop' to the components." - (declare (indent 2) (debug (form symbolp body))) + (declare (indent 2) (debug (form symbolp &rest body))) (let ((bindings (mapcar (lambda (elem) @@ -3601,7 +3601,7 @@ BODY is the backend specific code." (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))) + (declare (indent 1) (debug (form &rest body))) ;; Preserve trailing "/". `(funcall (if (directory-name-p ,filename) #'file-name-as-directory #'identity) @@ -5743,6 +5743,16 @@ of." (tramp-message proc 5 "Sentinel called: `%S' `%s'" proc event) (file-notify-rm-watch proc))) +(defun tramp-directory-watched (directory) + "Check, whether a directory is watched." + (let (result) + (dolist (p (process-list) result) + (setq result + (or result + (and-let* ((dir (process-get p 'tramp-watch-name)) + ((string-equal + dir (tramp-file-local-name directory)))))))))) + ;;; Functions for establishing connection: ;; The following functions are actions to be taken when seeing certain @@ -6120,7 +6130,7 @@ If the user quits via `C-g', it is propagated up to `tramp-file-name-handler'." (if (with-local-quit (setq result (accept-process-output proc 0 nil t)) t) (tramp-message - proc 10 "%s %s %s\n%s" + proc 10 "%S %S %s\n%s" proc (process-status proc) result (buffer-string)) ;; Propagate quit. (keyboard-quit))) @@ -6197,7 +6207,7 @@ nil." ;; timeout of sudo. The process buffer does not exist any longer then. (ignore-errors (tramp-message - proc 6 "\n%s" (tramp-get-buffer-string (process-buffer proc)))) + proc 6 "%S\n%s" proc (tramp-get-buffer-string (process-buffer proc)))) (unless found (if timeout (tramp-error @@ -6857,16 +6867,43 @@ ALIST is of the form ((FROM . TO) ...)." ;;; Compatibility functions section: +(defmacro with-tramp-local-environment (&rest body) + "Set environment for local processes and run BODY. +Beside some global variables, it let-binds also the connection-local +variables defined in `tramp-connection-local-default-system-variables'. +If the current buffer is a remote one, these connection-local variables +might have improper values." + (declare (debug t)) + (let ((bindings + (mapcar + (lambda (elem) `(,elem (default-value ',elem))) + (mapcar #'car tramp-connection-local-default-system-variables)))) + `(let* ((default-directory tramp-compat-temporary-file-directory) + (temporary-file-directory tramp-compat-temporary-file-directory) + ,@bindings) + (setenv "TERM" tramp-terminal-type) + (setenv "PROMPT_COMMAND") + (setenv "PS1" tramp-initial-end-of-output) + (setenv "INSIDE_EMACS" (tramp-inside-emacs)) + ,@body))) + +(defun tramp-start-process (vec name buffer program &rest args) + "Call `start-process' on the local host. +Run post process creation actions. Traces are written with verbosity of 6." + (let ((vec (or vec (car tramp-current-connection))) + (p (with-tramp-local-environment + (apply #'start-process name buffer program args)))) + ;; Initialize variables. + (tramp-post-process-creation p vec) + p)) + (defun tramp-call-process (vec program &optional infile destination display &rest args) "Call `call-process' on the local host. 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)) + (let ((destination (if (eq destination t) (current-buffer) destination)) (vec (or vec (car tramp-current-connection))) output error result) (tramp-message @@ -6875,8 +6912,9 @@ are written with verbosity of 6." (condition-case err (with-temp-buffer (setq result - (apply - #'call-process program infile (or destination t) display args) + (with-tramp-local-environment + (apply + #'call-process program infile (or destination t) display args)) output (tramp-get-buffer-string destination)) ;; `result' could also be an error string. (when (stringp result) @@ -6896,10 +6934,7 @@ are written with verbosity of 6." 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)) + (let ((buffer (if (eq buffer t) (current-buffer) buffer)) (vec (or vec (car tramp-current-connection))) result) (tramp-message @@ -6908,9 +6943,10 @@ are written with verbosity of 6." (condition-case err (progn (setq result - (apply - #'call-process-region - start end program delete buffer display args)) + (with-tramp-local-environment + (apply + #'call-process-region + start end program delete buffer display args))) ;; `result' could also be an error string. (when (stringp result) (signal 'file-error (list result))) @@ -6923,21 +6959,19 @@ are written with verbosity of 6." (tramp-message vec 6 "%d\n%s" result (error-message-string err)))) result)) -(defun tramp-process-lines - (vec program &rest args) +(defun tramp-process-lines (vec program &rest args) "Call `process-lines' on the local host. If an error occurs, it returns nil. Traces are written with verbosity of 6." - (let ((default-directory tramp-compat-temporary-file-directory) - (process-environment (default-toplevel-value 'process-environment)) - (vec (or vec (car tramp-current-connection))) + (let ((vec (or vec (car tramp-current-connection))) result) (if args (tramp-message vec 6 "%s %s" program (string-join args " ")) (tramp-message vec 6 "%s" program)) (setq result (condition-case err - (apply #'process-lines program args) + (with-tramp-local-environment + (apply #'process-lines program args)) (error (tramp-error vec (car err) (cdr err))))) (tramp-message vec 6 "\n%s" (string-join result "\n")) diff --git a/trampver.el b/trampver.el index 65e60c0464..9ee964613c 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.8.0 +;; Version: 2.8.0.1 ;; Package-Requires: ((emacs "28.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.8.0" +(defconst tramp-version "2.8.0.1" "This version of Tramp.") ;;;###tramp-autoload @@ -76,14 +76,18 @@ ;; Check for Emacs version. (let ((x (if (not (string-version-lessp emacs-version "28.1")) "ok" - (format "Tramp 2.8.0 is not fit for %s" + (format "Tramp 2.8.0.1 is not fit for %s" (replace-regexp-in-string "\n" "" (emacs-version)))))) (unless (string-equal "ok" x) (error "%s" x))) (defun tramp-inside-emacs () "Version string provided by INSIDE_EMACS environment variable." - (concat (or (getenv "INSIDE_EMACS") emacs-version) - ",tramp:" tramp-version)) + (let ((version-string (concat ",tramp:" tramp-version))) + (concat + ;; Remove duplicate entries. + (string-replace + version-string "" (or (getenv "INSIDE_EMACS") emacs-version)) + version-string))) ;; Tramp versions integrated into Emacs. If a user option declares a ;; `:package-version' which doesn't belong to an integrated Tramp