2

I've see this question about how to make a folder writable.

But how do I query for write permissions on a folder?!?!

On a directory in C:\Program Files\... where I clearly get IOError: [Errno 13] Permission denied every method I tried seems to clearly say ALL GOOD! ... (probably created by Admin; other dirs in Program Files can be written no problem)

For instance os.access also says fine. The docs tells me about EAFP and true: When I try to write a dummy file I get the wanted information but actually I'd like to not go that far.

On the other hand? How bad is it actually? Vaguely assuming I'd dare to do something like this:

import os
import uuid


def write_dir_test(path):
    dummypath = os.path.join(path, str(uuid.uuid4()))
    try:
        with open(dummypath, 'w'):
            pass
        os.remove(dummypath)
        return True
    except IOError:
        return False

It "feels" kinda dirty. But actually: Is it?

Is there a better way?

ewerybody
  • 1,443
  • 16
  • 29
  • 2
    I think all the ways are described in the link. you seem to be right in your attempt. Windows isn't the best OS around. – Jean-François Fabre Nov 10 '17 at 16:25
  • Ah ok. Thanks! Sorry for dup. I WAS actually searching before but I didn't use the word `Determining` m( – ewerybody Nov 10 '17 at 16:31
  • if you searched with SO engine, no wonder why you didn't find anything. Use google next time. – Jean-François Fabre Nov 10 '17 at 16:35
  • I used SO & duckduckgo ;] – ewerybody Nov 10 '17 at 16:38
  • 1
    You might have the right to create a file, but you might not be granted the right to delete it or might be denied this right. If the goal is to simply determine access rather than create the file, you should query the access via the Windows API. – Eryk Sun Nov 11 '17 at 03:39
  • 1
    Python's current implementation of `os.access` on Windows is useless in this regard. It only checks whether a file or directory is accessible in general (i.e. can read the file attributes) and whether a file is writable (i.e. not read-only). `os.access` ignores the read-only attribute on a directory. It prevents a directory from being deleted, but it has nothing to do with being allowed to modify it by adding files, etc. – Eryk Sun Nov 11 '17 at 03:45
  • But how would I query the Windows API via python? – ewerybody Nov 13 '17 at 09:27
  • 1
    Try to open the directory or file with the desired access. For example, if you want to create and delete a file in a directory, try to open the directory with `FILE_ADD_FILE` (i.e. `FILE_WRITE_DATA`) and `FILE_DELETE_CHILD` access. – Eryk Sun Nov 13 '17 at 13:00
  • 1
    Windows `CreateFile` allows opening a directory, but it requires backup semantics, which grants all access if the effective token (process or impersonation) has the backup and restore privileges enabled. This could be misleading. The native NT function `NtOpenFile` allows opening a directory without requiring backup semantics. Using the native API also allows supporting `dir_fd` to improve reliability by working relative to the parent directory via the `OBJECT_ATTRIBUTES` `RootDirectory` handle. – Eryk Sun Nov 13 '17 at 13:09
  • 1
    A downside to opening a directory to check access is the I/O sharing mode for read (execute), write (append), and delete access. If the directory is already open without write sharing, then trying to open with `FILE_WRITE_DATA` access will fail with a sharing violation error. Oddly, you can still add a file to an NTFS directory in this case. Apparently it only checks the sharing state when directly opening the directory, not when internally opening it to create a file. But you can assume that getting a sharing violation instead of access denied means you would otherwise be granted access. – Eryk Sun Nov 13 '17 at 13:26
  • 1
    Alternatively you could get the security descriptor via `GetSecurityInfo` and call `AccessCheck`, but that's more work. Also, it's not enough to check the security of a directory or file for read or delete access. If the account is denied or not granted the right to read attributes or delete an item explicitly, it could be implicitly granted the right by the parent directory granting the `FILE_LIST_DIRECTORY` and `FILE_DELETE_CHILD` rights. You'd have to recur to check the parent in those cases. – Eryk Sun Nov 13 '17 at 13:38
  • Possible duplicate of [Determining Whether a Directory is Writeable](https://stackoverflow.com/questions/2113427/determining-whether-a-directory-is-writeable) – Vasily Ryabov Jun 29 '18 at 17:43
  • @VasilyRyabov this one is explicitly for *nix and I'm asking for windows. But there is good stuff as well thanks – ewerybody Feb 17 '23 at 09:56
  • @ewerybody it works for Windows for me. I've set "Read-only" attribute in file properties and `os.access` returned `False` while it was `True` before. – Vasily Ryabov Feb 17 '23 at 16:51
  • So these give `False` for you? `os.access('C:\\Windows', os.W_OK) > True` `os.access('C:\\Program Files', os.W_OK) > True` – ewerybody Feb 19 '23 at 20:09

1 Answers1

1

To answer your question "how bad" is it?

Well, I first found the question you've linked to, then implemented almost exactly what you have (difference: I do str(uuid.uuid4()+".txt", I write a dummy string to the file, and I catch OSError instead of IOError, but other than that, I'm doing exactly the same thing:

def is_writable(dirpath):
    if sys.platform.startswith("linux"):
        return os.access(dirpath, os.W_OK)

    try:
        test_file = os.path.join(dirpath, str(uuid.uuid4()) + ".txt")
        with open(test_file, "w") as test:
            test.write("hello")
        os.unlink(test_file)
        return True
    except OSError as e:
        print(e)
        return False

For "production" use, I'd just remove the print(e).

You avoid some problems with tempfile by using uuid.

My version checks the platform, to default to os.access when on Linux, where it works.

Corner cases like "the directory is writable, but the disk is full" can often be ignored (you won't be writing to this directory anytime soon!).

To sum up — how bad? I'd say: relax, it's pretty good.

Tomasz Gandor
  • 8,235
  • 2
  • 60
  • 55
  • 1
    Hey thanks Tomasz! I guess the different error was due to different Python versions? It's been a while :) In my tests the opening already caused it to raise but testing writing might not hurt either – ewerybody Feb 17 '23 at 09:49