diff --git a/src/borg/testsuite/__init__.py b/src/borg/testsuite/__init__.py index ea7c464786..6339291441 100644 --- a/src/borg/testsuite/__init__.py +++ b/src/borg/testsuite/__init__.py @@ -131,6 +131,34 @@ def is_root(): return os.getuid() == 0 +@functools.lru_cache +def can_revoke_read_access(): + """Return True if taking away our own read permissions for a file really works. + + Highly privileged users can read files no matter what the permissions say: root ignores + the permission bits, and on cygwin a user holding SeBackupPrivilege bypasses the ACL + checks (cygwin always opens files with FILE_OPEN_FOR_BACKUP_INTENT). Tests that need an + unreadable file must be skipped for such users. + """ + if is_win32: + # there, read access is revoked via a deny ACE (see the icacls calls in the tests) + # rather than via chmod, and such an ACE is honoured also for privileged users. + return True + with unopened_tempfile() as filepath: + with open(filepath, "wb") as fd: + fd.write(b"secret") + os.chmod(filepath, 0o000) + try: + with open(filepath, "rb") as fd: + fd.read(1) + except OSError: + return True # good, we can not read it any more. + else: + return False + finally: + os.chmod(filepath, 0o600) # make sure the tempfile can be cleaned up. + + @functools.lru_cache def are_symlinks_supported(): with unopened_tempfile() as filepath: diff --git a/src/borg/testsuite/archiver/create_cmd_test.py b/src/borg/testsuite/archiver/create_cmd_test.py index c8d8077f22..30edb17d46 100644 --- a/src/borg/testsuite/archiver/create_cmd_test.py +++ b/src/borg/testsuite/archiver/create_cmd_test.py @@ -27,7 +27,7 @@ is_utime_fully_supported, is_birthtime_fully_supported, same_ts_ns, - is_root, + can_revoke_read_access, granularity_sleep, ) from . import ( @@ -308,7 +308,7 @@ def test_create_erroneous_file(archivers, request): assert "input/file3" in out -@pytest.mark.skipif(is_root(), reason="test must not be run as (fake)root") +@pytest.mark.skipif(not can_revoke_read_access(), reason="can not revoke our own read permissions") def test_create_no_permission_file(archivers, request): archiver = request.getfixturevalue(archivers) file_path = os.path.join(archiver.input_path, "file") @@ -317,7 +317,9 @@ def test_create_no_permission_file(archivers, request): create_regular_file(archiver.input_path, file_path + "3", size=1000) # revoke read permissions on file2 for everybody, including us: if is_win32: - subprocess.run(["icacls.exe", file_path + "2", "/deny", "everyone:(R)"]) + # "*S-1-1-0" is the well-known SID of the "Everyone" group. Using the SID instead of the + # group name keeps this working on non-English Windows installations. + subprocess.run(["icacls.exe", file_path + "2", "/deny", "*S-1-1-0:(R)"]) else: # note: this will NOT take away read permissions for root os.chmod(file_path + "2", 0o000)