emacs-diffs
[Top][All Lists]
Advanced

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

master bfb7c58ac5c: Optimizations on Tramp symlink handling


From: Michael Albinus
Subject: master bfb7c58ac5c: Optimizations on Tramp symlink handling
Date: Mon, 31 Jul 2023 13:40:24 -0400 (EDT)

branch: master
commit bfb7c58ac5c322d6363e83f68afa88fc88ba2a0f
Author: Michael Albinus <michael.albinus@gmx.de>
Commit: Michael Albinus <michael.albinus@gmx.de>

    Optimizations on Tramp symlink handling
    
    * lisp/net/tramp-sh.el (cl-seq): Require.
    (tramp-perl-file-truename): Print also whether the file is a symlink.
    (tramp-bundle-read-file-names): Rename from
    `tramp-vc-registered-read-file-names'.  Print also the
    `file-directory-p: value.
    (tramp-sh-handle-make-symbolic-link): Combine two commands.  Use
    `tramp-skeleton-make-symbolic-link'.
    (tramp-sh-handle-file-truename): Read also "file-symlink-marker"
    property.
    (tramp-sh-handle-file-directory-p): Simplify if-let clause.
    (tramp-sh-handle-file-name-all-completions): Simplify command.
    (tramp-bundle-read-file-names): New defun.
    (tramp-sh-handle-vc-registered, tramp-get-remote-path): Use it.
    (tramp-open-shell): Flush "scripts" connection property.
    (tramp-open-connection-setup-interactive-shell): Combine two commands.
    
    * lisp/net/tramp-smb.el (tramp-smb-handle-make-symbolic-link):
    * lisp/net/tramp-sudoedit.el (tramp-sudoedit-handle-make-symbolic-link):
    Use `tramp-skeleton-make-symbolic-link'.
    
    * lisp/net/tramp.el (tramp-skeleton-make-symbolic-link): Rename from
    `tramp-skeleton-handle-make-symbolic-link'.
    (tramp-handle-file-symlink-p): Check file property
    "file-symlink-marker".
---
 lisp/net/tramp-adb.el      |   4 +-
 lisp/net/tramp-sh.el       | 193 +++++++++++++++++++++++++--------------------
 lisp/net/tramp-smb.el      |   2 +-
 lisp/net/tramp-sudoedit.el |   6 +-
 lisp/net/tramp.el          |  14 +++-
 5 files changed, 125 insertions(+), 94 deletions(-)

diff --git a/lisp/net/tramp-adb.el b/lisp/net/tramp-adb.el
index 3d4dacb393c..eec00b17b4c 100644
--- a/lisp/net/tramp-adb.el
+++ b/lisp/net/tramp-adb.el
@@ -511,9 +511,9 @@ Emacs dired can't find files."
   (with-parsed-tramp-file-name (expand-file-name filename) nil
     (with-tramp-file-property v localname "file-writable-p"
       (if (file-exists-p filename)
+         ;; Examine `file-attributes' cache to see if request can be
+         ;; satisfied without remote operation.
          (if (tramp-file-property-p v localname "file-attributes")
-             ;; Examine `file-attributes' cache to see if request can
-             ;; be satisfied without remote operation.
              (tramp-check-cached-permissions v ?w)
            (tramp-adb-send-command-and-check
             v (format "test -w %s" (tramp-shell-quote-argument localname))))
diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el
index e889cb2e86f..b33e788b893 100644
--- a/lisp/net/tramp-sh.el
+++ b/lisp/net/tramp-sh.el
@@ -32,6 +32,7 @@
 ;;; Code:
 
 (eval-when-compile (require 'cl-lib))
+(require 'cl-seq)
 (require 'tramp)
 
 ;; `dired-*' declarations can be removed, starting with Emacs 29.1.
@@ -616,6 +617,13 @@ if (!$result) {
     $result = File::Spec->catpath($vol, File::Spec->catdir(@dirs), \"\");
 }
 
+if (-l $ARGV[0]) {
+    print \"t\\n\";
+    }
+else {
+    print \"nil\\n\";
+    }
+
 $result =~ s/\"/\\\\\"/g;
 print \"\\\"$result\\\"\\n\";
 ' \"$1\" %n"
@@ -699,11 +707,11 @@ characters need to be doubled.")
     " '((%s%%%%N%s) %%%%h (%s%%%%U%s . %%%%u) (%s%%%%G%s . %%%%g)"
     " %%%%X %%%%Y %%%%Z %%%%s %s%%%%A%s t %%%%i -1)' \"$1\" %%n || echo nil) |"
     " sed -e 's/\"/\\\\\"/g' -e 's/%s/\"/g'")
-    tramp-stat-marker tramp-stat-marker ; %%N
-    tramp-stat-marker tramp-stat-marker ; %%U
-    tramp-stat-marker tramp-stat-marker ; %%G
-    tramp-stat-marker tramp-stat-marker ; %%A
-    tramp-stat-quoted-marker)
+   tramp-stat-marker tramp-stat-marker ; %%N
+   tramp-stat-marker tramp-stat-marker ; %%U
+   tramp-stat-marker tramp-stat-marker ; %%G
+   tramp-stat-marker tramp-stat-marker ; %%A
+   tramp-stat-quoted-marker)
   "Shell function to produce output suitable for use with `file-attributes'
 on the remote file system.
 Format specifiers are replaced by `tramp-expand-script', percent
@@ -1015,7 +1023,7 @@ BEGIN {
 Format specifiers are replaced by `tramp-expand-script', percent
 characters need to be doubled.")
 
-(defconst tramp-vc-registered-read-file-names
+(defconst tramp-bundle-read-file-names
   "echo \"(\"
 while read file; do
     quoted=`echo \"$file\" | sed -e \"s/\\\"/\\\\\\\\\\\\\\\\\\\"/\"`
@@ -1029,13 +1037,18 @@ while read file; do
     else
        echo \"(\\\"$quoted\\\" \\\"file-readable-p\\\" nil)\"
     fi
+    if %s \"$file\"; then
+       echo \"(\\\"$quoted\\\" \\\"file-directory-p\\\" t)\"
+    else
+       echo \"(\\\"$quoted\\\" \\\"file-directory-p\\\" nil)\"
+    fi
 done
 echo \")\""
-  "Script to check existence of VC related files.
-It must be send formatted with two strings; the tests for file
-existence, and file readability.  Input shall be read via
-here-document, otherwise the command could exceed maximum length
-of command line.
+  "Script to check file attributes of a bundle of files.
+It must be sent formatted with three strings; the tests for file
+existence, file readability, and file directory.  Input shall be
+read via here-document, otherwise the command could exceed
+maximum length of command line.
 Format specifiers \"%s\" are replaced before the script is used.")
 
 ;; New handlers should be added here.
@@ -1145,19 +1158,17 @@ Operations not mentioned here will be handled by the 
normal Emacs functions.")
        (concat "Making a symbolic link: "
               "ln(1) does not exist on the remote host"))))
 
-  (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 (file-name-directory localname))))
-         (tramp-send-command-and-check
-         v (format
-            "%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.
-            (tramp-shell-quote-argument
-              (concat "./" (file-name-nondirectory localname))))))))
+  (tramp-skeleton-make-symbolic-link target linkname ok-if-already-exists
+    (tramp-send-command-and-check
+     v (format
+       "cd %s && %s -sf %s %s"
+       (tramp-shell-quote-argument (file-name-directory localname))
+       (tramp-get-remote-ln v)
+       (tramp-shell-quote-argument target)
+       ;; The command could exceed PATH_MAX, so we use relative
+       ;; file names.
+       (tramp-shell-quote-argument
+         (concat "./" (file-name-nondirectory localname)))))))
 
 (defun tramp-sh-handle-file-truename (filename)
   "Like `file-truename' for Tramp files."
@@ -1166,12 +1177,20 @@ Operations not mentioned here will be handled by the 
normal Emacs functions.")
      ;; Use GNU readlink --canonicalize-missing where available.
      ((tramp-get-remote-readlink v)
       (tramp-send-command-and-check
-       v (format "%s --canonicalize-missing %s"
-                (tramp-get-remote-readlink v)
-                (tramp-shell-quote-argument localname)))
+       v (format
+         (concat
+          "(if %s -h \"%s\"; then echo t; else echo nil; fi) && "
+          "%s --canonicalize-missing %s")
+         (tramp-get-test-command v)
+         (tramp-shell-quote-argument localname)
+         (tramp-get-remote-readlink v)
+         (tramp-shell-quote-argument localname)))
       (with-current-buffer (tramp-get-connection-buffer v)
        (goto-char (point-min))
-       (buffer-substring (point-min) (line-end-position))))
+       (tramp-set-file-property v localname "file-symlink-marker" (read 
(current-buffer)))
+       ;; We cannote call `read', the file name isn't quoted.
+       (forward-line)
+       (buffer-substring (point) (line-end-position))))
 
      ;; Use Perl implementation.
      ((and (tramp-get-remote-perl v)
@@ -1179,9 +1198,13 @@ Operations not mentioned here will be handled by the 
normal Emacs functions.")
           (tramp-get-connection-property v "perl-cwd-realpath"))
       (tramp-maybe-send-script
        v tramp-perl-file-truename "tramp_perl_file_truename")
-      (tramp-send-command-and-read
+      (tramp-send-command-and-check
        v (format "tramp_perl_file_truename %s"
-                (tramp-shell-quote-argument localname))))
+                (tramp-shell-quote-argument localname)))
+      (with-current-buffer (tramp-get-connection-buffer v)
+        (goto-char (point-min))
+       (tramp-set-file-property v localname "file-symlink-marker" (read 
(current-buffer)))
+       (read (current-buffer))))
 
      ;; Do it yourself.
      (t (tramp-file-local-name
@@ -1675,8 +1698,8 @@ ID-FORMAT valid values are `string' and `integer'."
        (with-tramp-file-property v localname "file-directory-p"
          (if-let
              ((truename (tramp-get-file-property v localname "file-truename"))
-              (attr-p (tramp-file-property-p
-                       v (tramp-file-local-name truename) "file-attributes")))
+              ((tramp-file-property-p
+                v (tramp-file-local-name truename) "file-attributes")))
              (eq (file-attribute-type
                   (tramp-get-file-property
                    v (tramp-file-local-name truename) "file-attributes"))
@@ -1688,9 +1711,9 @@ ID-FORMAT valid values are `string' and `integer'."
   (with-parsed-tramp-file-name (expand-file-name filename) nil
     (with-tramp-file-property v localname "file-writable-p"
       (if (file-exists-p filename)
+         ;; Examine `file-attributes' cache to see if request can be
+         ;; satisfied without remote operation.
          (if (tramp-file-property-p v localname "file-attributes")
-             ;; Examine `file-attributes' cache to see if request can
-             ;; be satisfied without remote operation.
              (tramp-check-cached-permissions v ?w)
            (tramp-run-test v "-w" localname))
        ;; If file doesn't exist, check if directory is writable.
@@ -1789,7 +1812,7 @@ ID-FORMAT valid values are `string' and `integer'."
                               "cd %s 2>&1 && %s -a 2>%s"
                               " | while IFS= read f; do"
                               " if %s -d \"$f\" 2>%s;"
-                              " then \\echo \"$f/\"; else \\echo \"$f\"; fi;"
+                              " then echo \"$f/\"; else echo \"$f\"; fi;"
                               " done")
                              (tramp-shell-quote-argument localname)
                              (tramp-get-ls-command v)
@@ -3525,6 +3548,41 @@ implementation will be used."
          (when coding-system-used
            (setq last-coding-system-used coding-system-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'."
+  (when files
+    (tramp-maybe-send-script
+     vec
+     (format tramp-bundle-read-file-names
+            (tramp-get-file-exists-command vec)
+            (format "%s -r" (tramp-get-test-command vec))
+            (format "%s -d" (tramp-get-test-command vec)))
+     "tramp_bundle_read_file_names")
+
+    (dolist
+       (elt
+        (ignore-errors
+          ;; We cannot use `tramp-send-command-and-read', because
+          ;; this does not cooperate well with heredoc documents.
+          (tramp-send-command
+           vec
+           (format
+            "tramp_bundle_read_file_names <<'%s'\n%s\n%s\n"
+            tramp-end-of-heredoc
+            (mapconcat #'tramp-shell-quote-argument
+                       files
+                       "\n")
+            tramp-end-of-heredoc))
+          (with-current-buffer (tramp-get-connection-buffer vec)
+            ;; Read the expression.
+            (goto-char (point-min))
+            (read (current-buffer)))))
+
+      (tramp-set-file-property
+       vec (car elt) (cadr elt) (cadr (cdr elt))))))
+
 (defvar tramp-vc-registered-file-names nil
   "List used to collect file names, which are checked during `vc-registered'.")
 
@@ -3570,36 +3628,7 @@ implementation will be used."
              (tramp-message v 10 "\n%s" tramp-vc-registered-file-names)
 
              ;; Send just one command, in order to fill the cache.
-             (when tramp-vc-registered-file-names
-               (tramp-maybe-send-script
-                v
-                (format tramp-vc-registered-read-file-names
-                        (tramp-get-file-exists-command v)
-                        (format "%s -r" (tramp-get-test-command v)))
-                "tramp_vc_registered_read_file_names")
-
-               (dolist
-                   (elt
-                    (ignore-errors
-                      ;; We cannot use `tramp-send-command-and-read',
-                      ;; because this does not cooperate well with
-                      ;; heredoc documents.
-                      (tramp-send-command
-                       v
-                       (format
-                        "tramp_vc_registered_read_file_names <<'%s'\n%s\n%s\n"
-                        tramp-end-of-heredoc
-                        (mapconcat #'tramp-shell-quote-argument
-                                   tramp-vc-registered-file-names
-                                   "\n")
-                        tramp-end-of-heredoc))
-                      (with-current-buffer (tramp-get-connection-buffer v)
-                        ;; Read the expression.
-                        (goto-char (point-min))
-                        (read (current-buffer)))))
-
-                 (tramp-set-file-property
-                  v (car elt) (cadr elt) (cadr (cdr elt))))))
+             (tramp-bundle-read-file-names v tramp-vc-registered-file-names))
 
            ;; Second run.  Now all `file-exists-p' or `file-readable-p'
            ;; calls shall be answered from the file cache.  We unset
@@ -4254,6 +4283,8 @@ file exists and nonzero exit status otherwise."
        "`tramp-histfile-override' uses invalid file `%s'"
        tramp-histfile-override))
 
+    (tramp-flush-connection-property
+     (tramp-get-connection-process vec) "scripts")
     (tramp-set-connection-property
      (tramp-get-connection-process vec) "remote-shell" shell)))
 
@@ -4335,12 +4366,10 @@ process to set up.  VEC specifies the connection."
     (tramp-open-shell vec (tramp-get-method-parameter vec 'tramp-remote-shell))
     (tramp-message vec 5 "Setting up remote shell environment")
 
-    ;; Disable line editing.
-    (tramp-send-command vec "set +o vi +o emacs" t)
-
-    ;; Dump option settings in the traces.
-    (when (>= tramp-verbose 9)
-      (tramp-send-command vec "set -o" t))
+    ;; Disable line editing.  Dump option settings in the traces.
+    (tramp-send-command
+     vec
+     (if (>= tramp-verbose 9) "set +o vi +o emacs -o" "set +o vi +o emacs") t)
 
     ;; Disable echo expansion.
     (tramp-send-command
@@ -5554,22 +5583,16 @@ Nonexistent directories are removed from spec."
          (setq remote-path (delq 'tramp-own-remote-path remote-path)))
 
        ;; Remove double entries.
-       (setq elt1 remote-path)
-       (while (consp elt1)
-         (while (and (car elt1) (setq elt2 (member (car elt1) (cdr elt1))))
-           (setcar elt2 nil))
-         (setq elt1 (cdr elt1)))
+       (setq remote-path
+             (cl-remove-duplicates
+              remote-path :test #'string-equal :from-end t))
 
        ;; Remove non-existing directories.
-       (delq
-        nil
-        (mapcar
-         (lambda (x)
-           (and
-            (stringp x)
-            (file-directory-p (tramp-make-tramp-file-name vec x))
-            x))
-         remote-path))))))
+       (let ((remote-file-name-inhibit-cache nil))
+         (tramp-bundle-read-file-names vec remote-path)
+         (cl-remove-if
+          (lambda (x) (not (tramp-get-file-property vec x "file-directory-p")))
+          remote-path))))))
 
 (defun tramp-get-remote-locale (vec)
   "Determine remote locale, supporting UTF8 if possible."
diff --git a/lisp/net/tramp-smb.el b/lisp/net/tramp-smb.el
index 60d40fef147..9c96a3f6851 100644
--- a/lisp/net/tramp-smb.el
+++ b/lisp/net/tramp-smb.el
@@ -1176,7 +1176,7 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are 
completely ignored."
     (unless (tramp-smb-get-cifs-capabilities v)
       (tramp-error v 'file-error "make-symbolic-link not supported")))
 
-  (tramp-skeleton-handle-make-symbolic-link target linkname 
ok-if-already-exists
+  (tramp-skeleton-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)
diff --git a/lisp/net/tramp-sudoedit.el b/lisp/net/tramp-sudoedit.el
index 2bbe0945330..2ce2647b5ac 100644
--- a/lisp/net/tramp-sudoedit.el
+++ b/lisp/net/tramp-sudoedit.el
@@ -574,9 +574,9 @@ the result will be a local, non-Tramp, file name."
   (with-parsed-tramp-file-name (expand-file-name filename) nil
     (with-tramp-file-property v localname "file-writable-p"
       (if (file-exists-p filename)
+         ;; Examine `file-attributes' cache to see if request can be
+         ;; satisfied without remote operation.
          (if (tramp-file-property-p v localname "file-attributes")
-             ;; Examine `file-attributes' cache to see if request can
-             ;; be satisfied without remote operation.
              (tramp-check-cached-permissions v ?w)
            (tramp-sudoedit-send-command
             v "test" "-w" (file-name-unquote localname)))
@@ -596,7 +596,7 @@ 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."
-  (tramp-skeleton-handle-make-symbolic-link target linkname 
ok-if-already-exists
+  (tramp-skeleton-make-symbolic-link target linkname ok-if-already-exists
     (tramp-sudoedit-send-command
      v "ln" "-sf"
      (file-name-unquote target)
diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el
index 76674e5207f..00b47f6bead 100644
--- a/lisp/net/tramp.el
+++ b/lisp/net/tramp.el
@@ -3286,6 +3286,8 @@ 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")))
@@ -3356,7 +3358,7 @@ BODY is the backend specific code."
         ,@body
         nil))))
 
-(defmacro tramp-skeleton-handle-make-symbolic-link
+(defmacro tramp-skeleton-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.
@@ -3961,8 +3963,14 @@ Let-bind it when necessary.")
 
 (defun tramp-handle-file-symlink-p (filename)
   "Like `file-symlink-p' for Tramp files."
-  (let ((x (file-attribute-type (file-attributes filename))))
-    (and (stringp x) x)))
+  (with-parsed-tramp-file-name (expand-file-name filename) nil
+    ;; Some operations, like `file-truename', set the file property
+    ;; "file-symlink-marker".  We can use it as indicator, and avoid a
+    ;; possible call of `file-attributes'.
+    (when (or (tramp-get-file-property v localname "file-symlink-marker")
+             (not (tramp-file-property-p v localname "file-symlink-marker")))
+      (let ((x (file-attribute-type (file-attributes filename))))
+       (and (stringp x) x)))))
 
 (defun tramp-handle-file-truename (filename)
   "Like `file-truename' for Tramp files."



reply via email to

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