emacs-diffs
[Top][All Lists]
Advanced

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

master 684950eb94: Make call of remote `id' more performant in Tramp


From: Michael Albinus
Subject: master 684950eb94: Make call of remote `id' more performant in Tramp
Date: Thu, 8 Sep 2022 04:36:18 -0400 (EDT)

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

    Make call of remote `id' more performant in Tramp
    
    * lisp/net/tramp-adb.el (tramp-adb-handle-file-exists-p): New defun.
    (tramp-adb-file-name-handler-alist): Use it.
    (tramp-adb-handle-file-executable-p)
    (tramp-adb-handle-file-readable-p)
    (tramp-adb-handle-file-writable-p)
    (tramp-adb-handle-get-remote-uid)
    (tramp-adb-handle-get-remote-gid)
    (tramp-adb-handle-get-remote-groups): Use caches consequently.
    
    * lisp/net/tramp-sh.el (tramp-perl-id, tramp-python-id): New defconsts.
    (tramp-sh-handle-get-remote-uid, tramp-sh-handle-get-remote-gid)
    (tramp-sh-handle-get-remote-groups): Use caches consequently.
    (tramp-sh-handle-file-writable-p): Use `file-writable-p'.
    (tramp-expand-script): Handle also "python" expansion.
    (tramp-get-remote-id): Do not set connection property anymore,
    this is done differently now.
    (tramp-get-remote-uid-with-id, tramp-get-remote-uid-with-perl)
    (tramp-get-remote-uid-with-python, tramp-get-remote-gid-with-id)
    (tramp-get-remote-gid-with-perl)
    (tramp-get-remote-gid-with-python): Remove.
    
    * lisp/net/tramp-sudoedit.el (tramp-sudoedit-handle-file-executable-p)
    (tramp-sudoedit-handle-file-exists-p)
    (tramp-sudoedit-handle-file-readable-p)
    (tramp-sudoedit-handle-file-writable-p):
    (tramp-sudoedit-handle-get-remote-uid)
    (tramp-sudoedit-handle-get-remote-gid)
    (tramp-sudoedit-handle-get-remote-groups): Use caches consequently.
    
    * lisp/net/tramp.el (tramp-check-cached-permissions):
    Call `tramp-get-remote-groups' only if needed.
    (tramp-get-remote-groups): Do not return default value.
    (tramp-read-id-output): New defun.
    
    * test/lisp/net/tramp-tests.el (tramp--test-deftest-with-perl):
    Suppress also remote `id'.
---
 lisp/net/tramp-adb.el        |  92 ++++++++++-----------
 lisp/net/tramp-sh.el         | 189 +++++++++++++++++++------------------------
 lisp/net/tramp-sudoedit.el   |  75 ++++++++---------
 lisp/net/tramp.el            |  54 ++++++++++---
 test/lisp/net/tramp-tests.el |   4 +-
 5 files changed, 209 insertions(+), 205 deletions(-)

diff --git a/lisp/net/tramp-adb.el b/lisp/net/tramp-adb.el
index 3fb28d91ea..dfb026f834 100644
--- a/lisp/net/tramp-adb.el
+++ b/lisp/net/tramp-adb.el
@@ -127,7 +127,7 @@ It is used for TCP/IP devices."
     (file-directory-p . tramp-handle-file-directory-p)
     (file-equal-p . tramp-handle-file-equal-p)
     (file-executable-p . tramp-adb-handle-file-executable-p)
-    (file-exists-p . tramp-handle-file-exists-p)
+    (file-exists-p . tramp-adb-handle-file-exists-p)
     (file-in-directory-p . tramp-handle-file-in-directory-p)
     (file-local-copy . tramp-adb-handle-file-local-copy)
     (file-locked-p . tramp-handle-file-locked-p)
@@ -489,24 +489,50 @@ Emacs dired can't find files."
   "Like `file-executable-p' for Tramp files."
   (with-parsed-tramp-file-name filename nil
     (with-tramp-file-property v localname "file-executable-p"
-      (tramp-adb-send-command-and-check
-       v (format "test -x %s" (tramp-shell-quote-argument localname))))))
+      ;; Examine `file-attributes' cache to see if request can be
+      ;; satisfied without remote operation.
+      (if (tramp-file-property-p v localname "file-attributes")
+         (or (tramp-check-cached-permissions v ?x)
+             (tramp-check-cached-permissions v ?s))
+       (tramp-adb-send-command-and-check
+        v (format "test -x %s" (tramp-shell-quote-argument localname)))))))
+
+(defun tramp-adb-handle-file-exists-p (filename)
+  "Like `file-exists-p' for Tramp files."
+  ;; `file-exists-p' is used as predicate in file name completion.
+  ;; We don't want to run it when `non-essential' is t, or there is
+  ;; no connection process yet.
+  (when (tramp-connectable-p filename)
+    (with-parsed-tramp-file-name filename nil
+      (with-tramp-file-property v localname "file-exists-p"
+       (if (tramp-file-property-p v localname "file-attributes")
+           (not (null (tramp-get-file-property v localname "file-attributes")))
+         (tramp-adb-send-command-and-check
+          v (format "test -e %s" (tramp-shell-quote-argument localname))))))))
 
 (defun tramp-adb-handle-file-readable-p (filename)
   "Like `file-readable-p' for Tramp files."
   (with-parsed-tramp-file-name filename nil
     (with-tramp-file-property v localname "file-readable-p"
-      (or (tramp-handle-file-readable-p filename)
-         (tramp-adb-send-command-and-check
-          v (format "test -r %s" (tramp-shell-quote-argument localname)))))))
+      ;; Examine `file-attributes' cache to see if request can be
+      ;; satisfied without remote operation.
+      (if (tramp-file-property-p v localname "file-attributes")
+         (tramp-handle-file-readable-p filename)
+       (tramp-adb-send-command-and-check
+        v (format "test -r %s" (tramp-shell-quote-argument localname)))))))
 
 (defun tramp-adb-handle-file-writable-p (filename)
   "Like `file-writable-p' for Tramp files."
   (with-parsed-tramp-file-name filename nil
     (with-tramp-file-property v localname "file-writable-p"
       (if (file-exists-p filename)
-         (tramp-adb-send-command-and-check
-          v (format "test -w %s" (tramp-shell-quote-argument localname)))
+         (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))))
+       ;; If file doesn't exist, check if directory is writable.
        (and
         (file-directory-p (file-name-directory filename))
         (file-writable-p (file-name-directory filename)))))))
@@ -1040,57 +1066,23 @@ implementation will be used."
 (defun tramp-adb-handle-get-remote-uid (vec id-format)
   "Like `tramp-get-remote-uid' for Tramp files.
  ID-FORMAT valid values are `string' and `integer'."
- ;; The result is cached in `tramp-get-remote-uid'.
-  (tramp-adb-send-command
-   vec
-   (format "id -u%s %s"
-          (if (equal id-format 'integer) "" "n")
-          (if (equal id-format 'integer)
-              "" "| sed -e s/^/\\\"/ -e s/\\$/\\\"/")))
-  (with-current-buffer (tramp-get-connection-buffer vec)
-    ;; Read the expression.
-    (goto-char (point-min))
-    (read (current-buffer))))
+  (tramp-adb-send-command vec "id")
+  (tramp-read-id-output vec)
+  (tramp-get-connection-property vec (format "uid-%s" id-format)))
 
 (defun tramp-adb-handle-get-remote-gid (vec id-format)
   "Like `tramp-get-remote-gid' for Tramp files.
 ID-FORMAT valid values are `string' and `integer'."
-  ;; The result is cached in `tramp-get-remote-gid'.
-  (tramp-adb-send-command
-   vec
-   (format "id -g%s %s"
-          (if (equal id-format 'integer) "" "n")
-          (if (equal id-format 'integer)
-              "" "| sed -e s/^/\\\"/ -e s/\\$/\\\"/")))
-  (with-current-buffer (tramp-get-connection-buffer vec)
-    ;; Read the expression.
-    (goto-char (point-min))
-    (read (current-buffer))))
+  (tramp-adb-send-command vec "id")
+  (tramp-read-id-output vec)
+  (tramp-get-connection-property vec (format "gid-%s" id-format)))
 
 (defun tramp-adb-handle-get-remote-groups (vec id-format)
   "Like `tramp-get-remote-groups' for Tramp files.
 ID-FORMAT valid values are `string' and `integer'."
-  ;; The result is cached in `tramp-get-remote-groups'.
   (tramp-adb-send-command vec "id")
-  (with-current-buffer (tramp-get-connection-buffer vec)
-    (let (groups-integer groups-string)
-      ;; Read the expression.
-      (goto-char (point-min))
-      (when (re-search-forward (rx bol (+ nonl) "groups=") nil 'noerror)
-       (while (looking-at
-               (rx (group (+ digit)) "(" (group (+ (any "_" word))) ")"))
-         (setq groups-integer (cons (string-to-number (match-string 1))
-                                    groups-integer)
-               groups-string (cons (match-string 2) groups-string))
-         (goto-char (match-end 0))
-         (skip-chars-forward ",")))
-      (tramp-set-connection-property
-       vec "groups-integer"
-       (setq groups-integer (nreverse groups-integer)))
-      (tramp-set-connection-property
-       vec "groups-string"
-       (setq groups-string (nreverse groups-string)))
-      (if (eq id-format 'integer) groups-integer groups-string))))
+  (tramp-read-id-output vec)
+  (tramp-get-connection-property vec (format "groups-%s" id-format)))
 
 (defun tramp-adb-get-device (vec)
   "Return full host name from VEC to be used in shell execution.
diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el
index ff153d955b..a783f8c16c 100644
--- a/lisp/net/tramp-sh.el
+++ b/lisp/net/tramp-sh.el
@@ -783,6 +783,41 @@ characters need to be doubled.")
 Format specifiers are replaced by `tramp-expand-script', percent
 characters need to be doubled.")
 
+(defconst tramp-perl-id
+  "%p -e '
+use strict;
+use warnings;
+use POSIX qw(getgroups);
+
+my ($user, $passwd, $uid, $gid) = getpwuid $< ;
+my $group = getgrgid $gid ;
+my @groups = map { $_ . \"(\" . getgrgid ($_) . \")\" } getgroups ();
+
+printf \"uid=%%d(%%s) gid=%%d(%%s) groups=%%s\\n\",
+  $uid, $user, $gid, $group, join \",\", @groups;' %n"
+  "Perl script printing `id' output.
+Format specifiers are replaced by `tramp-expand-script', percent
+characters need to be doubled.")
+
+(defconst tramp-python-id
+  "%y -c '
+import os, pwd, grp;
+
+def idform(id):
+  return \"{:d}({:s})\".format(id, grp.getgrgid(id)[0]);
+
+uid = os.getuid();
+user = pwd.getpwuid(uid)[0];
+gid = os.getgid();
+group = grp.getgrgid(gid)[0]
+groups = map(idform, os.getgrouplist(user, gid));
+
+print(\"uid={:d}({:s}) gid={:d}({:s}) groups={:s}\"
+      .format(uid, user, gid, group, \",\".join(groups)));' %n"
+  "Python script printing `id' output.
+Format specifiers are replaced by `tramp-expand-script', percent
+characters need to be doubled.")
+
 ;; These two use base64 encoding.
 (defconst tramp-perl-encode-with-module
   "%p -MMIME::Base64 -0777 -ne 'print encode_base64($_)' %n"
@@ -1524,10 +1559,16 @@ ID-FORMAT valid values are `string' and `integer'."
   ;; The result is cached in `tramp-get-remote-uid'.
   (ignore-errors
     (cond
-     ((tramp-get-remote-id vec) (tramp-get-remote-uid-with-id vec id-format))
-     ((tramp-get-remote-perl vec) (tramp-get-remote-uid-with-perl vec 
id-format))
+     ((tramp-get-remote-id vec)
+      (tramp-send-command vec (tramp-get-remote-id vec)))
+     ((tramp-get-remote-perl vec)
+      (tramp-maybe-send-script vec tramp-perl-id "tramp_perl_id")
+      (tramp-send-command vec "tramp_perl_id"))
      ((tramp-get-remote-python vec)
-      (tramp-get-remote-uid-with-python vec id-format)))))
+      (tramp-maybe-send-script vec tramp-python-id "tramp_python_id")
+      (tramp-send-command vec "tramp_python_id")))
+    (tramp-read-id-output vec)
+    (tramp-get-connection-property vec (format "uid-%s" id-format))))
 
 (defun tramp-sh-handle-get-remote-gid (vec id-format)
   "The gid of the remote connection VEC, in ID-FORMAT.
@@ -1535,36 +1576,33 @@ ID-FORMAT valid values are `string' and `integer'."
   ;; The result is cached in `tramp-get-remote-gid'.
   (ignore-errors
     (cond
-     ((tramp-get-remote-id vec) (tramp-get-remote-gid-with-id vec id-format))
-     ((tramp-get-remote-perl vec) (tramp-get-remote-gid-with-perl vec 
id-format))
+     ((tramp-get-remote-id vec)
+      (tramp-send-command vec (tramp-get-remote-id vec)))
+     ((tramp-get-remote-perl vec)
+      (tramp-maybe-send-script vec tramp-perl-id "tramp_perl_id")
+      (tramp-send-command vec "tramp_perl_id"))
      ((tramp-get-remote-python vec)
-      (tramp-get-remote-gid-with-python vec id-format)))))
+      (tramp-maybe-send-script vec tramp-python-id "tramp_python_id")
+      (tramp-send-command vec "tramp_python_id")))
+    (tramp-read-id-output vec)
+    (tramp-get-connection-property vec (format "gid-%s" id-format))))
 
 (defun tramp-sh-handle-get-remote-groups (vec id-format)
   "Like `tramp-get-remote-groups' for Tramp files.
 ID-FORMAT valid values are `string' and `integer'."
   ;; The result is cached in `tramp-get-remote-groups'.
-  (when (tramp-get-remote-id vec)
-    (tramp-send-command vec (tramp-get-remote-id vec)))
-  (with-current-buffer (tramp-get-connection-buffer vec)
-    (let (groups-integer groups-string)
-      ;; Read the expression.
-      (goto-char (point-min))
-      (when (re-search-forward (rx bol (+ nonl) "groups=") nil 'noerror)
-       (while (looking-at
-               (rx (group (+ digit)) "(" (group (+ (any "_" word))) ")"))
-         (setq groups-integer (cons (string-to-number (match-string 1))
-                                    groups-integer)
-               groups-string (cons (match-string 2) groups-string))
-         (goto-char (match-end 0))
-         (skip-chars-forward ",")))
-      (tramp-set-connection-property
-       vec "groups-integer"
-       (setq groups-integer (nreverse groups-integer)))
-      (tramp-set-connection-property
-       vec "groups-string"
-       (setq groups-string (nreverse groups-string)))
-      (if (eq id-format 'integer) groups-integer groups-string))))
+  (ignore-errors
+    (cond
+     ((tramp-get-remote-id vec)
+      (tramp-send-command vec (tramp-get-remote-id vec)))
+     ((tramp-get-remote-perl vec)
+      (tramp-maybe-send-script vec tramp-perl-id "tramp_perl_id")
+      (tramp-send-command vec "tramp_perl_id"))
+     ((tramp-get-remote-python vec)
+      (tramp-maybe-send-script vec tramp-python-id "tramp_python_id")
+      (tramp-send-command vec "tramp_python_id")))
+    (tramp-read-id-output vec)
+    (tramp-get-connection-property vec (format "groups-%s" id-format))))
 
 (defun tramp-sh-handle-set-file-uid-gid (filename &optional uid gid)
   "Like `tramp-set-file-uid-gid' for Tramp files."
@@ -1694,6 +1732,8 @@ ID-FORMAT valid values are `string' and `integer'."
   "Like `file-readable-p' for Tramp files."
   (with-parsed-tramp-file-name filename nil
     (with-tramp-file-property v localname "file-readable-p"
+      ;; Examine `file-attributes' cache to see if request can be
+      ;; satisfied without remote operation.
       (if (tramp-file-property-p v localname "file-attributes")
          (tramp-handle-file-readable-p filename)
        (tramp-run-test "-r" filename)))))
@@ -1730,8 +1770,9 @@ ID-FORMAT valid values are `string' and `integer'."
              (tramp-check-cached-permissions v ?w)
            (tramp-run-test "-w" filename))
        ;; If file doesn't exist, check if directory is writable.
-       (and (file-exists-p (file-name-directory filename))
-            (tramp-run-test "-w" (file-name-directory filename)))))))
+       (and
+        (file-directory-p (file-name-directory filename))
+        (file-writable-p (file-name-directory filename)))))))
 
 (defun tramp-sh-handle-file-ownership-preserved-p (filename &optional group)
   "Like `file-ownership-preserved-p' for Tramp files."
@@ -3971,15 +4012,15 @@ Fall back to normal file name handler if no Tramp 
handler exists."
 
 (defun tramp-expand-script (vec script)
   "Expand SCRIPT with remote files or commands.
-\"%a\", \"%h\", \"%l\", \"%o\", \"%p\", \"%r\" and \"%s\" format
-specifiers are replaced by the respective `awk', `hexdump', `ls',
-`od', `perl', `readlink' and `stat' commands.  \"%n\" is replaced
-by \"2>/dev/null\", and \"%t\" is replaced by a temporary file
-name.  If VEC is nil, the respective local commands are used.  If
-there is a format specifier which cannot be expanded, this
-function returns nil."
+\"%a\", \"%h\", \"%l\", \"%o\", \"%p\", \"%r\", \"%s\" and \"%y\"
+format specifiers are replaced by the respective `awk',
+`hexdump', `ls', `od', `perl', `readlink', `stat' and `python'
+commands.  \"%n\" is replaced by \"2>/dev/null\", and \"%t\" is
+replaced by a temporary file name.  If VEC is nil, the respective
+local commands are used.  If there is a format specifier which
+cannot be expanded, this function returns nil."
   (if (not (string-match-p
-           (rx (| bol (not (any "%"))) "%" (any "ahlnoprst")) script))
+           (rx (| bol (not (any "%"))) "%" (any "ahlnoprsty")) script))
       script
     (catch 'wont-work
       (let ((awk (when (string-match-p (rx (| bol (not (any "%"))) "%a") 
script)
@@ -4010,6 +4051,11 @@ function returns nil."
                     (if vec
                         (tramp-get-remote-perl vec) (executable-find "perl"))
                     (throw 'wont-work nil))))
+           (python (when (string-match-p (rx (| bol (not (any "%"))) "%y") 
script)
+                   (or
+                    (if vec
+                        (tramp-get-remote-python vec) (executable-find 
"python"))
+                    (throw 'wont-work nil))))
            (readlink (when (string-match-p
                             (rx (| bol (not (any "%"))) "%r") script)
                        (or
@@ -4032,7 +4078,7 @@ function returns nil."
         script
         (format-spec-make
          ?a awk ?h hdmp ?l ls ?n dev ?o od ?p perl
-         ?r readlink ?s stat ?t tmp))))))
+         ?r readlink ?s stat ?t tmp ?y python))))))
 
 (defun tramp-maybe-send-script (vec script name)
   "Define in remote shell function NAME implemented as SCRIPT.
@@ -5816,36 +5862,9 @@ This command is returned only if 
`delete-by-moving-to-trash' is non-nil."
          (while (and dl (setq result (tramp-find-executable vec cmd dl t t)))
            ;; Check POSIX parameter.
            (when (tramp-send-command-and-check vec (format "%s -u" result))
-             (tramp-set-connection-property
-              vec "uid-integer"
-              (with-current-buffer (tramp-get-connection-buffer vec)
-                (goto-char (point-min))
-                (read (current-buffer))))
              (throw 'id-found result))
            (setq dl (cdr dl))))))))
 
-(defun tramp-get-remote-uid-with-id (vec id-format)
-  "Implement `tramp-get-remote-uid' for Tramp files using `id'."
-  ;; `tramp-get-remote-id' sets already connection property "uid-integer".
-  (with-tramp-connection-property vec (format "uid-%s" id-format)
-    (tramp-send-command-and-read
-     vec
-     (format "%s -u%s %s"
-            (tramp-get-remote-id vec)
-            (if (equal id-format 'integer) "" "n")
-            (if (equal id-format 'integer)
-                "" "| sed -e s/^/\\\"/ -e s/\\$/\\\"/")))))
-
-(defun tramp-get-remote-uid-with-perl (vec id-format)
-  "Implement `tramp-get-remote-uid' for Tramp files using a Perl script."
-  (tramp-send-command-and-read
-   vec
-   (format "%s -le '%s'"
-          (tramp-get-remote-perl vec)
-          (if (equal id-format 'integer)
-              "print $>"
-            "print \"\\\"\", scalar getpwuid($>), \"\\\"\""))))
-
 (defun tramp-get-remote-python (vec)
   "Determine remote `python' command."
   (with-tramp-connection-property vec "python"
@@ -5853,46 +5872,6 @@ This command is returned only if 
`delete-by-moving-to-trash' is non-nil."
     (or (tramp-find-executable vec "python" (tramp-get-remote-path vec))
         (tramp-find-executable vec "python3" (tramp-get-remote-path vec)))))
 
-(defun tramp-get-remote-uid-with-python (vec id-format)
-  "Implement `tramp-get-remote-uid' for Tramp files using `python'."
-  (tramp-send-command-and-read
-   vec
-   (format "%s -c \"%s\""
-          (tramp-get-remote-python vec)
-          (if (equal id-format 'integer)
-              "import os; print (os.getuid())"
-    "import os, pwd; print ('\\\"' + pwd.getpwuid(os.getuid())[0] + 
'\\\"')"))))
-
-(defun tramp-get-remote-gid-with-id (vec id-format)
-  "Implement `tramp-get-remote-gid' for Tramp files using `id'."
-  (tramp-send-command-and-read
-   vec
-   (format "%s -g%s %s"
-          (tramp-get-remote-id vec)
-          (if (equal id-format 'integer) "" "n")
-          (if (equal id-format 'integer)
-              "" "| sed -e s/^/\\\"/ -e s/\\$/\\\"/"))))
-
-(defun tramp-get-remote-gid-with-perl (vec id-format)
-  "Implement `tramp-get-remote-gid' for Tramp files using a Perl script."
-  (tramp-send-command-and-read
-   vec
-   (format "%s -le '%s'"
-          (tramp-get-remote-perl vec)
-          (if (equal id-format 'integer)
-              "print ($)=~/(\\d+)/)"
-            "print \"\\\"\", scalar getgrgid($)), \"\\\"\""))))
-
-(defun tramp-get-remote-gid-with-python (vec id-format)
-  "Implement `tramp-get-remote-gid' for Tramp files using `python'."
-  (tramp-send-command-and-read
-   vec
-   (format "%s -c \"%s\""
-          (tramp-get-remote-python vec)
-          (if (equal id-format 'integer)
-              "import os; print (os.getgid())"
-    "import os, grp; print ('\\\"' + grp.getgrgid(os.getgid())[0] + 
'\\\"')"))))
-
 (defun tramp-get-remote-busybox (vec)
   "Determine remote `busybox' command."
   (with-tramp-connection-property vec "busybox"
diff --git a/lisp/net/tramp-sudoedit.el b/lisp/net/tramp-sudoedit.el
index ef0954ab83..e0b577fff8 100644
--- a/lisp/net/tramp-sudoedit.el
+++ b/lisp/net/tramp-sudoedit.el
@@ -442,8 +442,13 @@ the result will be a local, non-Tramp, file name."
   "Like `file-executable-p' for Tramp files."
   (with-parsed-tramp-file-name filename nil
     (with-tramp-file-property v localname "file-executable-p"
-      (tramp-sudoedit-send-command
-       v "test" "-x" (tramp-compat-file-name-unquote localname)))))
+      ;; Examine `file-attributes' cache to see if request can be
+      ;; satisfied without remote operation.
+      (if (tramp-file-property-p v localname "file-attributes")
+         (or (tramp-check-cached-permissions v ?x)
+             (tramp-check-cached-permissions v ?s))
+       (tramp-sudoedit-send-command
+        v "test" "-x" (tramp-compat-file-name-unquote localname))))))
 
 (defun tramp-sudoedit-handle-file-exists-p (filename)
   "Like `file-exists-p' for Tramp files."
@@ -453,8 +458,10 @@ the result will be a local, non-Tramp, file name."
   (when (tramp-connectable-p filename)
     (with-parsed-tramp-file-name filename nil
       (with-tramp-file-property v localname "file-exists-p"
-       (tramp-sudoedit-send-command
-        v "test" "-e" (tramp-compat-file-name-unquote localname))))))
+       (if (tramp-file-property-p v localname "file-attributes")
+           (not (null (tramp-get-file-property v localname "file-attributes")))
+         (tramp-sudoedit-send-command
+          v "test" "-e" (tramp-compat-file-name-unquote localname)))))))
 
 (defun tramp-sudoedit-handle-file-name-all-completions (filename directory)
   "Like `file-name-all-completions' for Tramp files."
@@ -483,9 +490,12 @@ the result will be a local, non-Tramp, file name."
   "Like `file-readable-p' for Tramp files."
   (with-parsed-tramp-file-name filename nil
     (with-tramp-file-property v localname "file-readable-p"
-      (or (tramp-handle-file-readable-p filename)
-         (tramp-sudoedit-send-command
-          v "test" "-r" (tramp-compat-file-name-unquote localname))))))
+      ;; Examine `file-attributes' cache to see if request can be
+      ;; satisfied without remote operation.
+      (if (tramp-file-property-p v localname "file-attributes")
+         (tramp-handle-file-readable-p filename)
+       (tramp-sudoedit-send-command
+        v "test" "-r" (tramp-compat-file-name-unquote localname))))))
 
 (defun tramp-sudoedit-handle-set-file-modes (filename mode &optional flag)
   "Like `set-file-modes' for Tramp files."
@@ -597,11 +607,16 @@ the result will be a local, non-Tramp, file name."
   (with-parsed-tramp-file-name filename nil
     (with-tramp-file-property v localname "file-writable-p"
       (if (file-exists-p filename)
-         (tramp-sudoedit-send-command
-          v "test" "-w" (tramp-compat-file-name-unquote localname))
-       (let ((dir (file-name-directory filename)))
-         (and (file-exists-p dir)
-              (file-writable-p dir)))))))
+         (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" (tramp-compat-file-name-unquote localname)))
+       ;; If file doesn't exist, check if directory is writable.
+       (and
+        (file-directory-p (file-name-directory filename))
+        (file-writable-p (file-name-directory filename)))))))
 
 (defun tramp-sudoedit-handle-make-directory (dir &optional parents)
   "Like `make-directory' for Tramp files."
@@ -720,43 +735,23 @@ VEC or USER, or if there is no home directory, return 
nil."
 (defun tramp-sudoedit-handle-get-remote-uid (vec id-format)
   "The uid of the remote connection VEC, in ID-FORMAT.
 ID-FORMAT valid values are `string' and `integer'."
-  ;; The result is cached in `tramp-get-remote-uid'.
-  (if (equal id-format 'integer)
-      (tramp-sudoedit-send-command-and-read vec "id" "-u")
-    (tramp-sudoedit-send-command-string vec "id" "-un")))
+  (tramp-sudoedit-send-command vec "id")
+  (tramp-read-id-output vec)
+  (tramp-get-connection-property vec (format "uid-%s" id-format)))
 
 (defun tramp-sudoedit-handle-get-remote-gid (vec id-format)
   "The gid of the remote connection VEC, in ID-FORMAT.
 ID-FORMAT valid values are `string' and `integer'."
-  ;; The result is cached in `tramp-get-remote-gid'.
-  (if (equal id-format 'integer)
-      (tramp-sudoedit-send-command-and-read vec "id" "-g")
-    (tramp-sudoedit-send-command-string vec "id" "-gn")))
+  (tramp-sudoedit-send-command vec "id")
+  (tramp-read-id-output vec)
+  (tramp-get-connection-property vec (format "gid-%s" id-format)))
 
 (defun tramp-sudoedit-handle-get-remote-groups (vec id-format)
   "Like `tramp-get-remote-groups' for Tramp files.
 ID-FORMAT valid values are `string' and `integer'."
-  ;; The result is cached in `tramp-get-remote-groups'.
   (tramp-sudoedit-send-command vec "id")
-  (with-current-buffer (tramp-get-connection-buffer vec)
-    (let (groups-integer groups-string)
-      ;; Read the expression.
-      (goto-char (point-min))
-      (when (re-search-forward (rx bol (+ nonl) "groups=") nil 'noerror)
-       (while (looking-at
-               (rx (group (+ digit)) "(" (group (+ (any "_" word))) ")"))
-         (setq groups-integer (cons (string-to-number (match-string 1))
-                                    groups-integer)
-               groups-string (cons (match-string 2) groups-string))
-         (goto-char (match-end 0))
-         (skip-chars-forward ",")))
-      (tramp-set-connection-property
-       vec "groups-integer"
-       (setq groups-integer (nreverse groups-integer)))
-      (tramp-set-connection-property
-       vec "groups-string"
-       (setq groups-string (nreverse groups-string)))
-      (if (eq id-format 'integer) groups-integer groups-string))))
+  (tramp-read-id-output vec)
+  (tramp-get-connection-property vec (format "groups-%s" id-format)))
 
 (defun tramp-sudoedit-handle-set-file-uid-gid (filename &optional uid gid)
   "Like `tramp-set-file-uid-gid' for Tramp files."
diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el
index cfc005d270..cd68801c21 100644
--- a/lisp/net/tramp.el
+++ b/lisp/net/tramp.el
@@ -5838,8 +5838,7 @@ be granted."
                       ((eq ?s access) 3)))
             (file-attr (file-attributes (tramp-make-tramp-file-name vec)))
             (remote-uid (tramp-get-remote-uid vec 'integer))
-            (remote-gid (tramp-get-remote-gid vec 'integer))
-            (remote-groups (tramp-get-remote-groups vec 'integer)))
+            (remote-gid (tramp-get-remote-gid vec 'integer)))
     (or
      ;; Not a symlink.
      (eq t (file-attribute-type file-attr))
@@ -5867,7 +5866,8 @@ be granted."
      (and
       (eq access
          (aref (file-attribute-modes file-attr) (+ offset 3)))
-      (member (file-attribute-group-id file-attr) remote-groups)))))
+      (member (file-attribute-group-id file-attr)
+             (tramp-get-remote-groups vec 'integer))))))
 
 (defmacro tramp-convert-file-attributes (vec localname id-format attr)
   "Convert `file-attributes' ATTR generated Tramp backend functions.
@@ -6008,12 +6008,48 @@ ID-FORMAT valid values are `string' and `integer'."
 (defun tramp-get-remote-groups (vec id-format)
   "The list of groups of the remote connection VEC, in ID-FORMAT.
 ID-FORMAT valid values are `string' and `integer'."
-  (or (and (tramp-file-name-p vec)
-          (with-tramp-connection-property vec (format "groups-%s" id-format)
-            (tramp-file-name-handler #'tramp-get-remote-groups vec id-format)))
-      ;; Ensure there is a valid result.
-      (and (equal id-format 'integer) (list tramp-unknown-id-integer))
-      (and (equal id-format 'string) (list tramp-unknown-id-string))))
+  (and (tramp-file-name-p vec)
+       (with-tramp-connection-property vec (format "groups-%s" id-format)
+        (tramp-file-name-handler #'tramp-get-remote-groups vec id-format))))
+
+(defun tramp-read-id-output (vec)
+  "Read in connection buffer the output of the `id' command.
+Set connection properties \"{uid,gid.groups}-{integer,string}\"."
+  (with-current-buffer (tramp-get-connection-buffer vec)
+    (let (uid-integer uid-string
+         gid-integer gid-string
+         groups-integer groups-string)
+      (goto-char (point-min))
+      ;; Read uid.
+      (when (re-search-forward
+            (rx "uid=" (group (+ digit)) "(" (group (+ (any "_" word))) ")")
+            nil 'noerror)
+       (setq uid-integer (string-to-number (match-string 1))
+             uid-string (match-string 2)))
+      ;; Read gid.
+      (when (re-search-forward
+            (rx "gid=" (group (+ digit)) "(" (group (+ (any "_" word))) ")")
+            nil 'noerror)
+       (setq gid-integer (string-to-number (match-string 1))
+             gid-string (match-string 2)))
+      ;; Read groups.
+      (when (re-search-forward (rx "groups=") nil 'noerror)
+       (while (looking-at
+               (rx (group (+ digit)) "(" (group (+ (any "_" word))) ")"))
+         (setq groups-integer (cons (string-to-number (match-string 1))
+                                    groups-integer)
+               groups-string (cons (match-string 2) groups-string))
+         (goto-char (match-end 0))
+         (skip-chars-forward ",")))
+      ;; Set connection properties.
+      (tramp-set-connection-property vec "uid-integer" uid-integer)
+      (tramp-set-connection-property vec "uid-string" uid-string)
+      (tramp-set-connection-property vec "gid-integer" gid-integer)
+      (tramp-set-connection-property vec "gid-string" gid-string)
+      (tramp-set-connection-property
+       vec "groups-integer" (nreverse groups-integer))
+      (tramp-set-connection-property
+       vec "groups-string" (nreverse groups-string)))))
 
 (defun tramp-local-host-p (vec)
   "Return t if this points to the local host, nil otherwise.
diff --git a/test/lisp/net/tramp-tests.el b/test/lisp/net/tramp-tests.el
index f42f6838c8..6f7c6702e7 100644
--- a/test/lisp/net/tramp-tests.el
+++ b/test/lisp/net/tramp-tests.el
@@ -3622,7 +3622,9 @@ This tests also `access-file', `file-readable-p',
               (append
                '((nil "stat" nil)
                  ;; See `tramp-sh-handle-file-truename'.
-                 (nil "readlink" nil))
+                 (nil "readlink" nil)
+                 ;; See `tramp-sh-handle-get-remote-*'.
+                 (nil "id" nil))
                tramp-connection-properties)))
         (progn
           (skip-unless (< (ert-test-result-duration result) 300))



reply via email to

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