Checking File Permissions in Linux with Python

Checking File Permissions in Linux with Python

You're right that os.access, like the underlying access syscall, checks for a specific user (real rather than effective IDs, to help out with suid situations).

os.stat is the right way to get more general info about a file, including permissions per user, group, and others. The st_mode attribute of the object that os.stat returns has the permission bits for the file.

To help interpret those bits, you may want to use the stat module. Specifically, you'll want the bitmasks defined here, and you'll use the & operator (bit-and) to use them to mask out the relevant bits in that st_mode attribute -- for example, if you just need a True/False check on whether a certain file is group-readable, one approach is:

import os
import stat

def isgroupreadable(filepath):
st = os.stat(filepath)
return bool(st.st_mode & stat.S_IRGRP)

Take care: the os.stat call can be somewhat costly, so make sure to extract all info you care about with a single call, rather than keep repeating calls for each bit of interest;-).

Check the permissions of a file in python

os.access returns False when the file does not exist, regardless of the mode parameter passed.

This isn't stated explicitly in the documentation for os.access but it's not terribly shocking behavior; after all, if a file doesn't exist, you can't possibly access it. Checking the access(2) man page as suggested by the docs gives another clue, in that access returns -1 in a wide variety of conditions. In any case, you can simply do as I did and check the return value in IDLE:

>>> import os
>>> os.access('does_not_exist.txt', os.R_OK)
False

In Python it's generally discouraged to go around checking types and such before trying to actually do useful things. This philosophy is often expressed with the initialism EAFP, which stands for Easier to Ask Forgiveness than Permission. If you refer to the docs again, you'll see this is particularly relevant in the present case:

Note: Using access() to check if a user is authorized to e.g. open a file before actually doing so using open() creates a security
hole, because the user might exploit the short time interval between
checking and opening the file to manipulate it. It’s preferable to use
EAFP techniques. For example:

if os.access("myfile", os.R_OK):
with open("myfile") as fp:
return fp.read()
return "some default data"

is better written as:

try:
fp = open("myfile")
except IOError as e:
if e.errno == errno.EACCES:
return "some default data"
# Not a permission error.
raise
else:
with fp:
return fp.read()

If you have other reasons for checking permissions than second-guessing the user before calling open(), you could look to How do I check whether a file exists using Python? for some suggestions. Remember that if you really need an exception to be raised, you can always raise it yourself; no need to go hunting for one in the wild.


Since the IOError can be brought up a couple different ways (such as
permission denied, or non-existent directory), I am trying to get my
module to identify and publish the issue.

That's what the second approach above does. See:

>>> try:
... open('file_no_existy.gif')
... except IOError as e:
... pass
...
>>> e.args
(2, 'No such file or directory')
>>> try:
... open('unreadable.txt')
... except IOError as e:
... pass
...
>>> e.args
(13, 'Permission denied')
>>> e.args == (e.errno, e.strerror)
True

But you need to pick one approach or the other. If you're asking forgiveness, do the thing (open the file) in a try-except block and deal with the consequences appropriately. If you succeed, then you know you succeeded because there's no exception.

On the other hand, if you ask permission (aka LBYL, Look Before You Leap) in this that and the other way, you still don't know if you can successfully open the file until you actually do it. What if the file gets moved after you check its permissions? What if there's a problem you didn't think to check for?

If you still want to ask permission, don't use try-except; you're not doing the thing so you're not going to throw errors. Instead, use conditional statements with calls to os.access as the condition.

Checking permission of a file using python

Yes, you can use Python's os.stat(path) or os.access(path) directly, e.g. to check it's executable

if os.access("/data/lims/LI", os.X_OK):
print "pass"

See Checking File Permissions in Linux with Python for more details.

Checking File Permissions in Linux with Python

You're right that os.access, like the underlying access syscall, checks for a specific user (real rather than effective IDs, to help out with suid situations).

os.stat is the right way to get more general info about a file, including permissions per user, group, and others. The st_mode attribute of the object that os.stat returns has the permission bits for the file.

To help interpret those bits, you may want to use the stat module. Specifically, you'll want the bitmasks defined here, and you'll use the & operator (bit-and) to use them to mask out the relevant bits in that st_mode attribute -- for example, if you just need a True/False check on whether a certain file is group-readable, one approach is:

import os
import stat

def isgroupreadable(filepath):
st = os.stat(filepath)
return bool(st.st_mode & stat.S_IRGRP)

Take care: the os.stat call can be somewhat costly, so make sure to extract all info you care about with a single call, rather than keep repeating calls for each bit of interest;-).

check permissions of directories in python

Neither answer recurses, though it's not entirely clear that that's what the OP wants. Here's a recursive approach (untested, but you get the idea):

import os
import stat
import sys

MODE = "775"

def mode_matches(mode, file):
"""Return True if 'file' matches 'mode'.

'mode' should be an integer representing an octal mode (eg
int("755", 8) -> 493).
"""
# Extract the permissions bits from the file's (or
# directory's) stat info.
filemode = stat.S_IMODE(os.stat(file).st_mode)

return filemode == mode

try:
top = sys.argv[1]
except IndexError:
top = '.'

try:
mode = int(sys.argv[2], 8)
except IndexError:
mode = MODE

# Convert mode to octal.
mode = int(mode, 8)

for dirpath, dirnames, filenames in os.walk(top):
dirs = [os.path.join(dirpath, x) for x in dirnames]
for dirname in dirs:
if mode_matches(mode, dirname):
print dirname

Something similar is described in the stdlib documentation for
stat.

Check if file is deletable

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


Related Topics



Leave a reply



Submit