Write file with specific permissions in Python Write file with specific permissions in Python python python

Write file with specific permissions in Python


What's the problem? file.close() will close the file even though it was open with os.open().

with os.fdopen(os.open('/path/to/file', os.O_WRONLY | os.O_CREAT, 0o600), 'w') as handle:  handle.write(...)


This answer addresses multiple concerns with the answer by vartec, especially the umask concern.

import osimport stat# Define file paramsfname = '/tmp/myfile'flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL  # Refer to "man 2 open".mode = stat.S_IRUSR | stat.S_IWUSR  # This is 0o600.umask = 0o777 ^ mode  # Prevents always downgrading umask to 0.# For security, remove file with potentially elevated modetry:    os.remove(fname)except OSError:    pass# Open file descriptorumask_original = os.umask(umask)try:    fdesc = os.open(fname, flags, mode)finally:    os.umask(umask_original)# Open file handle and write to filewith os.fdopen(fdesc, 'w') as fout:    fout.write('something\n')

If the desired mode is 0600, it can more clearly be specified as the octal number 0o600. Even better, just use the stat module.

Even though the old file is first deleted, a race condition is still possible. Including os.O_EXCL with os.O_CREAT in the flags will prevent the file from being created if it exists due to a race condition. This is a necessary secondary security measure to prevent opening a file that may already exist with a potentially elevated mode. In Python 3, FileExistsError with [Errno 17] is raised if the file exists.

Failing to first set the umask to 0 or to 0o777 ^ mode can lead to an incorrect mode (permission) being set by os.open. This is because the default umask is usually not 0, and it will be applied to the specified mode. For example, if my original umask is 2 i.e. 0o002, and my specified mode is 0o222, if I fail to first set the umask, the resulting file can instead have a mode of 0o220, which is not what I wanted. Per man 2 open, the mode of the created file is mode & ~umask.

The umask is restored to its original value as soon as possible. This getting and setting is not thread safe, and a threading.Lock must be used in a multithreaded application.

For more info about umask, refer to this thread.


updateFolks, while I thank you for the upvotes here, I myself have to argue against my originally proposed solution below. The reason is doing things this way, there will be an amount of time, however small, where the file does exist, and does not have the proper permissions in place - this leave open wide ways of attack, and even buggy behavior.
Of course creating the file with the correct permissions in the first place is the way to go - against the correctness of that, using Python's with is just some candy.

So please, take this answer as an example of "what not to do";

original post

You can use os.chmod instead:

>>> import os>>> name = "eek.txt">>> with open(name, "wt") as myfile:...   os.chmod(name, 0o600)...   myfile.write("eeek")...>>> os.system("ls -lh " + name)-rw------- 1 gwidion gwidion 4 2011-04-11 13:47 eek.txt0>>>

(Note that the way to use octals in Python is by being explicit - by prefixing it with "0o" like in "0o600". In Python 2.x it would work writing just 0600 - but that is both misleading and deprecated.)

However, if your security is critical, you probably should resort to creating it with os.open, as you do and use os.fdopen to retrieve a Python File object from the file descriptor returned by os.open.