From 895562a8b24fc637fb422d38ea64e7d445b3480d Mon Sep 17 00:00:00 2001 From: Michael Albinus Date: Thu, 3 Feb 2022 14:21:23 +0100 Subject: [PATCH] Implement file modification check in Tramp * lisp/net/tramp.el (tramp-handle-lock-file): Check, whether FILE is modified. * test/lisp/net/tramp-tests.el (tramp-test39-detect-external-change): New test. --- lisp/net/tramp.el | 8 ++++ test/lisp/net/tramp-tests.el | 72 ++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el index c6e55ff6889..121ede42c43 100644 --- a/lisp/net/tramp.el +++ b/lisp/net/tramp.el @@ -4008,6 +4008,14 @@ Do not set it manually, it is used buffer-local in `tramp-get-lock-pid'.") ;; was visited. (catch 'dont-lock (unless (eq (file-locked-p file) t) ;; Locked by me. + (when (and buffer-file-truename + (not (verify-visited-file-modtime)) + (file-exists-p file)) + ;; In filelock.c, `userlock--ask-user-about-supersession-threat' + ;; is called, which also checks file contents. This is unwise + ;; for remote files. + (ask-user-about-supersession-threat file)) + (when-let ((info (tramp-get-lock-file file)) (match (string-match tramp-lock-file-info-regexp info))) (unless (ask-user-about-lock diff --git a/test/lisp/net/tramp-tests.el b/test/lisp/net/tramp-tests.el index 01fe7355f45..b41824a6cf3 100644 --- a/test/lisp/net/tramp-tests.el +++ b/test/lisp/net/tramp-tests.el @@ -6018,6 +6018,78 @@ Use direct async.") (ignore-errors (delete-file tmp-name1)) (tramp-cleanup-connection tramp-test-vec 'keep-debug 'keep-password))))) +;; The function was introduced in Emacs 28.1. +(ert-deftest tramp-test39-detect-external-change () + "Check that an external file modification is reported." + (skip-unless (tramp--test-enabled)) + (skip-unless (not (tramp--test-ange-ftp-p))) + ;; Since Emacs 28.1. + (skip-unless (fboundp 'file-locked-p)) + + (dolist (quoted (if (tramp--test-expensive-test-p) '(nil t) '(nil))) + (dolist (create-lockfiles '(nil t)) + (let ((tmp-name (tramp--test-make-temp-name nil quoted)) + (remote-file-name-inhibit-cache t) + (remote-file-name-inhibit-locks nil) + tramp-allow-unsafe-temporary-files + (inhibit-message t) + ;; tramp-rclone.el and tramp-sshfs.el cache the mounted files. + (tramp-fuse-unmount-on-cleanup t) + auto-save-default + (backup-inhibited t) + noninteractive) + (with-temp-buffer + (unwind-protect + (progn + (setq buffer-file-name tmp-name + buffer-file-truename tmp-name) + (insert "foo") + ;; Bug#53207: with `create-lockfiles' nil, saving the + ;; buffer results in a prompt. + (cl-letf (((symbol-function 'yes-or-no-p) + (lambda (_) (ert-fail "Test failed unexpectedly")))) + (save-buffer)) + (should-not (file-locked-p tmp-name)) + + ;; For local files, just changing the file + ;; modification on disk doesn't hurt, because file + ;; contents in buffer and on disk are equal. For + ;; remote files, file contents is not compared. We + ;; mock an older modification time in buffer, because + ;; Tramp regards modification times equal if they + ;; differ for less than 2 seconds. + (set-visited-file-modtime (time-add (current-time) -60)) + ;; Some Tramp methods cannot check the file + ;; modification time properly, for them it doesn't + ;; make sense to test. + (when (not (verify-visited-file-modtime)) + (cl-letf (((symbol-function 'read-char-choice) + (lambda (prompt &rest _) (message "%s" prompt) ?y))) + (ert-with-message-capture captured-messages + (insert "bar") + (when create-lockfiles + (should (string-match-p + (format + "^%s changed on disk; really edit the buffer\\?" + (if (tramp--test-crypt-p) + ".+" (file-name-nondirectory tmp-name))) + captured-messages)) + (should (file-locked-p tmp-name))))) + + ;; `save-buffer' removes the file lock. + (cl-letf (((symbol-function 'yes-or-no-p) #'tramp--test-always) + ((symbol-function 'read-char-choice) + (lambda (&rest _) ?y))) + (save-buffer)) + (should-not (file-locked-p tmp-name)))) + + ;; Cleanup. + (set-buffer-modified-p nil) + (ignore-errors (delete-file tmp-name)) + (tramp-cleanup-connection + tramp-test-vec 'keep-debug 'keep-password))))))) + +;; The functions were introduced in Emacs 26.1. (ert-deftest tramp-test40-make-nearby-temp-file () "Check `make-nearby-temp-file' and `temporary-file-directory'." (skip-unless (tramp--test-enabled)) -- 2.39.5