43

Short Question:

Why can we manipulate a read-only file in Vim using : + w + q + ! even without being an administrator?

Long Question:

I have a text file (myFile.txt) which is read-only for everyone:

navid@navid-ThinkPad-T530:~/ubuntuTest$ ls -l myFile.txt 
-r--r--r-- 1 navid navid 26 Aug 22 21:21 myFile.txt

I can open it with Vim without having admin privileges:

navid@navid-ThinkPad-T530:~/ubuntuTest$ vi myFile.txt 

I modify it and press: Esc + : + w + q + Enter and I see this error message:

E45: 'readonly' option is set (add ! to override)

So far, everything makes sense. But when I press: Esc + : + w + q + ! + Enter, Vim saves the changes.

I'm using Ubuntu 16.04 and VIM 7.4.

Ravexina
  • 55,668
  • 25
  • 164
  • 183
Positive Navid
  • 925
  • 3
  • 12
  • 19

7 Answers7

59

As @Rob already mentioned, you can only do this if you have write access to the directory containing the file. Attempting to do the same thing to a file in, for example, /etc will fail.

As for how vim is doing this, it deletes the file and recreates it. To test this, I created a file owned by root:

echo foo | sudo tee fff

And then proceeded to edit the file with vim in the way that you describe, but attaching the process to strace to see what's happening:

strace vim fff 2> strace.out

I then checked strace.out and found:

unlink("fff")                           = 0
open("fff", O_WRONLY|O_CREAT|O_TRUNC, 0644) = 4
write(4, "foasdasdao\n", 11)            = 11

So, the file was first deleted (unlink("fff")), then a new file of the same name was created (open("fff", O_WRONLY|O_CREAT|O_TRUNC, 0644)) and the modifications I had made were written to it (write(4, "foasdasdao\n", 11)). If you try this at home, you will see that after you edit it with vim, the file will now belong to you and not root.

So, strictly speaking, vim isn't editing a file you have no write access to. It is deleting a file from a directory to which you do have write access and then creating a new file to which, again, you have write access.

terdon
  • 100,812
  • 2
    http://unix.stackexchange.com/questions/36467/why-inode-value-changes-when-we-edit-in-vi-editor – Gilles 'SO- stop being evil' Aug 23 '16 at 17:28
  • deleting a file is not considered a write operation? That seems unintuitive – CCJ Aug 23 '16 at 18:22
  • 9
    @CCJ it's a write operation on the directory but not the file, no. Write operations on files are those that change the file's contents. By the same token, creating/deleting files are write operations on the directory since you're changing its contents. – terdon Aug 23 '16 at 18:23
  • 1
    strace has a -o strace.out option, so you can leave the stderr of the traced process connected to the tty. You could also use -efile,write to trace only filesystem and write calls, not mmap / signals / anything else. – Peter Cordes Aug 23 '16 at 18:51
  • 2
    Also, that's a dangerous order of operations. It would be safer to write the replacement to a new filename and then use rename(2) to replace the old file. Then there's no time window where your data doesn't exist on disk. – Peter Cordes Aug 23 '16 at 18:53
  • 5
    @PeterCordes um, OK. You might want to direct your complaints to the vim developers though. I don't even use the thing, I'm in the emacs camp. – terdon Aug 23 '16 at 19:13
  • I'm also an emacs user >.< Oh well, I guess it works well enough for most people. – Peter Cordes Aug 23 '16 at 19:15
  • @CCJ: Just like how you can delete a const object in most programming languages. – Zan Lynx Aug 23 '16 at 21:42
  • 2
    @PeterCordes Vim does almost always have a .swp file on disk already to protect your data; be interesting to see if forcing that off changes the order of operations. – Weaver Aug 24 '16 at 06:46
  • 1
    @PeterCordes I believe Vim allows the method used to modify a file to be changed. See http://vimdoc.sourceforge.net/htmldoc/editing.html#backup and particularly http://vimdoc.sourceforge.net/htmldoc/options.html#%27backupcopy%27. Is that something like what you were thinking of? – andyg0808 Aug 24 '16 at 08:59
  • @andyg0808: yes. That makes sense, I thought VIM would have lots of options to control how it handled replacing files, since that's definitely something that people want their editor to get right (for their own personal definition of right). – Peter Cordes Aug 24 '16 at 09:10
  • 1
    so basically, write permission on a file is only useful if you don't have the write permission on the directory – njzk2 Aug 24 '16 at 18:20
  • 3
    @CCJ Deleting a file is a write operation to the directory containing it, not to the file itself. It is perfectly intuitive that if you are in charge of a directory (i.e., have write access to it), you should be able to control what's in it, and the owner of an individual file should not be allowed to override you. – fkraiem Aug 24 '16 at 19:56
  • @njzk2 well, no. If you don't have write access to the file, you can't write to it. You must delete it and create a new file with the same name. That's a very different thing. For instance, it changes the permissions and ownership of the file. – terdon Aug 24 '16 at 20:38
  • 1
    @terdon I wouldn't go as far as call that very different. The result is similar: there is a file with the same name, and it has my content in it. – njzk2 Aug 24 '16 at 20:46
  • 2
    This answer sounds to me like "The permissions model is broken". You say the file is read-only, it's just that I can change the contents arbitrarily. – DeadMG Aug 24 '16 at 21:44
  • The permissions model is fully fine, you just have to set correct permissions for the parent directory. – til_b Aug 25 '16 at 08:14
  • 2
    @DeadMG The permission model is fine, you just misunderstand how it works. If you have write access to a directory, that means you have full access to create and delete files in that directory. This is normal and is the case in all permission systems. You can, therefore, delete and recreate a file. You can't modify the file though. If you want to make a file read-only, you don't put it in a directory that can be written to. Calling that system broken is like complaining that someone opened your safe when you gave them the keys to it. – terdon Aug 25 '16 at 10:27
15

As long as you own the parent directory you can remove, or replace a file no matter the permission since you can change the content of the directory :).

Try it with other command like rm, it will prompt you but you can still do it. Make the directory not writable and that should stop it.

Addition:

Just tried it but as long as I own the file I can still modify it, even with the folder read only. However when I change ownership to root:root it cannot open the file for write. So solves the modifying files owned by root (or someone else)

Rob
  • 251
  • 1
  • 6
  • 7
    It sounds like VIM chooses from multiple strategies, including rewrite-in-place or unlink+write a new file. – Peter Cordes Aug 23 '16 at 19:01
  • 3
    @PeterCordes Yes It apparently will try very hard to do what you tell it to :) very crafty. :) – Rob Aug 23 '16 at 19:18
15

Using w! you are removing the original file (which you are permitted to do) and writing your version instead.

When you have write access to a directory, you can: create, move or delete files within that directory.

$ mkdir foo
$ echo hi > foo/file
$ chmod 777 foo
$ chmod 700 foo/file
$ ls -l foo/file 
-rwx------ 1 ravexina ravexina 7 Aug 31 03:19 foo/file

Now let me switch my user and change the file

$ sudo -u user2 -s
$ vi foo/a # save using w! (I wrote into the file bye)
$ ls -l foo/a
-rwx------ 1 user2 user2 7 Aug 31 03:20 foo/file

Now see what is in there:

$ cat foo/file
bye
Ravexina
  • 55,668
  • 25
  • 164
  • 183
9

See :help write-readonly:

                                                        write-readonly
When the 'cpoptions' option contains 'W', Vim will refuse to overwrite a
readonly file.  When 'W' is not present, ":w!" will overwrite a readonly file,
if the system allows it (the directory must be writable).

Since you have write permissions on the directory (meaning you can create, delete, or rename files in it), the system does allow it.


The default value of cpoptions does not contain W:

                                                'cpoptions' 'cpo' cpo
'cpoptions' 'cpo'       string  (Vim default: "aABceFs",
                                 Vi default:  all flags)
                        global
muru
  • 197,895
  • 55
  • 485
  • 740
2

Both your vim editor process and your file carry your

 getpwnam("navid")->pw_uid

ownership so that you could also shell out

 :!chmod +w %

and you might guess that once upon a time the even simpler

 :!rm %

(requiring only +w,u-t unlink permission on . and not even ownership) became too frequent for someone to type so that vim was reprogrammed to automatically offer and upon request automagically perform such an operation.

Try overwriting your big sister's

 /home/whoopi/.profile

as mere navid and bets are your vim gives you your desired refusal.

Zanna
  • 70,465
2

This is VIM's warning to you that might be relatively important considering how permissions work in UNIX. The apparent unintuitiveness of this is because UNIX filesystems have permissions for file stored in i-node of the file. Directory structure is somehow separate and is only linking these i-nodes. Directories also have their permissions that say whether you can link/unlink files into it, or read it or traverse to sub-directories. This design allows that the same file can appear at several different places in the directory structure (through hard links). By saying "add ! to override" VIM is trying to warn you that the original file will be unlinked (so it will stay in all other places untouched) and the new file will be created and linked to the original place in the directory structure. In case that the link-count of the original file decrements to zero, the original file will be freed, but if not, you are effectively cloning the file. Opening of the file also counts as link, so if some program opened the file and you agree to "add ! to override", the program will not see changes made to the file by you with VIM. The file only gets unlinked from directory by VIM and after closing the file by another program, the file will get freed, unless it was linked somewhere else.

Please note that in Windows, permissions for files are stored in directory, so from point of view of Windows permissions paradigm, this vim behaviour might indeed look strange. For writing to the file, Windows logically might also check some directory permissions, even super-directory permissions. As said above, in UNIX, directory permissions are irrelevant for the manipulation with the file as far as you was able to list it and open it (i.e. there were x for all super-directories). Opened file in UNIX might not not even have file name anymore if it was unlinked from all directories after opening.

For example, you have file /home/user1/foo and it is the same file as (i.e. hardlinked to) /home/user2/foo and the file is not writable by anyone and currently opened by program P (opened read-write by program started by root). If user1 opens it with vim and overwrites, he makes his own copy and no longer sees the original file. If subsequently user2 opens his link with vim and writes into it, it will get unlinked again and he will create another copy. Program P will still see the original file and can freely read or write into it. As soon as the program closes the file, the file will vanish (get freed by filesystem).

ludvik02
  • 109
  • 4
1

This isn't exactly an answer, but if you really want to set a file so that no one can change or delete it, you can make it immutable.

Normally, even when a file is owned by root, you can still delete the file if you have write permissions to the folder. But when you make the file immutable, even root cannot modify or delete it.

To make a file immutable (you need sudo):

sudo chattr +i myFile.txt

You can see this with lsattr (the letter i in the result):

$ lsattr myFile.txt
----i--------e-- myFile.txt

To make the file normal again:

sudo chattr -i myFile.txt

To clarify: When a file is immutable, it cannot be deleted, renamed, modified, or even hard-linked.

It's worth reading man chattr, because files can have a number of useful attributes.

You might also find "restricted deletion" useful. If placed on a folder (not a file), this means that whoever creates a file within the folder is permitted to modify or delete that file, but no one else is (except root). The folder /tmp has this flag set. You can see this with the t flag on /tmp:

$ ls -l --directory /tmp
drwxrwxrwt 10 root root 4096 Sep  6 09:00 /tmp

To set or remove the restricted deletion flag on a folder:

chmod +t myFolder      # Add the restricted deletion flag.
chmod -t myFolder      # Remove the restricted deletion flag.
Paddy Landau
  • 4,548