aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEli Zaretskii2022-09-08 11:49:17 +0300
committerEli Zaretskii2022-09-08 11:49:17 +0300
commit5bc44026b59daa48f3563b2592c6eeae5cee8a74 (patch)
tree2eef077a088fdb5b3a8a6b78f2a1d330aa4f8987
parent6b7dc132afe99e1b188882d32371b06021f64e95 (diff)
parent684950eb945064b8273109fc165818edd470da32 (diff)
downloademacs-5bc44026b59daa48f3563b2592c6eeae5cee8a74.tar.gz
emacs-5bc44026b59daa48f3563b2592c6eeae5cee8a74.zip
Merge branch 'master' of git.savannah.gnu.org:/srv/git/emacs
-rw-r--r--lisp/net/tramp-adb.el92
-rw-r--r--lisp/net/tramp-sh.el189
-rw-r--r--lisp/net/tramp-sudoedit.el75
-rw-r--r--lisp/net/tramp.el54
-rw-r--r--test/lisp/net/tramp-tests.el4
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."
127 (file-directory-p . tramp-handle-file-directory-p) 127 (file-directory-p . tramp-handle-file-directory-p)
128 (file-equal-p . tramp-handle-file-equal-p) 128 (file-equal-p . tramp-handle-file-equal-p)
129 (file-executable-p . tramp-adb-handle-file-executable-p) 129 (file-executable-p . tramp-adb-handle-file-executable-p)
130 (file-exists-p . tramp-handle-file-exists-p) 130 (file-exists-p . tramp-adb-handle-file-exists-p)
131 (file-in-directory-p . tramp-handle-file-in-directory-p) 131 (file-in-directory-p . tramp-handle-file-in-directory-p)
132 (file-local-copy . tramp-adb-handle-file-local-copy) 132 (file-local-copy . tramp-adb-handle-file-local-copy)
133 (file-locked-p . tramp-handle-file-locked-p) 133 (file-locked-p . tramp-handle-file-locked-p)
@@ -489,24 +489,50 @@ Emacs dired can't find files."
489 "Like `file-executable-p' for Tramp files." 489 "Like `file-executable-p' for Tramp files."
490 (with-parsed-tramp-file-name filename nil 490 (with-parsed-tramp-file-name filename nil
491 (with-tramp-file-property v localname "file-executable-p" 491 (with-tramp-file-property v localname "file-executable-p"
492 (tramp-adb-send-command-and-check 492 ;; Examine `file-attributes' cache to see if request can be
493 v (format "test -x %s" (tramp-shell-quote-argument localname)))))) 493 ;; satisfied without remote operation.
494 (if (tramp-file-property-p v localname "file-attributes")
495 (or (tramp-check-cached-permissions v ?x)
496 (tramp-check-cached-permissions v ?s))
497 (tramp-adb-send-command-and-check
498 v (format "test -x %s" (tramp-shell-quote-argument localname)))))))
499
500(defun tramp-adb-handle-file-exists-p (filename)
501 "Like `file-exists-p' for Tramp files."
502 ;; `file-exists-p' is used as predicate in file name completion.
503 ;; We don't want to run it when `non-essential' is t, or there is
504 ;; no connection process yet.
505 (when (tramp-connectable-p filename)
506 (with-parsed-tramp-file-name filename nil
507 (with-tramp-file-property v localname "file-exists-p"
508 (if (tramp-file-property-p v localname "file-attributes")
509 (not (null (tramp-get-file-property v localname "file-attributes")))
510 (tramp-adb-send-command-and-check
511 v (format "test -e %s" (tramp-shell-quote-argument localname))))))))
494 512
495(defun tramp-adb-handle-file-readable-p (filename) 513(defun tramp-adb-handle-file-readable-p (filename)
496 "Like `file-readable-p' for Tramp files." 514 "Like `file-readable-p' for Tramp files."
497 (with-parsed-tramp-file-name filename nil 515 (with-parsed-tramp-file-name filename nil
498 (with-tramp-file-property v localname "file-readable-p" 516 (with-tramp-file-property v localname "file-readable-p"
499 (or (tramp-handle-file-readable-p filename) 517 ;; Examine `file-attributes' cache to see if request can be
500 (tramp-adb-send-command-and-check 518 ;; satisfied without remote operation.
501 v (format "test -r %s" (tramp-shell-quote-argument localname))))))) 519 (if (tramp-file-property-p v localname "file-attributes")
520 (tramp-handle-file-readable-p filename)
521 (tramp-adb-send-command-and-check
522 v (format "test -r %s" (tramp-shell-quote-argument localname)))))))
502 523
503(defun tramp-adb-handle-file-writable-p (filename) 524(defun tramp-adb-handle-file-writable-p (filename)
504 "Like `file-writable-p' for Tramp files." 525 "Like `file-writable-p' for Tramp files."
505 (with-parsed-tramp-file-name filename nil 526 (with-parsed-tramp-file-name filename nil
506 (with-tramp-file-property v localname "file-writable-p" 527 (with-tramp-file-property v localname "file-writable-p"
507 (if (file-exists-p filename) 528 (if (file-exists-p filename)
508 (tramp-adb-send-command-and-check 529 (if (tramp-file-property-p v localname "file-attributes")
509 v (format "test -w %s" (tramp-shell-quote-argument localname))) 530 ;; Examine `file-attributes' cache to see if request can
531 ;; be satisfied without remote operation.
532 (tramp-check-cached-permissions v ?w)
533 (tramp-adb-send-command-and-check
534 v (format "test -w %s" (tramp-shell-quote-argument localname))))
535 ;; If file doesn't exist, check if directory is writable.
510 (and 536 (and
511 (file-directory-p (file-name-directory filename)) 537 (file-directory-p (file-name-directory filename))
512 (file-writable-p (file-name-directory filename))))))) 538 (file-writable-p (file-name-directory filename)))))))
@@ -1040,57 +1066,23 @@ implementation will be used."
1040(defun tramp-adb-handle-get-remote-uid (vec id-format) 1066(defun tramp-adb-handle-get-remote-uid (vec id-format)
1041 "Like `tramp-get-remote-uid' for Tramp files. 1067 "Like `tramp-get-remote-uid' for Tramp files.
1042 ID-FORMAT valid values are `string' and `integer'." 1068 ID-FORMAT valid values are `string' and `integer'."
1043 ;; The result is cached in `tramp-get-remote-uid'. 1069 (tramp-adb-send-command vec "id")
1044 (tramp-adb-send-command 1070 (tramp-read-id-output vec)
1045 vec 1071 (tramp-get-connection-property vec (format "uid-%s" id-format)))
1046 (format "id -u%s %s"
1047 (if (equal id-format 'integer) "" "n")
1048 (if (equal id-format 'integer)
1049 "" "| sed -e s/^/\\\"/ -e s/\\$/\\\"/")))
1050 (with-current-buffer (tramp-get-connection-buffer vec)
1051 ;; Read the expression.
1052 (goto-char (point-min))
1053 (read (current-buffer))))
1054 1072
1055(defun tramp-adb-handle-get-remote-gid (vec id-format) 1073(defun tramp-adb-handle-get-remote-gid (vec id-format)
1056 "Like `tramp-get-remote-gid' for Tramp files. 1074 "Like `tramp-get-remote-gid' for Tramp files.
1057ID-FORMAT valid values are `string' and `integer'." 1075ID-FORMAT valid values are `string' and `integer'."
1058 ;; The result is cached in `tramp-get-remote-gid'. 1076 (tramp-adb-send-command vec "id")
1059 (tramp-adb-send-command 1077 (tramp-read-id-output vec)
1060 vec 1078 (tramp-get-connection-property vec (format "gid-%s" id-format)))
1061 (format "id -g%s %s"
1062 (if (equal id-format 'integer) "" "n")
1063 (if (equal id-format 'integer)
1064 "" "| sed -e s/^/\\\"/ -e s/\\$/\\\"/")))
1065 (with-current-buffer (tramp-get-connection-buffer vec)
1066 ;; Read the expression.
1067 (goto-char (point-min))
1068 (read (current-buffer))))
1069 1079
1070(defun tramp-adb-handle-get-remote-groups (vec id-format) 1080(defun tramp-adb-handle-get-remote-groups (vec id-format)
1071 "Like `tramp-get-remote-groups' for Tramp files. 1081 "Like `tramp-get-remote-groups' for Tramp files.
1072ID-FORMAT valid values are `string' and `integer'." 1082ID-FORMAT valid values are `string' and `integer'."
1073 ;; The result is cached in `tramp-get-remote-groups'.
1074 (tramp-adb-send-command vec "id") 1083 (tramp-adb-send-command vec "id")
1075 (with-current-buffer (tramp-get-connection-buffer vec) 1084 (tramp-read-id-output vec)
1076 (let (groups-integer groups-string) 1085 (tramp-get-connection-property vec (format "groups-%s" id-format)))
1077 ;; Read the expression.
1078 (goto-char (point-min))
1079 (when (re-search-forward (rx bol (+ nonl) "groups=") nil 'noerror)
1080 (while (looking-at
1081 (rx (group (+ digit)) "(" (group (+ (any "_" word))) ")"))
1082 (setq groups-integer (cons (string-to-number (match-string 1))
1083 groups-integer)
1084 groups-string (cons (match-string 2) groups-string))
1085 (goto-char (match-end 0))
1086 (skip-chars-forward ",")))
1087 (tramp-set-connection-property
1088 vec "groups-integer"
1089 (setq groups-integer (nreverse groups-integer)))
1090 (tramp-set-connection-property
1091 vec "groups-string"
1092 (setq groups-string (nreverse groups-string)))
1093 (if (eq id-format 'integer) groups-integer groups-string))))
1094 1086
1095(defun tramp-adb-get-device (vec) 1087(defun tramp-adb-get-device (vec)
1096 "Return full host name from VEC to be used in shell execution. 1088 "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.")
783Format specifiers are replaced by `tramp-expand-script', percent 783Format specifiers are replaced by `tramp-expand-script', percent
784characters need to be doubled.") 784characters need to be doubled.")
785 785
786(defconst tramp-perl-id
787 "%p -e '
788use strict;
789use warnings;
790use POSIX qw(getgroups);
791
792my ($user, $passwd, $uid, $gid) = getpwuid $< ;
793my $group = getgrgid $gid ;
794my @groups = map { $_ . \"(\" . getgrgid ($_) . \")\" } getgroups ();
795
796printf \"uid=%%d(%%s) gid=%%d(%%s) groups=%%s\\n\",
797 $uid, $user, $gid, $group, join \",\", @groups;' %n"
798 "Perl script printing `id' output.
799Format specifiers are replaced by `tramp-expand-script', percent
800characters need to be doubled.")
801
802(defconst tramp-python-id
803 "%y -c '
804import os, pwd, grp;
805
806def idform(id):
807 return \"{:d}({:s})\".format(id, grp.getgrgid(id)[0]);
808
809uid = os.getuid();
810user = pwd.getpwuid(uid)[0];
811gid = os.getgid();
812group = grp.getgrgid(gid)[0]
813groups = map(idform, os.getgrouplist(user, gid));
814
815print(\"uid={:d}({:s}) gid={:d}({:s}) groups={:s}\"
816 .format(uid, user, gid, group, \",\".join(groups)));' %n"
817 "Python script printing `id' output.
818Format specifiers are replaced by `tramp-expand-script', percent
819characters need to be doubled.")
820
786;; These two use base64 encoding. 821;; These two use base64 encoding.
787(defconst tramp-perl-encode-with-module 822(defconst tramp-perl-encode-with-module
788 "%p -MMIME::Base64 -0777 -ne 'print encode_base64($_)' %n" 823 "%p -MMIME::Base64 -0777 -ne 'print encode_base64($_)' %n"
@@ -1524,10 +1559,16 @@ ID-FORMAT valid values are `string' and `integer'."
1524 ;; The result is cached in `tramp-get-remote-uid'. 1559 ;; The result is cached in `tramp-get-remote-uid'.
1525 (ignore-errors 1560 (ignore-errors
1526 (cond 1561 (cond
1527 ((tramp-get-remote-id vec) (tramp-get-remote-uid-with-id vec id-format)) 1562 ((tramp-get-remote-id vec)
1528 ((tramp-get-remote-perl vec) (tramp-get-remote-uid-with-perl vec id-format)) 1563 (tramp-send-command vec (tramp-get-remote-id vec)))
1564 ((tramp-get-remote-perl vec)
1565 (tramp-maybe-send-script vec tramp-perl-id "tramp_perl_id")
1566 (tramp-send-command vec "tramp_perl_id"))
1529 ((tramp-get-remote-python vec) 1567 ((tramp-get-remote-python vec)
1530 (tramp-get-remote-uid-with-python vec id-format))))) 1568 (tramp-maybe-send-script vec tramp-python-id "tramp_python_id")
1569 (tramp-send-command vec "tramp_python_id")))
1570 (tramp-read-id-output vec)
1571 (tramp-get-connection-property vec (format "uid-%s" id-format))))
1531 1572
1532(defun tramp-sh-handle-get-remote-gid (vec id-format) 1573(defun tramp-sh-handle-get-remote-gid (vec id-format)
1533 "The gid of the remote connection VEC, in ID-FORMAT. 1574 "The gid of the remote connection VEC, in ID-FORMAT.
@@ -1535,36 +1576,33 @@ ID-FORMAT valid values are `string' and `integer'."
1535 ;; The result is cached in `tramp-get-remote-gid'. 1576 ;; The result is cached in `tramp-get-remote-gid'.
1536 (ignore-errors 1577 (ignore-errors
1537 (cond 1578 (cond
1538 ((tramp-get-remote-id vec) (tramp-get-remote-gid-with-id vec id-format)) 1579 ((tramp-get-remote-id vec)
1539 ((tramp-get-remote-perl vec) (tramp-get-remote-gid-with-perl vec id-format)) 1580 (tramp-send-command vec (tramp-get-remote-id vec)))
1581 ((tramp-get-remote-perl vec)
1582 (tramp-maybe-send-script vec tramp-perl-id "tramp_perl_id")
1583 (tramp-send-command vec "tramp_perl_id"))
1540 ((tramp-get-remote-python vec) 1584 ((tramp-get-remote-python vec)
1541 (tramp-get-remote-gid-with-python vec id-format))))) 1585 (tramp-maybe-send-script vec tramp-python-id "tramp_python_id")
1586 (tramp-send-command vec "tramp_python_id")))
1587 (tramp-read-id-output vec)
1588 (tramp-get-connection-property vec (format "gid-%s" id-format))))
1542 1589
1543(defun tramp-sh-handle-get-remote-groups (vec id-format) 1590(defun tramp-sh-handle-get-remote-groups (vec id-format)
1544 "Like `tramp-get-remote-groups' for Tramp files. 1591 "Like `tramp-get-remote-groups' for Tramp files.
1545ID-FORMAT valid values are `string' and `integer'." 1592ID-FORMAT valid values are `string' and `integer'."
1546 ;; The result is cached in `tramp-get-remote-groups'. 1593 ;; The result is cached in `tramp-get-remote-groups'.
1547 (when (tramp-get-remote-id vec) 1594 (ignore-errors
1548 (tramp-send-command vec (tramp-get-remote-id vec))) 1595 (cond
1549 (with-current-buffer (tramp-get-connection-buffer vec) 1596 ((tramp-get-remote-id vec)
1550 (let (groups-integer groups-string) 1597 (tramp-send-command vec (tramp-get-remote-id vec)))
1551 ;; Read the expression. 1598 ((tramp-get-remote-perl vec)
1552 (goto-char (point-min)) 1599 (tramp-maybe-send-script vec tramp-perl-id "tramp_perl_id")
1553 (when (re-search-forward (rx bol (+ nonl) "groups=") nil 'noerror) 1600 (tramp-send-command vec "tramp_perl_id"))
1554 (while (looking-at 1601 ((tramp-get-remote-python vec)
1555 (rx (group (+ digit)) "(" (group (+ (any "_" word))) ")")) 1602 (tramp-maybe-send-script vec tramp-python-id "tramp_python_id")
1556 (setq groups-integer (cons (string-to-number (match-string 1)) 1603 (tramp-send-command vec "tramp_python_id")))
1557 groups-integer) 1604 (tramp-read-id-output vec)
1558 groups-string (cons (match-string 2) groups-string)) 1605 (tramp-get-connection-property vec (format "groups-%s" id-format))))
1559 (goto-char (match-end 0))
1560 (skip-chars-forward ",")))
1561 (tramp-set-connection-property
1562 vec "groups-integer"
1563 (setq groups-integer (nreverse groups-integer)))
1564 (tramp-set-connection-property
1565 vec "groups-string"
1566 (setq groups-string (nreverse groups-string)))
1567 (if (eq id-format 'integer) groups-integer groups-string))))
1568 1606
1569(defun tramp-sh-handle-set-file-uid-gid (filename &optional uid gid) 1607(defun tramp-sh-handle-set-file-uid-gid (filename &optional uid gid)
1570 "Like `tramp-set-file-uid-gid' for Tramp files." 1608 "Like `tramp-set-file-uid-gid' for Tramp files."
@@ -1694,6 +1732,8 @@ ID-FORMAT valid values are `string' and `integer'."
1694 "Like `file-readable-p' for Tramp files." 1732 "Like `file-readable-p' for Tramp files."
1695 (with-parsed-tramp-file-name filename nil 1733 (with-parsed-tramp-file-name filename nil
1696 (with-tramp-file-property v localname "file-readable-p" 1734 (with-tramp-file-property v localname "file-readable-p"
1735 ;; Examine `file-attributes' cache to see if request can be
1736 ;; satisfied without remote operation.
1697 (if (tramp-file-property-p v localname "file-attributes") 1737 (if (tramp-file-property-p v localname "file-attributes")
1698 (tramp-handle-file-readable-p filename) 1738 (tramp-handle-file-readable-p filename)
1699 (tramp-run-test "-r" filename))))) 1739 (tramp-run-test "-r" filename)))))
@@ -1730,8 +1770,9 @@ ID-FORMAT valid values are `string' and `integer'."
1730 (tramp-check-cached-permissions v ?w) 1770 (tramp-check-cached-permissions v ?w)
1731 (tramp-run-test "-w" filename)) 1771 (tramp-run-test "-w" filename))
1732 ;; If file doesn't exist, check if directory is writable. 1772 ;; If file doesn't exist, check if directory is writable.
1733 (and (file-exists-p (file-name-directory filename)) 1773 (and
1734 (tramp-run-test "-w" (file-name-directory filename))))))) 1774 (file-directory-p (file-name-directory filename))
1775 (file-writable-p (file-name-directory filename)))))))
1735 1776
1736(defun tramp-sh-handle-file-ownership-preserved-p (filename &optional group) 1777(defun tramp-sh-handle-file-ownership-preserved-p (filename &optional group)
1737 "Like `file-ownership-preserved-p' for Tramp files." 1778 "Like `file-ownership-preserved-p' for Tramp files."
@@ -3971,15 +4012,15 @@ Fall back to normal file name handler if no Tramp handler exists."
3971 4012
3972(defun tramp-expand-script (vec script) 4013(defun tramp-expand-script (vec script)
3973 "Expand SCRIPT with remote files or commands. 4014 "Expand SCRIPT with remote files or commands.
3974\"%a\", \"%h\", \"%l\", \"%o\", \"%p\", \"%r\" and \"%s\" format 4015\"%a\", \"%h\", \"%l\", \"%o\", \"%p\", \"%r\", \"%s\" and \"%y\"
3975specifiers are replaced by the respective `awk', `hexdump', `ls', 4016format specifiers are replaced by the respective `awk',
3976`od', `perl', `readlink' and `stat' commands. \"%n\" is replaced 4017`hexdump', `ls', `od', `perl', `readlink', `stat' and `python'
3977by \"2>/dev/null\", and \"%t\" is replaced by a temporary file 4018commands. \"%n\" is replaced by \"2>/dev/null\", and \"%t\" is
3978name. If VEC is nil, the respective local commands are used. If 4019replaced by a temporary file name. If VEC is nil, the respective
3979there is a format specifier which cannot be expanded, this 4020local commands are used. If there is a format specifier which
3980function returns nil." 4021cannot be expanded, this function returns nil."
3981 (if (not (string-match-p 4022 (if (not (string-match-p
3982 (rx (| bol (not (any "%"))) "%" (any "ahlnoprst")) script)) 4023 (rx (| bol (not (any "%"))) "%" (any "ahlnoprsty")) script))
3983 script 4024 script
3984 (catch 'wont-work 4025 (catch 'wont-work
3985 (let ((awk (when (string-match-p (rx (| bol (not (any "%"))) "%a") script) 4026 (let ((awk (when (string-match-p (rx (| bol (not (any "%"))) "%a") script)
@@ -4010,6 +4051,11 @@ function returns nil."
4010 (if vec 4051 (if vec
4011 (tramp-get-remote-perl vec) (executable-find "perl")) 4052 (tramp-get-remote-perl vec) (executable-find "perl"))
4012 (throw 'wont-work nil)))) 4053 (throw 'wont-work nil))))
4054 (python (when (string-match-p (rx (| bol (not (any "%"))) "%y") script)
4055 (or
4056 (if vec
4057 (tramp-get-remote-python vec) (executable-find "python"))
4058 (throw 'wont-work nil))))
4013 (readlink (when (string-match-p 4059 (readlink (when (string-match-p
4014 (rx (| bol (not (any "%"))) "%r") script) 4060 (rx (| bol (not (any "%"))) "%r") script)
4015 (or 4061 (or
@@ -4032,7 +4078,7 @@ function returns nil."
4032 script 4078 script
4033 (format-spec-make 4079 (format-spec-make
4034 ?a awk ?h hdmp ?l ls ?n dev ?o od ?p perl 4080 ?a awk ?h hdmp ?l ls ?n dev ?o od ?p perl
4035 ?r readlink ?s stat ?t tmp)))))) 4081 ?r readlink ?s stat ?t tmp ?y python))))))
4036 4082
4037(defun tramp-maybe-send-script (vec script name) 4083(defun tramp-maybe-send-script (vec script name)
4038 "Define in remote shell function NAME implemented as SCRIPT. 4084 "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."
5816 (while (and dl (setq result (tramp-find-executable vec cmd dl t t))) 5862 (while (and dl (setq result (tramp-find-executable vec cmd dl t t)))
5817 ;; Check POSIX parameter. 5863 ;; Check POSIX parameter.
5818 (when (tramp-send-command-and-check vec (format "%s -u" result)) 5864 (when (tramp-send-command-and-check vec (format "%s -u" result))
5819 (tramp-set-connection-property
5820 vec "uid-integer"
5821 (with-current-buffer (tramp-get-connection-buffer vec)
5822 (goto-char (point-min))
5823 (read (current-buffer))))
5824 (throw 'id-found result)) 5865 (throw 'id-found result))
5825 (setq dl (cdr dl)))))))) 5866 (setq dl (cdr dl))))))))
5826 5867
5827(defun tramp-get-remote-uid-with-id (vec id-format)
5828 "Implement `tramp-get-remote-uid' for Tramp files using `id'."
5829 ;; `tramp-get-remote-id' sets already connection property "uid-integer".
5830 (with-tramp-connection-property vec (format "uid-%s" id-format)
5831 (tramp-send-command-and-read
5832 vec
5833 (format "%s -u%s %s"
5834 (tramp-get-remote-id vec)
5835 (if (equal id-format 'integer) "" "n")
5836 (if (equal id-format 'integer)
5837 "" "| sed -e s/^/\\\"/ -e s/\\$/\\\"/")))))
5838
5839(defun tramp-get-remote-uid-with-perl (vec id-format)
5840 "Implement `tramp-get-remote-uid' for Tramp files using a Perl script."
5841 (tramp-send-command-and-read
5842 vec
5843 (format "%s -le '%s'"
5844 (tramp-get-remote-perl vec)
5845 (if (equal id-format 'integer)
5846 "print $>"
5847 "print \"\\\"\", scalar getpwuid($>), \"\\\"\""))))
5848
5849(defun tramp-get-remote-python (vec) 5868(defun tramp-get-remote-python (vec)
5850 "Determine remote `python' command." 5869 "Determine remote `python' command."
5851 (with-tramp-connection-property vec "python" 5870 (with-tramp-connection-property vec "python"
@@ -5853,46 +5872,6 @@ This command is returned only if `delete-by-moving-to-trash' is non-nil."
5853 (or (tramp-find-executable vec "python" (tramp-get-remote-path vec)) 5872 (or (tramp-find-executable vec "python" (tramp-get-remote-path vec))
5854 (tramp-find-executable vec "python3" (tramp-get-remote-path vec))))) 5873 (tramp-find-executable vec "python3" (tramp-get-remote-path vec)))))
5855 5874
5856(defun tramp-get-remote-uid-with-python (vec id-format)
5857 "Implement `tramp-get-remote-uid' for Tramp files using `python'."
5858 (tramp-send-command-and-read
5859 vec
5860 (format "%s -c \"%s\""
5861 (tramp-get-remote-python vec)
5862 (if (equal id-format 'integer)
5863 "import os; print (os.getuid())"
5864 "import os, pwd; print ('\\\"' + pwd.getpwuid(os.getuid())[0] + '\\\"')"))))
5865
5866(defun tramp-get-remote-gid-with-id (vec id-format)
5867 "Implement `tramp-get-remote-gid' for Tramp files using `id'."
5868 (tramp-send-command-and-read
5869 vec
5870 (format "%s -g%s %s"
5871 (tramp-get-remote-id vec)
5872 (if (equal id-format 'integer) "" "n")
5873 (if (equal id-format 'integer)
5874 "" "| sed -e s/^/\\\"/ -e s/\\$/\\\"/"))))
5875
5876(defun tramp-get-remote-gid-with-perl (vec id-format)
5877 "Implement `tramp-get-remote-gid' for Tramp files using a Perl script."
5878 (tramp-send-command-and-read
5879 vec
5880 (format "%s -le '%s'"
5881 (tramp-get-remote-perl vec)
5882 (if (equal id-format 'integer)
5883 "print ($)=~/(\\d+)/)"
5884 "print \"\\\"\", scalar getgrgid($)), \"\\\"\""))))
5885
5886(defun tramp-get-remote-gid-with-python (vec id-format)
5887 "Implement `tramp-get-remote-gid' for Tramp files using `python'."
5888 (tramp-send-command-and-read
5889 vec
5890 (format "%s -c \"%s\""
5891 (tramp-get-remote-python vec)
5892 (if (equal id-format 'integer)
5893 "import os; print (os.getgid())"
5894 "import os, grp; print ('\\\"' + grp.getgrgid(os.getgid())[0] + '\\\"')"))))
5895
5896(defun tramp-get-remote-busybox (vec) 5875(defun tramp-get-remote-busybox (vec)
5897 "Determine remote `busybox' command." 5876 "Determine remote `busybox' command."
5898 (with-tramp-connection-property vec "busybox" 5877 (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."
442 "Like `file-executable-p' for Tramp files." 442 "Like `file-executable-p' for Tramp files."
443 (with-parsed-tramp-file-name filename nil 443 (with-parsed-tramp-file-name filename nil
444 (with-tramp-file-property v localname "file-executable-p" 444 (with-tramp-file-property v localname "file-executable-p"
445 (tramp-sudoedit-send-command 445 ;; Examine `file-attributes' cache to see if request can be
446 v "test" "-x" (tramp-compat-file-name-unquote localname))))) 446 ;; satisfied without remote operation.
447 (if (tramp-file-property-p v localname "file-attributes")
448 (or (tramp-check-cached-permissions v ?x)
449 (tramp-check-cached-permissions v ?s))
450 (tramp-sudoedit-send-command
451 v "test" "-x" (tramp-compat-file-name-unquote localname))))))
447 452
448(defun tramp-sudoedit-handle-file-exists-p (filename) 453(defun tramp-sudoedit-handle-file-exists-p (filename)
449 "Like `file-exists-p' for Tramp files." 454 "Like `file-exists-p' for Tramp files."
@@ -453,8 +458,10 @@ the result will be a local, non-Tramp, file name."
453 (when (tramp-connectable-p filename) 458 (when (tramp-connectable-p filename)
454 (with-parsed-tramp-file-name filename nil 459 (with-parsed-tramp-file-name filename nil
455 (with-tramp-file-property v localname "file-exists-p" 460 (with-tramp-file-property v localname "file-exists-p"
456 (tramp-sudoedit-send-command 461 (if (tramp-file-property-p v localname "file-attributes")
457 v "test" "-e" (tramp-compat-file-name-unquote localname)))))) 462 (not (null (tramp-get-file-property v localname "file-attributes")))
463 (tramp-sudoedit-send-command
464 v "test" "-e" (tramp-compat-file-name-unquote localname)))))))
458 465
459(defun tramp-sudoedit-handle-file-name-all-completions (filename directory) 466(defun tramp-sudoedit-handle-file-name-all-completions (filename directory)
460 "Like `file-name-all-completions' for Tramp files." 467 "Like `file-name-all-completions' for Tramp files."
@@ -483,9 +490,12 @@ the result will be a local, non-Tramp, file name."
483 "Like `file-readable-p' for Tramp files." 490 "Like `file-readable-p' for Tramp files."
484 (with-parsed-tramp-file-name filename nil 491 (with-parsed-tramp-file-name filename nil
485 (with-tramp-file-property v localname "file-readable-p" 492 (with-tramp-file-property v localname "file-readable-p"
486 (or (tramp-handle-file-readable-p filename) 493 ;; Examine `file-attributes' cache to see if request can be
487 (tramp-sudoedit-send-command 494 ;; satisfied without remote operation.
488 v "test" "-r" (tramp-compat-file-name-unquote localname)))))) 495 (if (tramp-file-property-p v localname "file-attributes")
496 (tramp-handle-file-readable-p filename)
497 (tramp-sudoedit-send-command
498 v "test" "-r" (tramp-compat-file-name-unquote localname))))))
489 499
490(defun tramp-sudoedit-handle-set-file-modes (filename mode &optional flag) 500(defun tramp-sudoedit-handle-set-file-modes (filename mode &optional flag)
491 "Like `set-file-modes' for Tramp files." 501 "Like `set-file-modes' for Tramp files."
@@ -597,11 +607,16 @@ the result will be a local, non-Tramp, file name."
597 (with-parsed-tramp-file-name filename nil 607 (with-parsed-tramp-file-name filename nil
598 (with-tramp-file-property v localname "file-writable-p" 608 (with-tramp-file-property v localname "file-writable-p"
599 (if (file-exists-p filename) 609 (if (file-exists-p filename)
600 (tramp-sudoedit-send-command 610 (if (tramp-file-property-p v localname "file-attributes")
601 v "test" "-w" (tramp-compat-file-name-unquote localname)) 611 ;; Examine `file-attributes' cache to see if request can
602 (let ((dir (file-name-directory filename))) 612 ;; be satisfied without remote operation.
603 (and (file-exists-p dir) 613 (tramp-check-cached-permissions v ?w)
604 (file-writable-p dir))))))) 614 (tramp-sudoedit-send-command
615 v "test" "-w" (tramp-compat-file-name-unquote localname)))
616 ;; If file doesn't exist, check if directory is writable.
617 (and
618 (file-directory-p (file-name-directory filename))
619 (file-writable-p (file-name-directory filename)))))))
605 620
606(defun tramp-sudoedit-handle-make-directory (dir &optional parents) 621(defun tramp-sudoedit-handle-make-directory (dir &optional parents)
607 "Like `make-directory' for Tramp files." 622 "Like `make-directory' for Tramp files."
@@ -720,43 +735,23 @@ VEC or USER, or if there is no home directory, return nil."
720(defun tramp-sudoedit-handle-get-remote-uid (vec id-format) 735(defun tramp-sudoedit-handle-get-remote-uid (vec id-format)
721 "The uid of the remote connection VEC, in ID-FORMAT. 736 "The uid of the remote connection VEC, in ID-FORMAT.
722ID-FORMAT valid values are `string' and `integer'." 737ID-FORMAT valid values are `string' and `integer'."
723 ;; The result is cached in `tramp-get-remote-uid'. 738 (tramp-sudoedit-send-command vec "id")
724 (if (equal id-format 'integer) 739 (tramp-read-id-output vec)
725 (tramp-sudoedit-send-command-and-read vec "id" "-u") 740 (tramp-get-connection-property vec (format "uid-%s" id-format)))
726 (tramp-sudoedit-send-command-string vec "id" "-un")))
727 741
728(defun tramp-sudoedit-handle-get-remote-gid (vec id-format) 742(defun tramp-sudoedit-handle-get-remote-gid (vec id-format)
729 "The gid of the remote connection VEC, in ID-FORMAT. 743 "The gid of the remote connection VEC, in ID-FORMAT.
730ID-FORMAT valid values are `string' and `integer'." 744ID-FORMAT valid values are `string' and `integer'."
731 ;; The result is cached in `tramp-get-remote-gid'. 745 (tramp-sudoedit-send-command vec "id")
732 (if (equal id-format 'integer) 746 (tramp-read-id-output vec)
733 (tramp-sudoedit-send-command-and-read vec "id" "-g") 747 (tramp-get-connection-property vec (format "gid-%s" id-format)))
734 (tramp-sudoedit-send-command-string vec "id" "-gn")))
735 748
736(defun tramp-sudoedit-handle-get-remote-groups (vec id-format) 749(defun tramp-sudoedit-handle-get-remote-groups (vec id-format)
737 "Like `tramp-get-remote-groups' for Tramp files. 750 "Like `tramp-get-remote-groups' for Tramp files.
738ID-FORMAT valid values are `string' and `integer'." 751ID-FORMAT valid values are `string' and `integer'."
739 ;; The result is cached in `tramp-get-remote-groups'.
740 (tramp-sudoedit-send-command vec "id") 752 (tramp-sudoedit-send-command vec "id")
741 (with-current-buffer (tramp-get-connection-buffer vec) 753 (tramp-read-id-output vec)
742 (let (groups-integer groups-string) 754 (tramp-get-connection-property vec (format "groups-%s" id-format)))
743 ;; Read the expression.
744 (goto-char (point-min))
745 (when (re-search-forward (rx bol (+ nonl) "groups=") nil 'noerror)
746 (while (looking-at
747 (rx (group (+ digit)) "(" (group (+ (any "_" word))) ")"))
748 (setq groups-integer (cons (string-to-number (match-string 1))
749 groups-integer)
750 groups-string (cons (match-string 2) groups-string))
751 (goto-char (match-end 0))
752 (skip-chars-forward ",")))
753 (tramp-set-connection-property
754 vec "groups-integer"
755 (setq groups-integer (nreverse groups-integer)))
756 (tramp-set-connection-property
757 vec "groups-string"
758 (setq groups-string (nreverse groups-string)))
759 (if (eq id-format 'integer) groups-integer groups-string))))
760 755
761(defun tramp-sudoedit-handle-set-file-uid-gid (filename &optional uid gid) 756(defun tramp-sudoedit-handle-set-file-uid-gid (filename &optional uid gid)
762 "Like `tramp-set-file-uid-gid' for Tramp files." 757 "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."
5838 ((eq ?s access) 3))) 5838 ((eq ?s access) 3)))
5839 (file-attr (file-attributes (tramp-make-tramp-file-name vec))) 5839 (file-attr (file-attributes (tramp-make-tramp-file-name vec)))
5840 (remote-uid (tramp-get-remote-uid vec 'integer)) 5840 (remote-uid (tramp-get-remote-uid vec 'integer))
5841 (remote-gid (tramp-get-remote-gid vec 'integer)) 5841 (remote-gid (tramp-get-remote-gid vec 'integer)))
5842 (remote-groups (tramp-get-remote-groups vec 'integer)))
5843 (or 5842 (or
5844 ;; Not a symlink. 5843 ;; Not a symlink.
5845 (eq t (file-attribute-type file-attr)) 5844 (eq t (file-attribute-type file-attr))
@@ -5867,7 +5866,8 @@ be granted."
5867 (and 5866 (and
5868 (eq access 5867 (eq access
5869 (aref (file-attribute-modes file-attr) (+ offset 3))) 5868 (aref (file-attribute-modes file-attr) (+ offset 3)))
5870 (member (file-attribute-group-id file-attr) remote-groups))))) 5869 (member (file-attribute-group-id file-attr)
5870 (tramp-get-remote-groups vec 'integer))))))
5871 5871
5872(defmacro tramp-convert-file-attributes (vec localname id-format attr) 5872(defmacro tramp-convert-file-attributes (vec localname id-format attr)
5873 "Convert `file-attributes' ATTR generated Tramp backend functions. 5873 "Convert `file-attributes' ATTR generated Tramp backend functions.
@@ -6008,12 +6008,48 @@ ID-FORMAT valid values are `string' and `integer'."
6008(defun tramp-get-remote-groups (vec id-format) 6008(defun tramp-get-remote-groups (vec id-format)
6009 "The list of groups of the remote connection VEC, in ID-FORMAT. 6009 "The list of groups of the remote connection VEC, in ID-FORMAT.
6010ID-FORMAT valid values are `string' and `integer'." 6010ID-FORMAT valid values are `string' and `integer'."
6011 (or (and (tramp-file-name-p vec) 6011 (and (tramp-file-name-p vec)
6012 (with-tramp-connection-property vec (format "groups-%s" id-format) 6012 (with-tramp-connection-property vec (format "groups-%s" id-format)
6013 (tramp-file-name-handler #'tramp-get-remote-groups vec id-format))) 6013 (tramp-file-name-handler #'tramp-get-remote-groups vec id-format))))
6014 ;; Ensure there is a valid result. 6014
6015 (and (equal id-format 'integer) (list tramp-unknown-id-integer)) 6015(defun tramp-read-id-output (vec)
6016 (and (equal id-format 'string) (list tramp-unknown-id-string)))) 6016 "Read in connection buffer the output of the `id' command.
6017Set connection properties \"{uid,gid.groups}-{integer,string}\"."
6018 (with-current-buffer (tramp-get-connection-buffer vec)
6019 (let (uid-integer uid-string
6020 gid-integer gid-string
6021 groups-integer groups-string)
6022 (goto-char (point-min))
6023 ;; Read uid.
6024 (when (re-search-forward
6025 (rx "uid=" (group (+ digit)) "(" (group (+ (any "_" word))) ")")
6026 nil 'noerror)
6027 (setq uid-integer (string-to-number (match-string 1))
6028 uid-string (match-string 2)))
6029 ;; Read gid.
6030 (when (re-search-forward
6031 (rx "gid=" (group (+ digit)) "(" (group (+ (any "_" word))) ")")
6032 nil 'noerror)
6033 (setq gid-integer (string-to-number (match-string 1))
6034 gid-string (match-string 2)))
6035 ;; Read groups.
6036 (when (re-search-forward (rx "groups=") nil 'noerror)
6037 (while (looking-at
6038 (rx (group (+ digit)) "(" (group (+ (any "_" word))) ")"))
6039 (setq groups-integer (cons (string-to-number (match-string 1))
6040 groups-integer)
6041 groups-string (cons (match-string 2) groups-string))
6042 (goto-char (match-end 0))
6043 (skip-chars-forward ",")))
6044 ;; Set connection properties.
6045 (tramp-set-connection-property vec "uid-integer" uid-integer)
6046 (tramp-set-connection-property vec "uid-string" uid-string)
6047 (tramp-set-connection-property vec "gid-integer" gid-integer)
6048 (tramp-set-connection-property vec "gid-string" gid-string)
6049 (tramp-set-connection-property
6050 vec "groups-integer" (nreverse groups-integer))
6051 (tramp-set-connection-property
6052 vec "groups-string" (nreverse groups-string)))))
6017 6053
6018(defun tramp-local-host-p (vec) 6054(defun tramp-local-host-p (vec)
6019 "Return t if this points to the local host, nil otherwise. 6055 "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',
3622 (append 3622 (append
3623 '((nil "stat" nil) 3623 '((nil "stat" nil)
3624 ;; See `tramp-sh-handle-file-truename'. 3624 ;; See `tramp-sh-handle-file-truename'.
3625 (nil "readlink" nil)) 3625 (nil "readlink" nil)
3626 ;; See `tramp-sh-handle-get-remote-*'.
3627 (nil "id" nil))
3626 tramp-connection-properties))) 3628 tramp-connection-properties)))
3627 (progn 3629 (progn
3628 (skip-unless (< (ert-test-result-duration result) 300)) 3630 (skip-unless (< (ert-test-result-duration result) 300))