From e86fde53647b20bc645d5f92503a8989474f0680 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Ketelaars?= Date: Mon, 4 Apr 2022 06:47:47 +0200 Subject: [PATCH] Fix OpenBSD symlink mode test failure (#2055) OpenBSD does not have `lchmod()` causing `os.lchmod` to be unavailable on this platform. As a result ArchiverTestCase::test_basic_functionality fails when run manually (#2055). OpenBSD does have `fchmodat()`, which has a flag that makes it behave like `lchmod()`. In Python this can be used via `os.chmod(path, mode, follow_symlinks=False)`. As of Python 3.3 `os.lchmod(path, mode)` is equivalent to `os.chmod(path, mode, follow_symlinks=False)`. As such, switching to the latter is preferred as it enables more platforms to do the right thing. --- src/borg/archive.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/borg/archive.py b/src/borg/archive.py index d6fa3b105..7e16afb93 100644 --- a/src/borg/archive.py +++ b/src/borg/archive.py @@ -49,7 +49,6 @@ from .remote import cache_if_remote from .repository import Repository, LIST_SCAN_LIMIT -has_lchmod = hasattr(os, 'lchmod') has_link = hasattr(os, 'link') @@ -877,10 +876,18 @@ def restore_attrs(self, path, item, symlink=False, fd=None): 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) + else: + # To check whether a particular function in the os module accepts False for its + # follow_symlinks parameter, the in operator on supports_follow_symlinks should be + # used. However, os.chmod is special as some platforms without a working lchmod() do + # have fchmodat(), which has a flag that makes it behave like lchmod(). fchmodat() + # is ignored when deciding whether or not os.chmod should be set in + # os.supports_follow_symlinks. Work around this by using try/except. + try: + os.chmod(path, item.mode, follow_symlinks=False) + except NotImplementedError: + if not symlink: + os.chmod(path, item.mode) mtime = item.mtime if 'atime' in item: atime = item.atime