diff --git a/binary_database_files/storage.py b/binary_database_files/storage.py index 9615140..27a8ae9 100644 --- a/binary_database_files/storage.py +++ b/binary_database_files/storage.py @@ -54,7 +54,8 @@ def _open(self, name, mode="rb"): fqfn = self.path(name) if os.path.isfile(fqfn): # print('Loading file into database.') - self._save(name, open(fqfn, mode)) + with open(fqfn, mode) as fd: + self._save(name, fd) fh = super(DatabaseStorage, self)._open(name, mode) content = fh.read() size = fh.size diff --git a/binary_database_files/utils.py b/binary_database_files/utils.py index 2f04428..e93cf0a 100644 --- a/binary_database_files/utils.py +++ b/binary_database_files/utils.py @@ -1,10 +1,14 @@ import hashlib +import logging import os +import shutil from django.conf import settings from binary_database_files import settings as _settings +logger = logging.getLogger(__name__) + def is_fresh(name, content_hash): """ @@ -41,14 +45,38 @@ def get_hash_fn(name): fqfn = os.path.join(settings.MEDIA_ROOT, name) fqfn = os.path.normpath(fqfn) fqfn_parts = os.path.split(fqfn) - if not os.path.isdir(fqfn_parts[0]): - os.makedirs(fqfn_parts[0]) + + create_directory_for_file(fqfn) + hash_fn = os.path.join( fqfn_parts[0], _settings.DB_FILES_DEFAULT_HASH_FN_TEMPLATE % fqfn_parts[1] ) return hash_fn +def create_directory_for_file(full_path): + + directory = os.path.dirname(full_path) + directory_permissions_mode = settings.FILE_UPLOAD_DIRECTORY_PERMISSIONS + + if os.path.isdir(directory): + return + + try: + if directory_permissions_mode is not None: + # Set the umask because os.makedirs() doesn't apply the "mode" + # argument to intermediate-level directories. + old_umask = os.umask(0o777 & ~directory_permissions_mode) + try: + os.makedirs(directory, directory_permissions_mode, exist_ok=True) + finally: + os.umask(old_umask) + else: + os.makedirs(directory, exist_ok=True) + except FileExistsError: + raise FileExistsError('%s exists and is not a directory.' % directory) + + def write_file(name, content, overwrite=False): """ Writes the given content to the relative filename under the MEDIA_ROOT. @@ -57,10 +85,11 @@ def write_file(name, content, overwrite=False): fqfn = os.path.normpath(fqfn) if os.path.isfile(fqfn) and not overwrite: return - fqfn_parts = os.path.split(fqfn) - if not os.path.isdir(fqfn_parts[0]): - os.makedirs(fqfn_parts[0]) - open(fqfn, "wb").write(content) + + create_directory_for_file(fqfn) + + with open(fqfn, "wb") as fd: + fd.write(content) # Cache hash. hash_value = get_file_hash(fqfn) @@ -69,20 +98,28 @@ def write_file(name, content, overwrite=False): value = bytes(hash_value, "utf-8") except TypeError: value = hash_value - open(hash_fn, "wb").write(value) + + with open(hash_fn, "wb") as fd: + fd.write(value) # Set ownership and permissions. uname = getattr(settings, "DATABASE_FILES_USER", None) gname = getattr(settings, "DATABASE_FILES_GROUP", None) - if gname: - gname = ":" + gname - if uname: - os.system('chown -RL %s%s "%s"' % (uname, gname, fqfn_parts[0])) # noqa: S605 + if uname is not None or gname is not None: + try: + shutil.chown(fqfn, user=uname, group=gname) + shutil.chown(hash_fn, user=uname, group=gname) + except OSError as e: + logger.warning('Could not chown file %s: %s', fqfn, e) # Set permissions. - perms = getattr(settings, "DATABASE_FILES_PERMS", None) + perms = getattr(settings, "DATABASE_FILES_PERMS", settings.FILE_UPLOAD_PERMISSIONS) if perms: - os.system('chmod -R %s "%s"' % (perms, fqfn_parts[0])) # noqa: S605 + try: + os.chmod(fqfn, perms) + os.chmod(hash_fn, perms) + except OSError as e: + logger.warning('Could not chmod file %s: %s', fqfn, e) def get_file_hash(fin, force_encoding=None, encoding=None, errors=None, chunk_size=128):