22

This question already has an answer here:

Quite often we see that the file we are trying to save in vim after edit is reported to be read-only. The way around this is to add !wq, i am trying to figure out what internally goes that allows the vim program to gain enough permission to write the read-only file ?

Is there a internal flag which is switched or the vim temporarily gains the privileges for some time ?

marked as duplicate by Olorin, Jeff Schaller, Christopher, X Tian, Stephen Harris Feb 27 at 2:28

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • 4
    “Quite often” (?) indicates that you might want to fix your file permissions. This should happen very rarely. – Konrad Rudolph Feb 25 at 14:39
26

When you do w! in Vim, what actually happens depends on who owns the file.

  • If you (the current user) are the owner of the file, Vim will change the permissions to be writable before rewriting the file. It then removes the write permissions to restore the permission bits to what it was from the start.

  • If you are not the owner of the file, but if you have write permissions in the current directory, Vim will delete the original file and write the document to a new file with the same name. The new file will then be assigned the same permissions as the original file, but will be owned by you.

At no time does Vim gain elevated privileges to be able to write to the file.

The mechanics described above are the available options that any program that needs to write to a read-only file has to choose from (i.e. either temporarily change the permission while writing to the file, or delete the file and create a new one), and what Vim ends up choosing to do may in the end may depend on a number of configurable settings.

As seen in comments below, there is some confusion about the above. If you want to see for yourself what actually happens with your setup of Vim on your particular brand of Unix, I'd recommend tracing the system calls that Vim does while writing to a read-only file. How this is done depends on what Unix you are using. On Linux, this is likely done through e.g. strace vim file (then editing the file, saving it with w! and exiting).


This is the first case (output from ktrace+kdump on OpenBSD):

13228 vim      CALL  chmod(0x19b1d94b4b10,0100644<S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH|S_IFREG>)
13228 vim      NAMI  "file"
13228 vim      RET   chmod 0
13228 vim      CALL  lseek(3,0x1000,SEEK_SET)
13228 vim      RET   lseek 4096/0x1000
13228 vim      CALL  write(3,0x19b1e0aa9000,0x1000)

This changes permissions on the file so that it's writable (the S_IWUSR flag used with chmod()) and writes the buffer to it.

It then sets the original permissions:

13228 vim      CALL  fchmod(4,0100444<S_IRUSR|S_IRGRP|S_IROTH|S_IFREG>)
13228 vim      RET   fchmod 0
13228 vim      CALL  close(4)
13228 vim      RET   close 0

For the other case:

It first unlinks (deletes) the file and then recreates it (before writing to the file and changing permissions later):

44487 vim      CALL  unlink(0x79fdbc1f000)
44487 vim      NAMI  "file"
44487 vim      RET   unlink 0
44487 vim      CALL  open(0x79fdbc1f000,0x201<O_WRONLY|O_CREAT>,0644<S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH>)
44487 vim      NAMI  "file"
44487 vim      RET   open 4
  • It's really odd that you would get different results (even on a different OS) for the same software. Could you confirm the version you are using and the value of the two relavent config options you mention (backup and backupcopy) – Philip Couling Feb 25 at 10:52
  • @PhilipCouling Vim 8.1.800. Both backup and backupcopy are unset (this is without a private .vimrc file). – Kusalananda Feb 25 at 10:53
  • 1
    I did a truss on FreeBSD of both VIM and NeoVIM. I even checked nvi for good measure. I duplicated Kusalananda's results with all three. – JdeBP Feb 25 at 10:59
  • 1
    @JdeBP Yes I'm rapidly becoming suspicious I'm seeing something unusual or Debian specific. – Philip Couling Feb 25 at 11:04
  • 1
    I get @PhilipCouling's results when testing Vim 8.0.1298 on Gentoo. – Mark Feb 25 at 23:10
4

Vim cannot gain additional permissions. The :w! just overrides the internal 'readonly' option, which may have been set because:

  • you've opened the file via the -R command-line option or with :view instead of :edit, or :setlocal readonly
  • Vim recognizes that the file currently doesn't have write permissions

For the latter case, a file write may still be possible because Vim (by default) creates a new file and then replaces the original file with it. That still depends on the permissions being set in a way to allow this.


To really gain write permissions where the user that opened Vim doesn't have any, the :w !sudo tee >/dev/null file trick has to be used, either directly, or through a plugin like SudoEdit.

2

You can't ever write to a file that you don't have write permissions on. However you can delete that file if you have write permissions on the directory.

The trick that VIM is using is to delete the file and write a new one.

It's possible to show this is the method VIM uses without reading source code by checking the inode number before and after:

$ touch foo
$ chmod u-w foo
$ ls -li foo
60818465 -r--r----- 1 philip philip 0 Feb 25 10:24 foo
$ vi foo
$ # edit the file and save with :w!
$ ls -li foo
60818467 -r--r----- 1 philip philip 8 Feb 25 10:25 foo

Note that the inode number changed showing that the new file is NOT the same file as the one you edited.


FYI My current config is really short:

runtime! debian.vim
if has("syntax")
  syntax on
endif
set tabstop=4
set autoindent

And debian packages installed are:

vim                               2:8.1.0875-1
vim-common                        2:8.1.0875-1
vim-runtime                       2:8.1.0875-1
vim-tiny                          2:8.1.0875-1
  • Changing permissions on the file is exactly what Vim does, at least on OpenBSD. – Kusalananda Feb 25 at 10:33
  • @Kusalananda as per the experement above, not in Debian with Vim 2:8.1.0875-1. The file is clearly being completely replaced. – Philip Couling Feb 25 at 10:34
  • This is not the method that VIM uses by default. The doco (:help :write) states that it temporarily changes permissions, and a quick truss confirms that the doco is right. You have non-default backup options set. – JdeBP Feb 25 at 10:35
  • The phrase "by default" means "what happens if not otherwise configired" no? And :set backupcopy? results in backupcopy=auto. I've checked my config. There's nothing in there to change this behaviour (I think?) – Philip Couling Feb 25 at 10:42
  • when vim is in compatible mode, :w! will only override the read-only mode of the buffer, but will not try to change the file perms back and forth or do other amateur hour stuff. This is the only right behavior -- it would be nice if it was clearly mentioned which one of the options turned on or off by :set compatible is responsible for this. – mosvy Feb 25 at 10:57
2

When vim is in vi-compatible mode, :w! will only override the read-only mode of the buffer, but will not try to change the file permissions back and forth, bypass permissions by renaming a different file to the name of the original, or do other amateur hour stuff.

This is the only right behavior IMHO -- the flag which controls this is W from cpoptions / cpo. From :help cpo:

                                                *'cpoptions'* *'cpo'* *cpo*
'cpoptions' 'cpo'       string  (Vim default: "aABceFs",
                                 Vi default:  all flags)
...
                                                                *cpo-W*
                W       Don't overwrite a readonly file.  When omitted, ":w!"
                        overwrites a readonly file, if possible.

:set compatible will turn all cpo flags on. You can change just the W flag with :set cpo+=W or set cpo-=W.

  • That doesn't really answer the actual question that is being asked, however, which is how VIM does things when it does override file permissions. – JdeBP Feb 25 at 18:32
  • @JdeBP I know, but since I've spent time researching it (when I have to use vim I always set compatible as a matter of course and don't bother to learn anything beyond that), I might've just as well put it in an answer. Feel free to downvote/flag/ignore. If you want to know what vim is actually doing when :w!, you can have a look at buf_write() in src/fileio.c and do_write() in src/ex_cmds.c. – mosvy Feb 25 at 18:40

Not the answer you're looking for? Browse other questions tagged or ask your own question.