From 684950eb945064b8273109fc165818edd470da32 Mon Sep 17 00:00:00 2001 From: Michael Albinus Date: Thu, 8 Sep 2022 10:36:08 +0200 Subject: [PATCH] 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 3fb28d91eae..dfb026f8344 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 ff153d955be..a783f8c16c1 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 ef0954ab83f..e0b577fff85 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 cfc005d270c..cd68801c214 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 f42f6838c86..6f7c6702e76 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)) -- 2.39.2