diff --git a/src/borg/archive.py b/src/borg/archive.py index 95ed2d8de..9156679a6 100644 --- a/src/borg/archive.py +++ b/src/borg/archive.py @@ -680,43 +680,46 @@ Utilization of max. archive size: {csize_max:.0%} gid = item.gid if gid is None else gid # This code is a bit of a mess due to os specific differences try: - if fd: - os.fchown(fd, uid, gid) - else: - os.chown(path, uid, gid, follow_symlinks=False) - except OSError: - pass - if fd: - os.fchmod(fd, item.mode) - elif not symlink: - os.chmod(path, item.mode) - elif has_lchmod: # Not available on Linux - os.lchmod(path, item.mode) - mtime = item.mtime - if 'atime' in item: - atime = item.atime - else: - # old archives only had mtime in item metadata - atime = mtime - if 'birthtime' in item: - birthtime = item.birthtime try: - # This should work on FreeBSD, NetBSD, and Darwin and be harmless on other platforms. - # See utimes(2) on either of the BSDs for details. if fd: - os.utime(fd, None, ns=(atime, birthtime)) + os.fchown(fd, uid, gid) else: - os.utime(path, None, ns=(atime, birthtime), follow_symlinks=False) + os.chown(path, uid, gid, follow_symlinks=False) + except OSError: + pass + if fd: + os.fchmod(fd, item.mode) + elif not symlink: + os.chmod(path, item.mode) + elif has_lchmod: # Not available on Linux + os.lchmod(path, item.mode) + mtime = item.mtime + if 'atime' in item: + atime = item.atime + else: + # old archives only had mtime in item metadata + atime = mtime + if 'birthtime' in item: + birthtime = item.birthtime + try: + # This should work on FreeBSD, NetBSD, and Darwin and be harmless on other platforms. + # See utimes(2) on either of the BSDs for details. + if fd: + os.utime(fd, None, ns=(atime, birthtime)) + else: + os.utime(path, None, ns=(atime, birthtime), follow_symlinks=False) + except OSError: + # some systems don't support calling utime on a symlink + pass + try: + if fd: + os.utime(fd, None, ns=(atime, mtime)) + else: + os.utime(path, None, ns=(atime, mtime), follow_symlinks=False) except OSError: # some systems don't support calling utime on a symlink pass - try: - if fd: - os.utime(fd, None, ns=(atime, mtime)) - else: - os.utime(path, None, ns=(atime, mtime), follow_symlinks=False) - except OSError: - # some systems don't support calling utime on a symlink + except AttributeError: pass acl_set(path, item, self.numeric_owner, fd=fd) # chown removes Linux capabilities, so set the extended attributes at the end, after chown, since they include diff --git a/src/borg/helpers/checks.py b/src/borg/helpers/checks.py index de484f5f8..383eb37c7 100644 --- a/src/borg/helpers/checks.py +++ b/src/borg/helpers/checks.py @@ -1,4 +1,5 @@ import os +import sys from .errors import Error @@ -8,9 +9,14 @@ class PythonLibcTooOld(Error): def check_python(): - required_funcs = {os.stat, os.utime, os.chown} + if sys.platform.startswith(('win32', )): + required_funcs = {os.stat} + else: + required_funcs = {os.stat, os.utime, os.chown} if not os.supports_follow_symlinks.issuperset(required_funcs): raise PythonLibcTooOld + pass + class ExtensionModuleError(Error): diff --git a/src/borg/platform/__init__.py b/src/borg/platform/__init__.py index 3bb9ac68b..3054c9dcc 100644 --- a/src/borg/platform/__init__.py +++ b/src/borg/platform/__init__.py @@ -19,6 +19,7 @@ if not sys.platform.startswith(('win32', )): from .posix import process_alive, local_pid_alive # posix swidth implementation works for: linux, freebsd, darwin, openindiana, cygwin from .posix import swidth + from .posix import get_errno from .posix import uid2user, user2uid, gid2group, group2gid, getosusername if sys.platform.startswith('linux'): # pragma: linux only diff --git a/src/borg/platform/xattr.py b/src/borg/platform/xattr.py index 6ed82a92b..63f9bf7e4 100644 --- a/src/borg/platform/xattr.py +++ b/src/borg/platform/xattr.py @@ -1,8 +1,6 @@ import errno import os -from .posix import get_errno - from ..helpers import Buffer @@ -39,6 +37,7 @@ class BufferTooSmallError(Exception): def _check(rv, path=None, detect_buffer_too_small=False): + from . import get_errno # circular reference if imported at global level! if rv < 0: e = get_errno() if detect_buffer_too_small and e == errno.ERANGE: diff --git a/src/borg/repository.py b/src/borg/repository.py index 0c9748e44..916f66a19 100644 --- a/src/borg/repository.py +++ b/src/borg/repository.py @@ -652,6 +652,10 @@ class Repository: except OSError as os_error: logger.warning('Failed to check free space before committing: ' + str(os_error)) return + except AttributeError: + # TODO move the call to statvfs to platform + logger.warning('Failed to check free space before committing: no statvfs method available' ) + return # f_bavail: even as root - don't touch the Federal Block Reserve! free_space = st_vfs.f_bavail * st_vfs.f_bsize logger.debug('check_free_space: required bytes {}, free bytes {}'.format(required_free_space, free_space)) diff --git a/src/borg/testsuite/__init__.py b/src/borg/testsuite/__init__.py index caa7d2a22..13af4d72e 100644 --- a/src/borg/testsuite/__init__.py +++ b/src/borg/testsuite/__init__.py @@ -2,7 +2,11 @@ from contextlib import contextmanager import filecmp import functools import os -import posix +try: + import posix # buildin but not everywhere +except: + posix = None + import stat import sys import sysconfig @@ -44,7 +48,7 @@ except ImportError: has_llfuse = False # The mtime get/set precision varies on different OS and Python versions -if 'HAVE_FUTIMENS' in getattr(posix, '_have_functions', []): +if posix != None and 'HAVE_FUTIMENS' in getattr(posix, '_have_functions', []): st_mtime_ns_round = 0 elif 'HAVE_UTIMES' in sysconfig.get_config_vars(): st_mtime_ns_round = -6 @@ -95,7 +99,10 @@ def are_fifos_supported(): os.mkfifo(filepath) return True except OSError: - return False + pass + except NotImplementedError: + pass + return False @functools.lru_cache() @@ -113,6 +120,8 @@ def is_utime_fully_supported(): return True except OSError: pass + except NotImplementedError: + pass return False @@ -135,6 +144,8 @@ def is_birthtime_fully_supported(): return True except OSError: pass + except NotImplementedError: + pass return False diff --git a/src/borg/testsuite/hashindex.py b/src/borg/testsuite/hashindex.py index 0b4b3bc5f..eb9154f84 100644 --- a/src/borg/testsuite/hashindex.py +++ b/src/borg/testsuite/hashindex.py @@ -8,7 +8,7 @@ import zlib from ..hashindex import NSIndex, ChunkIndex, ChunkIndexEntry from .. import hashindex from ..crypto.file_integrity import IntegrityCheckedFile, FileIntegrityError -from . import BaseTestCase +from . import BaseTestCase, unopened_tempfile # Note: these tests are part of the self test, do not use or import py.test functionality here. # See borg.selftest for details. If you add/remove test methods, update SELFTEST_COUNT @@ -55,21 +55,22 @@ class HashIndexTestCase(BaseTestCase): self.assert_raises(KeyError, idx.__delitem__, H(x)) self.assert_equal(len(idx), 50) idx_name = tempfile.NamedTemporaryFile() - idx.write(idx_name.name) - del idx - # Verify file contents - with open(idx_name.name, 'rb') as fd: - self.assert_equal(hashlib.sha256(fd.read()).hexdigest(), sha) - # Make sure we can open the file - idx = cls.read(idx_name.name) - self.assert_equal(len(idx), 50) - for x in range(50, 100): - self.assert_equal(idx[H(x)], make_value(x * 2)) - idx.clear() - self.assert_equal(len(idx), 0) - idx.write(idx_name.name) - del idx - self.assert_equal(len(cls.read(idx_name.name)), 0) + with unopened_tempfile() as filepath: + idx.write(filepath) + del idx + # Verify file contents + with open(filepath, 'rb') as fd: + self.assert_equal(hashlib.sha256(fd.read()).hexdigest(), sha) + # Make sure we can open the file + idx = cls.read(filepath) + self.assert_equal(len(idx), 50) + for x in range(50, 100): + self.assert_equal(idx[H(x)], make_value(x * 2)) + idx.clear() + self.assert_equal(len(idx), 0) + idx.write(filepath) + del idx + self.assert_equal(len(cls.read(filepath)), 0) def test_nsindex(self): self._generic_test(NSIndex, lambda x: (x, x), @@ -81,20 +82,20 @@ class HashIndexTestCase(BaseTestCase): def test_resize(self): n = 2000 # Must be >= MIN_BUCKETS - idx_name = tempfile.NamedTemporaryFile() - idx = NSIndex() - idx.write(idx_name.name) - initial_size = os.path.getsize(idx_name.name) - self.assert_equal(len(idx), 0) - for x in range(n): - idx[H(x)] = x, x - idx.write(idx_name.name) - self.assert_true(initial_size < os.path.getsize(idx_name.name)) - for x in range(n): - del idx[H(x)] - self.assert_equal(len(idx), 0) - idx.write(idx_name.name) - self.assert_equal(initial_size, os.path.getsize(idx_name.name)) + with unopened_tempfile() as filepath: + idx = NSIndex() + idx.write(filepath) + initial_size = os.path.getsize(filepath) + self.assert_equal(len(idx), 0) + for x in range(n): + idx[H(x)] = x, x + idx.write(filepath) + self.assert_true(initial_size < os.path.getsize(filepath)) + for x in range(n): + del idx[H(x)] + self.assert_equal(len(idx), 0) + idx.write(filepath) + self.assert_equal(initial_size, os.path.getsize(filepath)) def test_iteritems(self): idx = NSIndex() diff --git a/src/borg/testsuite/platform.py b/src/borg/testsuite/platform.py index 0ae1458ae..c32ac060a 100644 --- a/src/borg/testsuite/platform.py +++ b/src/borg/testsuite/platform.py @@ -4,7 +4,6 @@ import random import shutil import sys import tempfile -import pwd import unittest from ..platform import acl_get, acl_set, swidth @@ -44,6 +43,7 @@ def fakeroot_detected(): def user_exists(username): try: + import pwd # buildin but not on all OS pwd.getpwnam(username) return True except (KeyError, ValueError):