From 64af3c94a6197cd0c6a283880c900eeb5bf12961 Mon Sep 17 00:00:00 2001 From: Michael Albinus Date: Tue, 25 Feb 2020 13:25:57 +0100 Subject: [PATCH] Finish implementation of {set-}file-modes FLAG arg in Tramp * lisp/net/tramp-adb.el (tramp-adb-handle-file-local-copy): Do not use 'nofollow for temporary files. Use `tramp-compat-set-file-modes'. (tramp-adb-handle-write-region): Do not use 'nofollow for temporary files. (tramp-adb-handle-set-file-modes): Implement FLAG. * lisp/net/tramp-compat.el (tramp-compat-file-modes) (tramp-compat-set-file-modes): New defaliases. * lisp/net/tramp-gvfs.el (tramp-gvfs-handle-set-file-modes): Make explicit check (eq flag 'nofollow). * lisp/net/tramp-sh.el (tramp-sh-handle-set-file-modes): Implement FLAG. (tramp-do-copy-or-rename-file-directly) (tramp-sh-handle-file-local-copy, tramp-sh-handle-write-region): Do not use 'nofollow for temporary files. (tramp-get-remote-chmod-h): New defun. * lisp/net/tramp-smb.el (tramp-smb-handle-set-file-modes): Implement FLAG. * lisp/net/tramp-sudoedit.el (tramp-sudoedit-handle-set-file-modes): Implement FLAG. (tramp-sudoedit-handle-write-region): Use `tramp-compat-set-file-modes'. * lisp/net/tramp.el (tramp-default-file-modes): Optional argument FLAG. (tramp-handle-file-modes): Use `file-truename' instead of `file-chase-links'. The latter function does not work for remote file names. (tramp-handle-write-region): Call `tramp-default-file-modes' with 'nofollow if needed. Do not use 'nofollow for temporary files. * test/lisp/net/tramp-tests.el (tramp--test-ignore-make-symbolic-link-error): Check also for "Cannot chmod .* with nofollow flag" error. (tramp-test20-file-modes): Extend test. (tramp--test-emacs28-p): New defun. --- lisp/net/tramp-adb.el | 11 ++--- lisp/net/tramp-compat.el | 22 +++++++-- lisp/net/tramp-gvfs.el | 2 +- lisp/net/tramp-sh.el | 44 +++++++++++++----- lisp/net/tramp-smb.el | 3 +- lisp/net/tramp-sudoedit.el | 21 ++++----- lisp/net/tramp.el | 27 ++++++----- test/lisp/net/tramp-tests.el | 86 +++++++++++++++++++++++++++++------- 8 files changed, 156 insertions(+), 60 deletions(-) diff --git a/lisp/net/tramp-adb.el b/lisp/net/tramp-adb.el index 96ef95dbe30..a118e7d2143 100644 --- a/lisp/net/tramp-adb.el +++ b/lisp/net/tramp-adb.el @@ -591,8 +591,9 @@ Emacs dired can't find files." (ignore-errors (delete-file tmpfile)) (tramp-error v 'file-error "Cannot make local copy of file `%s'" filename)) - (set-file-modes tmpfile (logior (or (file-modes filename) 0) #o0400) - 'nofollow)) + (set-file-modes + tmpfile + (logior (or (tramp-compat-file-modes filename 'nofollow) 0) #o0400))) tmpfile))) (defun tramp-adb-handle-file-writable-p (filename) @@ -637,8 +638,7 @@ But handle the case, if the \"test\" command is not available." (tmpfile (tramp-compat-make-temp-file filename))) (when (and append (file-exists-p filename)) (copy-file filename tmpfile 'ok) - (set-file-modes tmpfile (logior (or (file-modes tmpfile) 0) #o0600) - 'nofollow)) + (set-file-modes tmpfile (logior (or (file-modes tmpfile) 0) #o0600))) (tramp-run-real-handler #'write-region (list start end tmpfile append 'no-message lockname)) (with-tramp-progress-reporter @@ -669,8 +669,9 @@ But handle the case, if the \"test\" command is not available." (defun tramp-adb-handle-set-file-modes (filename mode &optional flag) "Like `set-file-modes' for Tramp files." - flag ;; FIXME: Support 'nofollow'. (with-parsed-tramp-file-name filename nil + (when (and (eq flag 'nofollow) (file-symlink-p filename)) + (tramp-error v 'file-error "Cannot chmod %s with %s flag" filename flag)) (tramp-flush-file-properties v localname) (tramp-adb-send-command-and-check v (format "chmod %o %s" mode localname)))) diff --git a/lisp/net/tramp-compat.el b/lisp/net/tramp-compat.el index 87bcd08b85a..8f85550bca0 100644 --- a/lisp/net/tramp-compat.el +++ b/lisp/net/tramp-compat.el @@ -165,7 +165,7 @@ This is a string of ten letters or dashes as in ls -l." "The error symbol for the `file-missing' error.") ;; `file-local-name', `file-name-quoted-p', `file-name-quote' and -;; `file-name-unquote' are introduced in Emacs 26. +;; `file-name-unquote' are introduced in Emacs 26.1. (defalias 'tramp-compat-file-local-name (if (fboundp 'file-local-name) #'file-local-name @@ -175,7 +175,8 @@ It returns a file name which can be used directly as argument of `process-file', `start-file-process', or `shell-command'." (or (file-remote-p name 'localname) name)))) -;; `file-name-quoted-p' got a second argument in Emacs 27.1. +;; `file-name-quoted-p', `file-name-quote' and `file-name-unquote' got +;; a second argument in Emacs 27.1. (defalias 'tramp-compat-file-name-quoted-p (if (and (fboundp 'file-name-quoted-p) @@ -217,7 +218,7 @@ NAME is unquoted." localname (if (= (length localname) 2) "/" (substring localname 2)))) (concat (file-remote-p name) localname))))) -;; `tramp-syntax' has changed its meaning in Emacs 26. We still +;; `tramp-syntax' has changed its meaning in Emacs 26.1. We still ;; support old settings. (defsubst tramp-compat-tramp-syntax () "Return proper value of `tramp-syntax'." @@ -275,6 +276,19 @@ A nil value for either argument stands for the current time." (lambda (reporter &optional value _suffix) (progress-reporter-update reporter value)))) +;; `file-modes' and `set-file-modes' got argument FLAG in Emacs 28.1. +(defalias 'tramp-compat-file-modes + (if (equal (tramp-compat-funcall 'func-arity #'file-modes) '(1 . 2)) + #'file-modes + (lambda (filename &optional _flag) + (file-modes filename)))) + +(defalias 'tramp-compat-set-file-modes + (if (equal (tramp-compat-funcall 'func-arity #'set-file-modes) '(2 . 3)) + #'set-file-modes + (lambda (filename mode &optional _flag) + (set-file-modes filename mode)))) + (add-hook 'tramp-unload-hook (lambda () (unload-feature 'tramp-loaddefs 'force) @@ -284,6 +298,8 @@ A nil value for either argument stands for the current time." ;;; TODO: ;; +;; * `func-arity' exists since Emacs 26.1. +;; ;; * Starting with Emacs 27.1, there's no need to escape open ;; parentheses with a backslash in docstrings anymore. diff --git a/lisp/net/tramp-gvfs.el b/lisp/net/tramp-gvfs.el index 79835804bc0..3ce7bbbd4a3 100644 --- a/lisp/net/tramp-gvfs.el +++ b/lisp/net/tramp-gvfs.el @@ -1567,7 +1567,7 @@ If FILE-SYSTEM is non-nil, return file system attributes." (with-parsed-tramp-file-name filename nil (tramp-flush-file-properties v localname) (tramp-gvfs-send-command - v "gvfs-set-attribute" (if flag "-nt" "-t") "uint32" + v "gvfs-set-attribute" (if (eq flag 'nofollow) "-nt" "-t") "uint32" (tramp-gvfs-url-file-name (tramp-make-tramp-file-name v)) "unix::mode" (number-to-string mode)))) diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el index f31d3615884..761f594b6b9 100644 --- a/lisp/net/tramp-sh.el +++ b/lisp/net/tramp-sh.el @@ -1481,13 +1481,17 @@ of." (defun tramp-sh-handle-set-file-modes (filename mode &optional flag) "Like `set-file-modes' for Tramp files." (with-parsed-tramp-file-name filename nil - (tramp-flush-file-properties v localname) - flag ;; FIXME: Support 'nofollow'. - ;; FIXME: extract the proper text from chmod's stderr. - (tramp-barf-unless-okay - v - (format "chmod %o %s" mode (tramp-shell-quote-argument localname)) - "Error while changing file's mode %s" filename))) + (let ((chmod "chmod")) + (when (and (eq flag 'nofollow) (file-symlink-p filename)) + (or (setq chmod (tramp-get-remote-chmod-h v)) + (tramp-error + v 'file-error "Cannot chmod %s with %s flag" filename flag))) + (tramp-flush-file-properties v localname) + ;; FIXME: extract the proper text from chmod's stderr. + (tramp-barf-unless-okay + v + (format "%s %o %s" chmod mode (tramp-shell-quote-argument localname)) + "Error while changing file's mode %s" filename)))) (defun tramp-sh-handle-set-file-times (filename &optional time) "Like `set-file-times' for Tramp files." @@ -2280,7 +2284,7 @@ the uid and gid from FILENAME." ;; We must change the ownership as local user. ;; Since this does not work reliable, we also ;; give read permissions. - (set-file-modes tmpfile #o0777 'nofollow) + (set-file-modes tmpfile #o0777) (tramp-set-file-uid-gid tmpfile (tramp-get-remote-uid v 'integer) @@ -3222,8 +3226,7 @@ STDERR can also be a file name." (delete-file tmpfile2))))) ;; Set proper permissions. - (set-file-modes tmpfile (tramp-default-file-modes filename) - 'nofollow) + (set-file-modes tmpfile (tramp-default-file-modes filename)) ;; Set local user ownership. (tramp-set-file-uid-gid tmpfile)) @@ -3272,7 +3275,8 @@ STDERR can also be a file name." #'write-region (list start end localname append 'no-message lockname)) - (let* ((modes (save-excursion (tramp-default-file-modes filename))) + (let* ((modes (tramp-default-file-modes + filename (and (eq mustbenew 'excl) 'nofollow))) ;; We use this to save the value of ;; `last-coding-system-used' after writing the tmp ;; file. At the end of the function, we set @@ -3322,7 +3326,7 @@ STDERR can also be a file name." ;; handles permissions. ;; Ensure that it is still readable. (when modes - (set-file-modes tmpfile (logior (or modes 0) #o0400) 'nofollow)) + (set-file-modes tmpfile (logior (or modes 0) #o0400))) ;; This is a bit lengthy due to the different methods ;; possible for file transfer. First, we check whether the @@ -5897,6 +5901,22 @@ ID-FORMAT valid values are `string' and `integer'." vec (concat command " -A n = emacs-major-version 27)) +(defun tramp--test-emacs28-p () + "Check for Emacs version >= 28.1. +Some semantics has been changed for there, w/o new functions or +variables, so we check the Emacs version directly." + (>= emacs-major-version 28)) + (defun tramp--test-adb-p () "Check, whether the remote host runs Android. This requires restrictions of file name syntax." -- 2.39.2