41

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.

  • 1
    @Zanna Do you own the directory that the file is in? – Rob Aug 23 '16 at 15:31
  • Yeah this would be a HUGE problem otherwise :) – Rob Aug 23 '16 at 15:40
  • 10
    Modifying a file and replacing a file are two different things with different permission requirements. – David Schwartz Aug 23 '16 at 17:24
  • 1
    You might want to have a look at this. It basically answers your question and as @DavidSchwartz correctly pointed out: Modifying a file and replacing a file are two different things – Panagiotis Tabakis Aug 23 '16 at 18:02
  • @PanagiotisTabakis Very nice find this is brilliant.. chmod to make the file read-write and back again if you own it.. LOVE IT :) – Rob Aug 23 '16 at 19:27
57

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.

  • 2
  • 8
    @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
  • 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
  • 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
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)

  • 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
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
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).

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.

  • Thank you @Zanna for the code editing even though it cost me novice a tiny bit of reputation, deservedly though. – Roman Czyborra Oct 7 '17 at 21:09
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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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