diff --git a/ocrd/ocrd/workspace.py b/ocrd/ocrd/workspace.py index 1b46fce19..117b5f592 100644 --- a/ocrd/ocrd/workspace.py +++ b/ocrd/ocrd/workspace.py @@ -252,7 +252,7 @@ def save_mets(self): log.info("Saving mets '%s'", self.mets_target) if self.automatic_backup: WorkspaceBackupManager(self).add() - with atomic_write(self.mets_target, overwrite=True) as f: + with atomic_write(self.mets_target) as f: f.write(self.mets.to_xml(xmllint=True).decode('utf-8')) def resolve_image_exif(self, image_url): diff --git a/ocrd/ocrd/workspace_backup.py b/ocrd/ocrd/workspace_backup.py index c6ef1d29f..a23223b54 100644 --- a/ocrd/ocrd/workspace_backup.py +++ b/ocrd/ocrd/workspace_backup.py @@ -83,7 +83,7 @@ def add(self): mets_file = join(d, 'mets.xml') log.info("Backing up to %s" % mets_file) makedirs(d) - with atomic_write(mets_file, overwrite=True) as f: + with atomic_write(mets_file) as f: f.write(mets_str.decode('utf-8')) return chksum diff --git a/ocrd_utils/ocrd_utils/os.py b/ocrd_utils/ocrd_utils/os.py index a67fd2d1f..c3112de39 100644 --- a/ocrd_utils/ocrd_utils/os.py +++ b/ocrd_utils/ocrd_utils/os.py @@ -8,10 +8,10 @@ 'atomic_write', ] -from atomicwrites import atomic_write as atomic_write_ +from atomicwrites import atomic_write as atomic_write_, AtomicWriter from tempfile import TemporaryDirectory import contextlib -from os import getcwd, chdir, stat, chmod +from os import getcwd, chdir, stat, fchmod, umask from os.path import exists, abspath as abspath_ from zipfile import ZipFile @@ -55,12 +55,25 @@ def unzip_file_to_dir(path_to_zip, output_directory): z.extractall(output_directory) z.close() + + +# ht @pabs3 +# https://github.com/untitaker/python-atomicwrites/issues/42 +class AtomicWriterPerms(AtomicWriter): + def get_fileobject(self, **kwargs): + f = super().get_fileobject(**kwargs) + try: + mode = stat(self._path).st_mode + except FileNotFoundError: + # Creating a new file, emulate what os.open() does + mask = umask(0) + umask(mask) + mode = 0o664 & ~mask + fd = f.fileno() + fchmod(fd, mode) + return f + @contextlib.contextmanager -def atomic_write(fpath, overwrite=False): - if exists(fpath): - mode = stat(fpath).st_mode - else: - mode = 0o664 - with atomic_write_(fpath, overwrite=True) as f: +def atomic_write(fpath): + with atomic_write_(fpath, writer_cls=AtomicWriterPerms, overwrite=True) as f: yield f - chmod(fpath, mode) diff --git a/tests/test_workspace.py b/tests/test_workspace.py index ee91a9c88..336bf1d23 100644 --- a/tests/test_workspace.py +++ b/tests/test_workspace.py @@ -1,4 +1,4 @@ -from os import walk, stat, chmod +from os import walk, stat, chmod, umask from stat import filemode from subprocess import run, PIPE from os.path import join, exists, abspath, basename, dirname @@ -280,12 +280,13 @@ def test_image_from_page_basic(self): self.assertEquals(info['features'], 'binarized,clipped') def test_mets_permissions(self): - initLogging() with TemporaryDirectory() as tempdir: ws = self.resolver.workspace_from_nothing(tempdir) ws.save_mets() mets_path = join(ws.directory, 'mets.xml') - assert filemode(stat(mets_path).st_mode) == '-rw-rw-r--' + mask = umask(0) + umask(mask) + assert '%o' % (stat(mets_path).st_mode & ~mask) == '100644' chmod(mets_path, 0o777) ws.save_mets() assert filemode(stat(mets_path).st_mode) == '-rwxrwxrwx'