How to change the permission of files using Python?

How to change the permission of files using Python?

Introduction

Hello World! This time I want to share the snippet of code that I used during my Outreachy internship with Apache Airflow. This code is used to change the permission of files using Python. While I got this snippet out of StackOverflow, I found the right solution after searching for a long time with help from my mentor Jarek. Searching with the right term to find out the apt solution is an important skill in development :)

Backstory

The backstory for this snippet is, I had to change the permission of the file in my code repository. I had to remove the write permission for the group and other users(different user types in Linux file access). You can set the permission for files with octal representation in chmod command. But on doing that way, you tend to overwrite the original permission of the file. The goal for me is to just remove the write permission for the group and other users while keeping the other permission of the file intact.

Bash way of changing file permission

You could change the file permission and access control using chmod command. To modify the permission you have to specify who and what are the access control for each whos. The list of who includes user(u- file owner), group(g-member of the user group), and others(o-all other except user and member of the user group). You could specify all (a) which means all the three sets specified above. The permission for each file includes read(r), write(w) and execute(x). The image below shows how to interpret the symbol when you list the file.

symbol_repr.png Let's see an example by listing a file in Unix.

sathishkannan@Sathishs-MacBook-Air ~ % ls -l test.txt
-rw-r-xr-x  1 sathishkannan  staff  0 Feb 14 17:05 test.txt

For the above example, the information about the file indicates that it is a file type where the user has read and write permission, group has read and execute permission, other has read and execute permission.

Changing the permission using Symbolic representation

You could see the symbolic representation of file (something like this -rwxr-xr-x) by using ls -l filename.

sathishkannan@Sathishs-MacBook-Air ~ % ls -l test.txt
---xr-xr-x  1 sathishkannan  staff  0 Feb 14 17:05 test.txt
sathishkannan@Sathishs-MacBook-Air ~ % chmod u=rwx test.txt
sathishkannan@Sathishs-MacBook-Air ~ % ls -l test.txt
-rwxr-xr-x  1 sathishkannan  staff  0 Feb 14 17:05 test.txt
sathishkannan@Sathishs-MacBook-Air ~ % chmod g+w test.txt
sathishkannan@Sathishs-MacBook-Air ~ % ls -l test.txt
-rwxrwxr-x  1 sathishkannan  staff  0 Feb 14 17:05 test.txt

You can either set the given permission for each user/group/other/all or you can modify only the specific permission using symbolic representation.

chmod u=rwx test.txt

For example in the above command, we have given permission for user to read, write and execute access.

chmod g+w test.txt

But in the above command, we add write permission alone to group. Here we don't worry about values other than the ones we are trying to modify.

Similarly, you can remove permission too.

sathishkannan@Sathishs-MacBook-Air ~ % ls -l test.txt
-rwxrwxr-x  1 sathishkannan  staff  0 Feb 14 17:05 test.txt
sathishkannan@Sathishs-MacBook-Air ~ % chmod o-x test.txt
sathishkannan@Sathishs-MacBook-Air ~ % ls -l test.txt
-rwxrwxr--  1 sathishkannan  staff  0 Feb 14 17:05 test.txt

In this above command chmod o-x test.txt we have removed the execute permission for Other.(o-x indicates that execute permission is removed for Other)

This way we can modify the specific permission for file/ directory.

Changing the permission using Octal representation

The below image gives an easy way to understand the Octal representation for changing the permission.

octal_repr.png

A few examples for changing the file permission using Octal representation are,

sathishkannan@Sathishs-MacBook-Air ~ % ls -l test.txt
-rwxr-xr--  1 sathishkannan  staff  0 Feb 14 17:05 test.txt
sathishkannan@Sathishs-MacBook-Air ~ % chmod 754 test.txt
sathishkannan@Sathishs-MacBook-Air ~ % ls -al test.txt
-rwxr-xr--  1 sathishkannan  staff  0 Feb 14 17:05 test.txt
sathishkannan@Sathishs-MacBook-Air ~ % chmod 756 test.txt
sathishkannan@Sathishs-MacBook-Air ~ % ls -al test.txt
-rwxr-xrw-  1 sathishkannan  staff  0 Feb 14 17:05 test.txt
sathishkannan@Sathishs-MacBook-Air ~ % chmod 421 test.txt
sathishkannan@Sathishs-MacBook-Air ~ % ls -al test.txt
-r---w---x  1 sathishkannan  staff  0 Feb 14 17:05 test.txt

Here in the example chmod 421 test.txt 421 is represented as below

user symbol:    u        g        o
octal repr:     4        2        1
binary repr:    100     010     001
symbol repr:    r--      -w-    --x

By setting octal digits to change file permission, we tend to access all the user-specific access control permission.

Pythonic way of changing file permission

Now let's take an overview of how this can be done in Python.

Changing the permission using Symbolic representation

While we could directly change the file/directory permission using symbol representation in Bash way. In pythonic way, we have to first retrieve the current file permission and we can use stat package to toggle the specific bits to change the permission. We also need to know about bit operation to understand it's working. By doing that way, it has the same effect of modifying only the permission that we intend to make.

Here are a few flags used in stat that will help us to understand about setting file permission

stat.S_IRUSR
Owner has read permission.

stat.S_IWUSR
Owner has write permission.

stat.S_IXUSR
Owner has execute permission.

stat.S_IRWXG
Mask for group permissions.

stat.S_IRGRP
Group has read permission.

stat.S_IWGRP
Group has write permission.

stat.S_IXGRP
Group has execute permission.

stat.S_IRWXO
Mask for permissions for others (not in group).

stat.S_IROTH
Others have read permission.

stat.S_IWOTH
Others have write permission.

stat.S_IXOTH
Others have execute permission.

Below is the code snippet that you can use to remove the file permission specifically.

In [27]: import stat

In [28]: file_to_fix='test.txt'

In [29]: def change_file_permission(file_to_fix):
    ...:     current = stat.S_IMODE(os.stat(file_to_fix).st_mode)
    ...:     new = current & ~stat.S_IWGRP & ~stat.S_IWOTH  # Removes group/other write permission
    ...:     os.chmod(file_to_fix, new)
    ...:
In [30]: ls -l test.txt
-rwxrw-rw-  1 sathishkannan  staff  0 Feb 14 17:05 test.txt*

In [31]: change_file_permission(file_to_fix)
# After this step group/other write permission is removed
In [32]: ls -l test.txt
-rwxr--r--  1 sathishkannan  staff  0 Feb 14 17:05 test.txt*

This is the specific command current & ~stat.S_IWGRP & ~stat.S_IWOTH that is used to remove the group/other write permission. Here we have used bit operators ~(invert operator - negates the bit) and & (AND operator - has bit value enabled only if both has values ). This way we can remove the specific permission.

Below is the code snippet that you can use to add the file permission specifically.

In [45]: import stat

In [46]: ls -l test.txt
-rwxrw-rw-  1 sathishkannan  staff  0 Feb 14 17:05 test.txt*

In [47]: def change_file_permission(file_to_fix):
    ...:     current = stat.S_IMODE(os.stat(file_to_fix).st_mode)
    ...:     new = current | stat.S_IXGRP | stat.S_IXOTH  # Add group/other execute permission
    ...:     os.chmod(file_to_fix, new)
    ...:

In [48]: file_to_fix='test.txt'

In [49]: change_file_permission(file_to_fix)
# After this step group/other execute permission is added
In [50]: ls -l test.txt
-rwxrwxrwx  1 sathishkannan  staff  0 Feb 14 17:05 test.txt*

This is the specific command current | stat.S_IXGRP | stat.S_IXOTH that is used to add the group/other execute permission. Here we have used bit operators |(OR operator - has bit value enabled if any of the operand has values). This way we can add the specific permission.

Changing the permission using Octal representation This is somewhat similar to what we have done in bash style. Except that we have to pass the octal representation for file permission by prefixing it with 0o from Python 3.7

In [3]: import os

In [4]: os.chmod('test.txt',0o421)

In [5]: ls -l test.txt
-r---w---x  1 sathishkannan  staff  0 Feb 14 17:05 test.txt*

In [6]: os.chmod('test.txt',0o644)

In [7]: ls -l test.txt
-rw-r--r--  1 sathishkannan  staff  0 Feb 14 17:05 test.txt

Hope you found the information in this blog useful. Please post your questions or feedbacks if you have any.

Credits: stackoverflow.com/questions/25988471/remove.. computerhope.com/unix/uchmod.htm en.wikipedia.org/wiki/Chmod linuxcommand.org/lc3_lts0090.php complexsql.com/unix-file-permissions superkogito.github.io/blog/chmodmodes.html Photo by Viktor Talashuk on Unsplash