From: Michael Albinus Date: Sun, 23 Jun 2019 16:58:11 +0000 (+0200) Subject: Improve error handling in tramp-gvfs X-Git-Tag: emacs-27.0.90~2306 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=383a557b537562ceed38da3c9a07790c2f6b67f6;p=emacs.git Improve error handling in tramp-gvfs * lisp/net/tramp-gvfs.el (tramp-gvfs-get-directory-attributes) (tramp-gvfs-get-root-attributes) (tramp-gvfs-handle-file-attributes): Don't ignore errors. (tramp-make-goa-name): New defun. (tramp-gvfs-get-remote-prefix): Use it. (tramp-gvfs-maybe-open-connection): Raise user errors in case of. Check also, that GOA accounts are proper. (tramp-get-goa-accounts): Cache connection property. * lisp/net/tramp.el (tramp-handle-file-equal-p) (tramp-handle-file-in-directory-p): Use `tramp-equal-remote'. --- diff --git a/lisp/net/tramp-gvfs.el b/lisp/net/tramp-gvfs.el index 17c2e79833b..cee7a1209bd 100644 --- a/lisp/net/tramp-gvfs.el +++ b/lisp/net/tramp-gvfs.el @@ -933,76 +933,74 @@ file names." (defun tramp-gvfs-get-directory-attributes (directory) "Return GVFS attributes association list of all files in DIRECTORY." - (ignore-errors - ;; Don't modify `last-coding-system-used' by accident. - (let ((last-coding-system-used last-coding-system-used) - result) - (with-parsed-tramp-file-name directory nil - (with-tramp-file-property v localname "directory-attributes" - (tramp-message v 5 "directory gvfs attributes: %s" localname) - ;; Send command. - (tramp-gvfs-send-command - v "gvfs-ls" "-h" "-n" "-a" - (mapconcat #'identity tramp-gvfs-file-attributes ",") - (tramp-gvfs-url-file-name directory)) - ;; Parse output. - (with-current-buffer (tramp-get-connection-buffer v) - (goto-char (point-min)) - (while (looking-at - (concat "^\\(.+\\)[[:blank:]]" - "\\([[:digit:]]+\\)[[:blank:]]" - "(\\(.+?\\))" - tramp-gvfs-file-attributes-with-gvfs-ls-regexp)) - (let ((item (list (cons "type" (match-string 3)) - (cons "standard::size" (match-string 2)) - (cons "name" (match-string 1))))) - (goto-char (1+ (match-end 3))) - (while (looking-at - (concat - tramp-gvfs-file-attributes-with-gvfs-ls-regexp - "\\(" tramp-gvfs-file-attributes-with-gvfs-ls-regexp - "\\|" "$" "\\)")) - (push (cons (match-string 1) (match-string 2)) item) - (goto-char (match-end 2))) - ;; Add display name as head. - (push - (cons (cdr (or (assoc "standard::display-name" item) - (assoc "name" item))) - (nreverse item)) - result)) - (forward-line))) - result))))) + ;; Don't modify `last-coding-system-used' by accident. + (let ((last-coding-system-used last-coding-system-used) + result) + (with-parsed-tramp-file-name directory nil + (with-tramp-file-property v localname "directory-attributes" + (tramp-message v 5 "directory gvfs attributes: %s" localname) + ;; Send command. + (tramp-gvfs-send-command + v "gvfs-ls" "-h" "-n" "-a" + (mapconcat #'identity tramp-gvfs-file-attributes ",") + (tramp-gvfs-url-file-name directory)) + ;; Parse output. + (with-current-buffer (tramp-get-connection-buffer v) + (goto-char (point-min)) + (while (looking-at + (concat "^\\(.+\\)[[:blank:]]" + "\\([[:digit:]]+\\)[[:blank:]]" + "(\\(.+?\\))" + tramp-gvfs-file-attributes-with-gvfs-ls-regexp)) + (let ((item (list (cons "type" (match-string 3)) + (cons "standard::size" (match-string 2)) + (cons "name" (match-string 1))))) + (goto-char (1+ (match-end 3))) + (while (looking-at + (concat + tramp-gvfs-file-attributes-with-gvfs-ls-regexp + "\\(" tramp-gvfs-file-attributes-with-gvfs-ls-regexp + "\\|" "$" "\\)")) + (push (cons (match-string 1) (match-string 2)) item) + (goto-char (match-end 2))) + ;; Add display name as head. + (push + (cons (cdr (or (assoc "standard::display-name" item) + (assoc "name" item))) + (nreverse item)) + result)) + (forward-line))) + result)))) (defun tramp-gvfs-get-root-attributes (filename &optional file-system) "Return GVFS attributes association list of FILENAME. If FILE-SYSTEM is non-nil, return file system attributes." - (ignore-errors - ;; Don't modify `last-coding-system-used' by accident. - (let ((last-coding-system-used last-coding-system-used) - result) - (with-parsed-tramp-file-name filename nil - (with-tramp-file-property - v localname - (if file-system "file-system-attributes" "file-attributes") - (tramp-message - v 5 "file%s gvfs attributes: %s" - (if file-system " system" "") localname) - ;; Send command. - (if file-system - (tramp-gvfs-send-command - v "gvfs-info" "--filesystem" (tramp-gvfs-url-file-name filename)) + ;; Don't modify `last-coding-system-used' by accident. + (let ((last-coding-system-used last-coding-system-used) + result) + (with-parsed-tramp-file-name filename nil + (with-tramp-file-property + v localname + (if file-system "file-system-attributes" "file-attributes") + (tramp-message + v 5 "file%s gvfs attributes: %s" + (if file-system " system" "") localname) + ;; Send command. + (if file-system (tramp-gvfs-send-command - v "gvfs-info" (tramp-gvfs-url-file-name filename))) - ;; Parse output. - (with-current-buffer (tramp-get-connection-buffer v) - (goto-char (point-min)) - (while (re-search-forward - (if file-system - tramp-gvfs-file-system-attributes-regexp - tramp-gvfs-file-attributes-with-gvfs-info-regexp) - nil t) - (push (cons (match-string 1) (match-string 2)) result)) - result)))))) + v "gvfs-info" "--filesystem" (tramp-gvfs-url-file-name filename)) + (tramp-gvfs-send-command + v "gvfs-info" (tramp-gvfs-url-file-name filename))) + ;; Parse output. + (with-current-buffer (tramp-get-connection-buffer v) + (goto-char (point-min)) + (while (re-search-forward + (if file-system + tramp-gvfs-file-system-attributes-regexp + tramp-gvfs-file-attributes-with-gvfs-info-regexp) + nil t) + (push (cons (match-string 1) (match-string 2)) result)) + result))))) (defun tramp-gvfs-get-file-attributes (filename) "Return GVFS attributes association list of FILENAME." @@ -1020,123 +1018,122 @@ If FILE-SYSTEM is non-nil, return file system attributes." (defun tramp-gvfs-handle-file-attributes (filename &optional id-format) "Like `file-attributes' for Tramp files." (unless id-format (setq id-format 'integer)) - (ignore-errors - (let ((attributes (tramp-gvfs-get-file-attributes filename)) - dirp res-symlink-target res-numlinks res-uid res-gid res-access - res-mod res-change res-size res-filemodes res-inode res-device) - (when attributes - ;; ... directory or symlink - (setq dirp (if (equal "directory" (cdr (assoc "type" attributes))) t)) + (let ((attributes (tramp-gvfs-get-file-attributes filename)) + dirp res-symlink-target res-numlinks res-uid res-gid res-access + res-mod res-change res-size res-filemodes res-inode res-device) + (when attributes + ;; ... directory or symlink + (setq dirp (if (equal "directory" (cdr (assoc "type" attributes))) t)) + (setq res-symlink-target + (cdr (assoc "standard::symlink-target" attributes))) + (when (stringp res-symlink-target) (setq res-symlink-target - (cdr (assoc "standard::symlink-target" attributes))) - (when (stringp res-symlink-target) - (setq res-symlink-target - ;; Parse unibyte codes "\xNN". We assume they are - ;; non-ASCII codepoints in the range #x80 through #xff. - ;; Convert them to multibyte. - (decode-coding-string - (replace-regexp-in-string - "\\\\x\\([[:xdigit:]]\\{2\\}\\)" - (lambda (x) - (unibyte-string (string-to-number (match-string 1 x) 16))) - res-symlink-target) - 'utf-8))) - ;; ... number links - (setq res-numlinks - (string-to-number - (or (cdr (assoc "unix::nlink" attributes)) "0"))) - ;; ... uid and gid - (setq res-uid - (if (eq id-format 'integer) - (string-to-number - (or (cdr (assoc "unix::uid" attributes)) - (eval-when-compile - (format "%s" tramp-unknown-id-integer)))) - (or (cdr (assoc "owner::user" attributes)) - (cdr (assoc "unix::uid" attributes)) - tramp-unknown-id-string))) - (setq res-gid - (if (eq id-format 'integer) - (string-to-number - (or (cdr (assoc "unix::gid" attributes)) - (eval-when-compile - (format "%s" tramp-unknown-id-integer)))) - (or (cdr (assoc "owner::group" attributes)) - (cdr (assoc "unix::gid" attributes)) - tramp-unknown-id-string))) - ;; ... last access, modification and change time - (setq res-access - (seconds-to-time - (string-to-number - (or (cdr (assoc "time::access" attributes)) "0")))) - (setq res-mod - (seconds-to-time - (string-to-number - (or (cdr (assoc "time::modified" attributes)) "0")))) - (setq res-change - (seconds-to-time - (string-to-number - (or (cdr (assoc "time::changed" attributes)) "0")))) - ;; ... size - (setq res-size - (string-to-number - (or (cdr (assoc "standard::size" attributes)) "0"))) - ;; ... file mode flags - (setq res-filemodes - (let ((n (cdr (assoc "unix::mode" attributes)))) - (if n - (tramp-file-mode-from-int (string-to-number n)) - (format - "%s%s%s%s------" - (if dirp "d" (if res-symlink-target "l" "-")) - (if (equal (cdr (assoc "access::can-read" attributes)) - "FALSE") - "-" "r") - (if (equal (cdr (assoc "access::can-write" attributes)) - "FALSE") - "-" "w") - (if (equal (cdr (assoc "access::can-execute" attributes)) - "FALSE") - "-" "x"))))) - ;; ... inode and device - (setq res-inode - (let ((n (cdr (assoc "unix::inode" attributes)))) - (if n - (string-to-number n) - (tramp-get-inode (tramp-dissect-file-name filename))))) - (setq res-device - (let ((n (cdr (assoc "unix::device" attributes)))) - (if n - (string-to-number n) - (tramp-get-device (tramp-dissect-file-name filename))))) - - ;; Return data gathered. - (list - ;; 0. t for directory, string (name linked to) for - ;; symbolic link, or nil. - (or dirp res-symlink-target) - ;; 1. Number of links to file. - res-numlinks - ;; 2. File uid. - res-uid - ;; 3. File gid. - res-gid - ;; 4. Last access time, as a list of integers. - ;; 5. Last modification time, likewise. - ;; 6. Last status change time, likewise. - res-access res-mod res-change - ;; 7. Size in bytes (-1, if number is out of range). - res-size - ;; 8. File modes. - res-filemodes - ;; 9. t if file's gid would change if file were deleted - ;; and recreated. - nil - ;; 10. Inode number. - res-inode - ;; 11. Device number. - res-device - ))))) + ;; Parse unibyte codes "\xNN". We assume they are + ;; non-ASCII codepoints in the range #x80 through #xff. + ;; Convert them to multibyte. + (decode-coding-string + (replace-regexp-in-string + "\\\\x\\([[:xdigit:]]\\{2\\}\\)" + (lambda (x) + (unibyte-string (string-to-number (match-string 1 x) 16))) + res-symlink-target) + 'utf-8))) + ;; ... number links + (setq res-numlinks + (string-to-number + (or (cdr (assoc "unix::nlink" attributes)) "0"))) + ;; ... uid and gid + (setq res-uid + (if (eq id-format 'integer) + (string-to-number + (or (cdr (assoc "unix::uid" attributes)) + (eval-when-compile + (format "%s" tramp-unknown-id-integer)))) + (or (cdr (assoc "owner::user" attributes)) + (cdr (assoc "unix::uid" attributes)) + tramp-unknown-id-string))) + (setq res-gid + (if (eq id-format 'integer) + (string-to-number + (or (cdr (assoc "unix::gid" attributes)) + (eval-when-compile + (format "%s" tramp-unknown-id-integer)))) + (or (cdr (assoc "owner::group" attributes)) + (cdr (assoc "unix::gid" attributes)) + tramp-unknown-id-string))) + ;; ... last access, modification and change time + (setq res-access + (seconds-to-time + (string-to-number + (or (cdr (assoc "time::access" attributes)) "0")))) + (setq res-mod + (seconds-to-time + (string-to-number + (or (cdr (assoc "time::modified" attributes)) "0")))) + (setq res-change + (seconds-to-time + (string-to-number + (or (cdr (assoc "time::changed" attributes)) "0")))) + ;; ... size + (setq res-size + (string-to-number + (or (cdr (assoc "standard::size" attributes)) "0"))) + ;; ... file mode flags + (setq res-filemodes + (let ((n (cdr (assoc "unix::mode" attributes)))) + (if n + (tramp-file-mode-from-int (string-to-number n)) + (format + "%s%s%s%s------" + (if dirp "d" (if res-symlink-target "l" "-")) + (if (equal (cdr (assoc "access::can-read" attributes)) + "FALSE") + "-" "r") + (if (equal (cdr (assoc "access::can-write" attributes)) + "FALSE") + "-" "w") + (if (equal (cdr (assoc "access::can-execute" attributes)) + "FALSE") + "-" "x"))))) + ;; ... inode and device + (setq res-inode + (let ((n (cdr (assoc "unix::inode" attributes)))) + (if n + (string-to-number n) + (tramp-get-inode (tramp-dissect-file-name filename))))) + (setq res-device + (let ((n (cdr (assoc "unix::device" attributes)))) + (if n + (string-to-number n) + (tramp-get-device (tramp-dissect-file-name filename))))) + + ;; Return data gathered. + (list + ;; 0. t for directory, string (name linked to) for + ;; symbolic link, or nil. + (or dirp res-symlink-target) + ;; 1. Number of links to file. + res-numlinks + ;; 2. File uid. + res-uid + ;; 3. File gid. + res-gid + ;; 4. Last access time, as a list of integers. + ;; 5. Last modification time, likewise. + ;; 6. Last status change time, likewise. + res-access res-mod res-change + ;; 7. Size in bytes (-1, if number is out of range). + res-size + ;; 8. File modes. + res-filemodes + ;; 9. t if file's gid would change if file were deleted + ;; and recreated. + nil + ;; 10. Inode number. + res-inode + ;; 11. Device number. + res-device + )))) (defun tramp-gvfs-handle-file-executable-p (filename) "Like `file-executable-p' for Tramp files." @@ -1744,13 +1741,7 @@ This is relevant for GNOME Online Accounts." ;; Ensure that GNOME Online Accounts are cached. (when (member (tramp-file-name-method vec) tramp-goa-methods) (tramp-get-goa-accounts vec)) - (tramp-get-connection-property - (make-tramp-goa-name - :method (tramp-file-name-method vec) - :user (tramp-file-name-user vec) - :host (tramp-file-name-host vec) - :port (tramp-file-name-port vec)) - "prefix" "/"))) + (tramp-get-connection-property (tramp-make-goa-name vec) "prefix" "/"))) (defun tramp-gvfs-maybe-open-connection (vec) "Maybe open a connection VEC. @@ -1781,15 +1772,24 @@ connection if a previous connection has died for some reason." (when (and (string-equal method "afp") (string-equal localname "/")) - (tramp-error vec 'file-error "Filename must contain an AFP volume")) + (tramp-user-error vec "Filename must contain an AFP volume")) (when (and (string-match-p "davs?" method) (string-equal localname "/")) - (tramp-error vec 'file-error "Filename must contain a WebDAV share")) + (tramp-user-error vec "Filename must contain a WebDAV share")) (when (and (string-equal method "smb") (string-equal localname "/")) - (tramp-error vec 'file-error "Filename must contain a Windows share")) + (tramp-user-error vec "Filename must contain a Windows share")) + + (when (member method tramp-goa-methods) + ;; Ensure that GNOME Online Accounts are cached. + (tramp-get-goa-accounts vec) + (when (tramp-get-connection-property + (tramp-make-goa-name vec) "FilesDisabled" t) + (tramp-user-error + vec "There is no Online Account `%s'" + (tramp-make-tramp-file-name vec 'noloc)))) (with-tramp-progress-reporter vec 3 @@ -1910,6 +1910,15 @@ is applied, and it returns t if the return code is zero." ;; D-Bus GNOME Online Accounts functions. +(defun tramp-make-goa-name (vec) + "Transform VEC into a `tramp-goa-name' structure." + (when (tramp-file-name-p vec) + (make-tramp-goa-name + :method (tramp-file-name-method vec) + :user (tramp-file-name-user vec) + :host (tramp-file-name-host vec) + :port (tramp-file-name-port vec)))) + (defun tramp-get-goa-accounts (vec) "Retrieve GNOME Online Accounts, and cache them. The hash key is a `tramp-goa-name' structure. The value is an @@ -1917,52 +1926,55 @@ alist of the properties of `tramp-goa-interface-account' and `tramp-goa-interface-files' of the corresponding GNOME online account. Additionally, a property \"prefix\" is added. VEC is used only for traces." - (dolist - (object-path - (mapcar - #'car - (tramp-dbus-function - vec #'dbus-get-all-managed-objects - `(:session ,tramp-goa-service ,tramp-goa-path)))) - (let* ((account-properties - (with-tramp-dbus-get-all-properties vec - :session tramp-goa-service object-path - tramp-goa-interface-account)) - (files-properties - (with-tramp-dbus-get-all-properties vec - :session tramp-goa-service object-path - tramp-goa-interface-files)) - (identity - (or (cdr (assoc "PresentationIdentity" account-properties)) "")) - key) - ;; Only accounts which matter. - (when (and - (not (cdr (assoc "FilesDisabled" account-properties))) - (member - (cdr (assoc "ProviderType" account-properties)) - '("google" "owncloud")) - (string-match tramp-goa-identity-regexp identity)) - (setq key (make-tramp-goa-name - :method (cdr (assoc "ProviderType" account-properties)) - :user (match-string 1 identity) - :host (match-string 2 identity) - :port (match-string 3 identity))) - (when (string-equal (tramp-goa-name-method key) "google") - (setf (tramp-goa-name-method key) "gdrive")) - (when (string-equal (tramp-goa-name-method key) "owncloud") - (setf (tramp-goa-name-method key) "nextcloud")) - ;; Cache all properties. - (dolist (prop (nconc account-properties files-properties)) - (tramp-set-connection-property key (car prop) (cdr prop))) - ;; Cache "prefix". - (tramp-message - vec 10 "%s prefix %s" key - (tramp-set-connection-property - key "prefix" - (directory-file-name - (url-filename - (url-generic-parse-url - (tramp-get-connection-property key "Uri" "file:///")))))))))) + (with-tramp-connection-property (tramp-make-goa-name vec) "goa-accounts" + (dolist + (object-path + (mapcar + #'car + (tramp-dbus-function + vec #'dbus-get-all-managed-objects + `(:session ,tramp-goa-service ,tramp-goa-path)))) + (let* ((account-properties + (with-tramp-dbus-get-all-properties vec + :session tramp-goa-service object-path + tramp-goa-interface-account)) + (files-properties + (with-tramp-dbus-get-all-properties vec + :session tramp-goa-service object-path + tramp-goa-interface-files)) + (identity + (or (cdr (assoc "PresentationIdentity" account-properties)) "")) + key) + ;; Only accounts which matter. + (when (and + (not (cdr (assoc "FilesDisabled" account-properties))) + (member + (cdr (assoc "ProviderType" account-properties)) + '("google" "owncloud")) + (string-match tramp-goa-identity-regexp identity)) + (setq key (make-tramp-goa-name + :method (cdr (assoc "ProviderType" account-properties)) + :user (match-string 1 identity) + :host (match-string 2 identity) + :port (match-string 3 identity))) + (when (string-equal (tramp-goa-name-method key) "google") + (setf (tramp-goa-name-method key) "gdrive")) + (when (string-equal (tramp-goa-name-method key) "owncloud") + (setf (tramp-goa-name-method key) "nextcloud")) + ;; Cache all properties. + (dolist (prop (nconc account-properties files-properties)) + (tramp-set-connection-property key (car prop) (cdr prop))) + ;; Cache "prefix". + (tramp-message + vec 10 "%s prefix %s" key + (tramp-set-connection-property + key "prefix" + (directory-file-name + (url-filename + (url-generic-parse-url + (tramp-get-connection-property key "Uri" "file:///"))))))))) + ;; Mark, that goa accounts have been cached. + "cached")) ;; D-Bus zeroconf functions. diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el index 37b06cbe422..e5b0f149ca6 100644 --- a/lisp/net/tramp.el +++ b/lisp/net/tramp.el @@ -3127,9 +3127,8 @@ User is always nil." ;; Native `file-equalp-p' calls `file-truename', which requires a ;; remote connection. This can be avoided, if FILENAME1 and ;; FILENAME2 are not located on the same remote host. - (when (string-equal - (file-remote-p (expand-file-name filename1)) - (file-remote-p (expand-file-name filename2))) + (when (tramp-equal-remote + (expand-file-name filename1) (expand-file-name filename2)) (tramp-run-real-handler #'file-equal-p (list filename1 filename2)))) (defun tramp-handle-file-exists-p (filename) @@ -3141,9 +3140,8 @@ User is always nil." ;; Native `file-in-directory-p' calls `file-truename', which ;; requires a remote connection. This can be avoided, if FILENAME ;; and DIRECTORY are not located on the same remote host. - (when (string-equal - (file-remote-p (expand-file-name filename)) - (file-remote-p (expand-file-name directory))) + (when (tramp-equal-remote + (expand-file-name filename) (expand-file-name directory)) (tramp-run-real-handler #'file-in-directory-p (list filename directory)))) (defun tramp-handle-file-local-copy (filename)