classification
Title: shutil.rmtree and write protected files
Type: Stage: resolved
Components: Library (Lib) Versions: Python 3.8, Python 3.6
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: PeterFS, eryksun
Priority: normal Keywords:

Created on 2020-01-15 09:35 by PeterFS, last changed 2021-03-17 20:14 by eryksun. This issue is now closed.

Messages (4)
msg360033 - (view) Author: Peter Liedholm (PeterFS) * Date: 2020-01-15 09:35
Ubuntu 18.4 and Windows 7 has different behaviour when deleting write protected files with rmtree.

Ubuntu silently deletes them (unexpected?)
Windows moans about access denied (expected?)

Reproduction method linux
mkdir test; touch test/file.txt; chmod -w test/file.txt

Reproduction method windows
mkdir test && type nul > test\file.txt && attrib +R test\file.txt

Reproduction method cont.
python3 -c "import shutil; shutil.rmtree('test')"
msg360046 - (view) Author: Eryk Sun (eryksun) * (Python triager) Date: 2020-01-15 12:09
I'm not certain exactly what you want Python to be doing here, but let's start out by clarifying some concepts.

> Reproduction method linux
> mkdir test; touch test/file.txt; chmod -w test/file.txt

Unlinking a file requires write permission to the parent directory, since that's what's getting directly modified. Indirectly, if it's the last link to the file, then the file itself gets deleted. But Unix has no delete permission on individual files. You may have been misled into thinking so by bash, which requests confirmation when unlinking a file that lacks write permission. 

That said, some Linux filesystems implement an [i]mmutable file attribute (e.g. chattr +i test/file.txt). Unlinking an immutable file is completely disallowed, so Linux requires super-user access to modify this file attribute. In practice, we can differentiate the two cases by the errno value. If the parent directory doesn't allow write access, the error is EACCES. But if the file is immutable, the error is EPERM. 

> Reproduction method windows
> mkdir test && type nul > test\file.txt && attrib +R test\file.txt

In Windows, delete access (actually delete/rename access, i.e. unlink/relink) is separate from write access.

For all filesystems, a file's share-access control trumps everything else. If previous opens do not share delete access, a request for delete access fails as a sharing violation (32).

Else if the user's access token has SeRestorePrivilege enabled, then delete access will be granted. Actually, the file open also has to request backup semantics for this privilege to apply, but the internal open used by WINAPI DeleteFileW does this for us.

Else if the parent directory grants delete-child access to the user or one of the user's enabled groups, then the user is granted delete access to the file. 

Else the discretionary access control on a file may grant or deny delete access -- if it's also allowed by mandatory access control. Delete access will not be granted if the file's mandatory label denies write-up access and the label is at a higher integrity level (defaults to medium) than that of the user's access token (defaults to medium).

Windows filesystems also support a readonly file attribute. We can open a readonly file for delete access, which has to be allowed because Windows allows renaming (relinking) a readonly file. However when we try to set a readonly file's delete disposition, such as via SetFileInformationByHandle: FileDispositionInfo, the request will fail with access denied (5). (Actually, at the lower-level NT API level, this case is STATUS_CANNOT_DELETE, which is differentiated from STATUS_ACCESS_DENIED, but the Windows API aggregates them.) Normally this two-step open/delete process is wrapped up in a single call such as DeleteFileW. However, if you're using the two-step process directly, note that Windows 10 has a new disposition flag to ignore the readonly attribute: FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE.
msg360751 - (view) Author: Peter Liedholm (PeterFS) * Date: 2020-01-27 10:51
What I would expect is a consistent behaviour and as a user I am not interested in inner guts of differences between filesystems.
Regards
/Peter
msg388963 - (view) Author: Eryk Sun (eryksun) * (Python triager) Date: 2021-03-17 20:14
> What I would expect is a consistent behaviour and as a 
> user I am not interested in inner guts of differences 
> between filesystems.

It's already consistent. msg360033 contains a misunderstanding about what write permission on a file means in Unix. The parent directory controls whether a file can be unlinked -- except for immutable files. For example:

    $ mkdir test; touch test/file.txt; chmod -w test
    $ python3.8 -c "import shutil; shutil.rmtree('test')"
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/usr/lib/python3.8/shutil.py", line 715, in rmtree
        _rmtree_safe_fd(fd, path, onerror)
      File "/usr/lib/python3.8/shutil.py", line 672, in _rmtree_safe_fd
        onerror(os.unlink, fullname, sys.exc_info())
      File "/usr/lib/python3.8/shutil.py", line 670, in _rmtree_safe_fd
        os.unlink(entry.name, dir_fd=topfd)
    PermissionError: [Errno 13] Permission denied: 'file.txt'
History
Date User Action Args
2021-03-17 20:14:04eryksunsetstatus: open -> closed
resolution: not a bug
messages: + msg388963

stage: resolved
2020-01-27 10:51:13PeterFSsetmessages: + msg360751
2020-01-15 12:09:24eryksunsetnosy: + eryksun
messages: + msg360046
2020-01-15 09:35:33PeterFScreate