bug-sed
[Top][All Lists]
Advanced

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

bug#27200: sed happily modifies read-only files.


From: Assaf Gordon
Subject: bug#27200: sed happily modifies read-only files.
Date: Wed, 7 Jun 2017 17:39:45 +0000
User-agent: Mutt/1.5.23 (2014-03-12)

Hello,

On Wed, Jun 07, 2017 at 04:37:49PM +0200, Péter wrote:
(We are discussing about these bits: 
https://www.gnu.org/software/coreutils/manual/html_node/Mode-Structure.html)

Yes, exactly those bits.
However - there is a difference between those bits on a file,
and these bits on the directory containing that file.
That difference is the source of the confusion.

$ ls -la
-r--r--r-- 1 pp pp    10 jún    7 15:02 foo

Are you stating that in this case the file is (should be treated) writable?

The file is not writable (you can not change the content of the existing file). But it can be replaced with another file,
which is exactly what sed (and VI) are doing - that is the way
unix file/directory permissions have been for decades.


Sorry, I can not believe that.

There's no need to believe, just to try it ;)

Some *facts*:

$ vi foo

When trying to insert something: "W10: Warning: Changing a readonly file"
When trying to save the file: "[RED] E45: 'readonly' option is set (add ! to 
override)"

Exactly! "add ! to override" means VI can replace that file.

When you use "w!" in VI,
VI *can not* replace the content of the existing file (the kernel prevents it). But it can create a new temporary file,
then replace the source file with it.

$ nano foo

When trying to save (after some insertion): "[ Error writing foo: Permission denied 
]"

$ echo FFFFF >foo
bash: foo: Permission denied

$ python3 -c 'with open("foo", "w") as f: f.write("FFFFFFFF\n")'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
PermissionError: [Errno 13] Permission denied: 'foo'


In all these examples, you are attempting to modify the file directly.
They are prevented by the kernel.

This is not what sed (and VI) are doing - they are replacing the
file with another - which is perfectly permissable because
the directory is writable.

I know that this protection is not hyper-strong. Can be "circumvented". If a (bad) guy wants to modify the foo file, (s)he can accomplish it (with the trick of using unlink instead of mv ...):

There is no such thing as strong/hyper-strong/not-so-string protection.
Either something is protected, or it is not.

The *content* of the existing file can not be modified
(i.e. the kernel will reject write attempts, and no user-program
can modify it).
But because the directory containing that file is not write-protected,
a user can rename the file, delete the file, and replace the file
with another file (which gives the impression that the file was modified).



(I remain unconvinced, unless will state otherwise.)

I'll try to demonstrate what happens in two ways.

First, using inode:
A 'file' in unix is just a link to an inode (which represents
the file's content and metadata on the file system).
Multiple filenames can point to the same content on the file system,
thus having the same inode (these are 'hard-links').
Same inode means the same content, different inode means different
"files".
More info here: https://en.wikipedia.org/wiki/Inode

Create a file:

   $ echo hello > 1.txt

Its inode number is 8051885:

   $ ls -li
   total 4
   8051885 -rw-rw-r-- 1 gordon gordon 6 Jun  7 12:15 1.txt

Modify the file's content:

   $ echo world >> 1.txt

It is still the same 'file' on the file system (same inode number):

   $ ls -li
   total 4
   8051885 -rw-rw-r-- 1 gordon gordon 12 Jun  7 12:15 1.txt

Remove write-permissions from the file's content,
this prevents writing (as expected):

   $ chmod a-w 1.txt
   $ echo foobar >> 1.txt
   bash: 1.txt: Permission denied

If you now open the file in 'vi',
modify it, and do "w!" and "q" (to force-override the file),
you'll see it is a *different* file (different inode value):

   $ ls -li
   total 4
   8052811 -r--r--r-- 1 gordon gordon 19 Jun  7 12:18 1.txt

VI *replaced* the file, it did not (and could not) modify
the file directly.



Another way: since you've mentioned python,
observe the difference between these two scripts:

   $ echo hello > 1.txt
   $ chmod a-w 1.txt
   $ ls -li 1.txt
   8051885 -r--r--r-- 1 gordon gordon 6 Jun  7 13:21 1.txt

This script tries to modify the file's content directly,
and will fail with "permission denied":

   $ cat 1.py
   f=open("1.txt","w")
   f.write("world")

$ python 1.py Traceback (most recent call last):
     File "1.py", line 1, in <module>
       f=open("1.txt","w")
   IOError: [Errno 13] Permission denied: '1.txt'


But this script replaces the file with a new file,
and that is working just fine (just like 'sed' and 'vim'):

   $ cat 2.py

   import os
   inf=open("1.txt","r")
   outf=open("1.tmp","w")
   for i in inf:
       outf.write(i)
   outf.write("world\n")
   inf.close()
   outf.close()
   os.rename("1.tmp","1.txt")


   $ python 2.py
   [[ no error ]]

   $ cat 1.txt
   hello
   world



When programs like mv/rm/vi ask you about the file being
"write protected", they do the equivalent of this python script:


   import os, sys, stat

   if not os.access("1.txt",os.W_OK):
       a = input("The file is write-protected, replace it (y/n)? ")
       if a != "y":
           sys.exit("aborting")

   inf=open("1.txt","r")
   outf=open("1.tmp","w")
   for i in inf:
       outf.write(i)
   outf.write("hello\n")
   inf.close()
   outf.close()

   os.rename("1.tmp","1.txt")

That is, they check the file permission mode,
and if the write-bits are off, *as a courtesy*, they
ask if you are sure about replacing it.

But there is *nothing* technical (in the OS/kernel) that
protects the file from being replaced.

-----

I hope that the above at least convinces you that
from a pure technical POV, a file without write-permission
bits can be replaced (but not modified) - thus this is
not a bug in sed.


You can still argue that 'sed' should be friendlier to
(and more protective of) the user,
and warn about such cases (and optionally add a "--force" parameter).

That is certainly a valid request,
however, due to backwards compatiblity, this behaviour is not likely
to change without a lot more discussion and preparations.


Hope this helps,
regards,
- assaf








reply via email to

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