1

How to check in python if file is deletable without deleting it? After all I will delete it with:

try:
    os.remove(file_path)
except OSError as error:
    print(error)

So there is no problem with security hole between check and delete. I just have to inform user before press button that something is wrong.

ToTamire
  • 1,425
  • 1
  • 13
  • 23
  • Does this answer your question? [Checking File Permissions in Linux with Python](https://stackoverflow.com/questions/1861836/checking-file-permissions-in-linux-with-python) – komeil majidi Jul 04 '21 at 09:35
  • 1
    Why test it beforehand then? Give them a delete button and feedback if it does not work. If at any time another program can snatch the file and prevent deleting, the "test" beforehand has no value whatsoever. – Patrick Artner Jul 04 '21 at 09:43
  • @komeilmajidi I will check this in 2 hours, but this don't look like answer to my question – ToTamire Jul 04 '21 at 09:48
  • @PatrickArtner I have to do few things in a row and I want to minify fail this process in the middle. – ToTamire Jul 04 '21 at 09:51
  • How do you normally check if a file can be deleted? By reviewing the Premision and answering this question is already available – komeil majidi Jul 04 '21 at 10:21
  • @komeilmajidi In some projects I can delete file after permission check, in others I still have permission denied errors. So I don't think that this post answering my question. – ToTamire Jul 04 '21 at 11:49

2 Answers2

3

Explanation

To check if file is deletable I have to check if file_path has write permission.

os.access(file_path, os.W_OK)

This is not enough. Deleting a file requires also write and execute permissions on the directory containing the file. So I have to check if directory containing the file has write and execute premissions.

os.access(file_dirname, os.W_OK | os.X_OK)

This is still not enough. File can be locked by other process, so I have to check that I can access the file.

try:
    file = open(file_path, 'w')
    file.close()
except OSError:
    print('File locked')

I can also check if file_path is a file.

os.path.isfile(file_path)

Solution

def is_file_deletable(file_path):
    ''' return True if file is deletable, False otherwise '''

    file_dirname = os.path.dirname(file_path)  # get the directory name of file_path

    if os.path.isfile(file_path):  # if file_path exists and is a file
        if os.access(file_path, os.W_OK):  # if file_path has write permission
            if os.access(file_dirname, os.W_OK | os.X_OK):  # if directory containing file_path has write and execute permission
                try:  # if file_path can be opened for write
                    file = open(file_path, 'w')  # Attention: This will delete all the content from the file
                    file.close()
                    return True  # file_path is not locked
                except OSError:  # if file_path can't be opened for write
                    pass  # file_path is locked

    return False

Short version

def is_file_deletable(file_path):

    file_dirname = os.path.dirname(file_path)  # get the directory name of file_path
               
    if os.access(file_dirname, os.W_OK | os.X_OK):  # if folder containing file_path has write and execute permission
        try:  # if file_path can be opened for write
            file = open(file_path, 'w')  # Attention: This will delete all the content from the file
            file.close()
            return True  # file_path is a file and has write permission and is not locked
        except OSError:  # if file_path can't be opened for write
            pass  # file_path is not a file, or don't has write permission or is locked

    return False
ToTamire
  • 1,425
  • 1
  • 13
  • 23
-1

You can do this with os.access.

import os

if os.access(filepath, os.W_OK):
   print("Can write file - and remove it, too")
else:
   print("Cannot write file")

From the doc,

os.F_OK, os.R_OK, os.W_OK, os.X_OK [are] values to pass as the mode parameter of access() to test the existence, readability, writability and executability of path, respectively.

crissal
  • 2,547
  • 7
  • 25