-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP: Set permissions for atomic open_file() #1382
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
import re | ||
import io | ||
import os | ||
import stat | ||
import sys | ||
import codecs | ||
from weakref import WeakKeyDictionary | ||
|
@@ -33,6 +34,12 @@ def _make_text_stream(stream, encoding, errors, | |
force_readable=force_readable, | ||
force_writable=force_writable) | ||
|
||
def _get_current_umask(): | ||
"""Get current umask.""" | ||
umask = os.umask(0) | ||
os.umask(umask) | ||
return umask | ||
|
||
|
||
def is_ascii_encoding(encoding): | ||
"""Checks if a given encoding is ascii.""" | ||
|
@@ -512,7 +519,19 @@ def open_stream(filename, mode='r', encoding=None, errors='strict', | |
else: | ||
f = os.fdopen(fd, mode) | ||
|
||
return _AtomicFile(f, tmp_filename, os.path.realpath(filename)), True | ||
real_filename = os.path.realpath(filename) | ||
|
||
# Get permissions to set on the target file. If the target file already | ||
# exists, retain its current permissions. If the target file will be | ||
# created, respect the permissions from the current umask. | ||
permissions = None | ||
if not WIN: | ||
try: | ||
permissions = stat.S_IMODE(os.stat(real_filename).st_mode) | ||
except OSError: | ||
msmolens marked this conversation as resolved.
Show resolved
Hide resolved
|
||
permissions = 0o666 & ~_get_current_umask() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMO for the fallback it's better to set here a default permission, like 0o666 or even more paranoidal 0o600. As @andersk right said it's not thread-safe. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or even better, just There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The default permissions if you didn’t specify The underlying problem is that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are right, anyways at least on save an existing file there will be correct permissions. |
||
|
||
return _AtomicFile(f, tmp_filename, real_filename, permissions), True | ||
|
||
|
||
# Used in a destructor call, needs extra protection from interpreter cleanup. | ||
|
@@ -526,10 +545,11 @@ def open_stream(filename, mode='r', encoding=None, errors='strict', | |
|
||
class _AtomicFile(object): | ||
|
||
def __init__(self, f, tmp_filename, real_filename): | ||
def __init__(self, f, tmp_filename, real_filename, permissions): | ||
self._f = f | ||
self._tmp_filename = tmp_filename | ||
self._real_filename = real_filename | ||
self._permissions = permissions | ||
self.closed = False | ||
|
||
@property | ||
|
@@ -546,6 +566,8 @@ def close(self, delete=False): | |
except OSError: | ||
pass | ||
_replace(self._tmp_filename, self._real_filename) | ||
if self._permissions is not None: | ||
os.chmod(self._real_filename, self._permissions) | ||
self.closed = True | ||
|
||
def __getattr__(self, name): | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is, unfortunately, a potential security problem in the presence of multiple threads. (See https://bugs.python.org/issue21082.)
I’m not sure what to suggest instead, other than avoiding
tempfile
and writing the loop to safely create a fresh filename manually.