bug-gnu-emacs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

bug#66546: 30.0.50; save-buffer to write-protected file without backup f


From: Jens Schmidt
Subject: bug#66546: 30.0.50; save-buffer to write-protected file without backup fails
Date: Sat, 14 Oct 2023 21:09:53 +0200
User-agent: Mozilla Thunderbird

X-Debbugs-Cc: Eli Zaretskii <eliz@gnu.org>
Severity: minor

Eli, sorry to disturb you with another edge-case bug, but the commit
that has introduced it was yours, I think ...

*** Issue A

In a shell execute:

echo foo > foo
chmod 0400 foo
./src/emacs -Q foo
C-x C-q
bar
C-0 C-x C-s
=> File foo is write-protected; try to save anyway?
yes RET
=> basic-save-buffer-2: Opening output file: Permission denied,
   /home/jschmidt/work/emacs-master/foo

When saving *with* backup, that is, without prefix C-0, everything works
as expected: The file temporarily gets write permissions, gets written
to, and write permissions get removed again.

The root cause is related to Eli's introduction of calls to
`set-file-extended-attributes' in ccad023bc3c7.  Here:

------------------------- files.el -------------------------
        ;; If file not writable, see if we can make it writable
        ;; temporarily while we write it.
        ;; But no need to do so if we have just backed it up
        ;; (setmodes is set) because that says we're superseding.
        (cond ((and tempsetmodes (not setmodes))
               ;; Change the mode back, after writing.
               (setq setmodes
                     (list (file-modes buffer-file-name)
                           (with-demoted-errors
                               "Error getting extended attributes: %s"
                             (file-extended-attributes buffer-file-name))
                           buffer-file-name))
               ;; If set-file-extended-attributes fails, fall back on
               ;; set-file-modes.
               (unless
                   (with-demoted-errors "Error setting attributes: %s"
                     (set-file-extended-attributes buffer-file-name
                                                   (nth 1 setmodes)))
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [1]
                 (set-file-modes buffer-file-name
                                 (logior (car setmodes) 128)))))
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [2]
------------------------- files.el -------------------------

The marked call [1] is a no-op, since it does not change the permissions
in any way when writing them back, while the call [2] to
`set-file-modes' actually changes the file mode.

Since an extended file attribute Elisp object cannot be modified yet (is
that so?) to mark it, for example, as writable, I propose to just drop
that call to `set-file-extended-attributes', like this:

------------------------- snip -------------------------
diff --git a/lisp/files.el b/lisp/files.el
index e1421b403bf..d4d556fa470 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -5944,14 +5944,8 @@ basic-save-buffer-2
                                "Error getting extended attributes: %s"
                             (file-extended-attributes buffer-file-name))
                           buffer-file-name))
-              ;; If set-file-extended-attributes fails, fall back on
-              ;; set-file-modes.
-              (unless
-                  (with-demoted-errors "Error setting attributes: %s"
-                    (set-file-extended-attributes buffer-file-name
-                                                  (nth 1 setmodes)))
-                (set-file-modes buffer-file-name
-                                (logior (car setmodes) 128)))))
+              (set-file-modes buffer-file-name
+                              (logior (car setmodes) 128))))
        (let (success)
          (unwind-protect
              (progn
------------------------- snip -------------------------

*** Issue B

But then there is another issue (?), probably even longer unnoticed.  To
notice it, apply above patch, make, then execute the following test
case:

rm -f foo
echo foo > foo
chmod 0400 foo
./src/emacs -Q

Define the following advice on `write-region' to simulate a write error:

------------------------- snip -------------------------
(advice-add 'write-region :override
            (lambda (&rest _) (error "No space left on device")))
------------------------- snip -------------------------

Then continue

C-x C-f foo RET
C-x C-q
bar
C-0 C-x C-s
=> File foo is write-protected; try to save anyway?
yes RET
=> No space left on device

Now check the permissions of file foo:

  [emacs-master]$ ls -al foo
  -rw------- 1 jschmidt jschmidt 7 Oct 14 20:33 foo

So the write permissions did not get removed again.

Here the root cause is that the `unwind-protect' around the
`write-region' in `basic-save-buffer-2' does not handle the no-backup
case separately.  I would extend the clean-up form of the
`unwind-protect' to distinguish these both cases and handle the
no-backup case appropriately, here by trying first to set back the
extended attributes, then the regular ones.

*** What to do and where?

I can provide patches for both issues.  Plus ERT tests.

I guess that would be in one changeset, since these are related.

Should I provide a separate patch fixing only issue A for emacs-29?  Or
is that issue not relevant for emacs-29, given that it went unnoticed
for 12 years?





reply via email to

[Prev in Thread] Current Thread [Next in Thread]