Repository: borgbackup/borg

Issue:
tests missing for FreeBSD acl code
borg has some platform specific code for ACLs on FreeBSD, but looks like there are no tests for that code (we only have ACL tests for Linux and macOS (darwin)).


Discussion:
Dummy file created for these tests. Find [here](https://github.com/borgbackup/borg/pull/7748).
the freebsd64 vagrant machines needs:
- "acls" option in /etc/fstab.
- group "wheel" (not "root") in acls

guess we can't test nfs4 acls, if we do not have a nfs v4 server and such a fs mounted?


Resolving patch:
diff --git a/src/borg/testsuite/platform.py b/src/borg/testsuite/platform.py
--- a/src/borg/testsuite/platform.py
+++ b/src/borg/testsuite/platform.py
@@ -9,32 +9,6 @@
 from . import unopened_tempfile
 from .locking import free_pid  # NOQA
 
-ACCESS_ACL = """
-user::rw-
-user:root:rw-:0
-user:9999:r--:9999
-group::r--
-group:root:r--:0
-group:9999:r--:9999
-mask::rw-
-other::r--
-""".strip().encode(
-    "ascii"
-)
-
-DEFAULT_ACL = """
-user::rw-
-user:root:r--:0
-user:8888:r--:8888
-group::r--
-group:root:r--:0
-group:8888:r--:8888
-mask::rw-
-other::r--
-""".strip().encode(
-    "ascii"
-)
-
 
 def fakeroot_detected():
     return "FAKEROOTKEY" in os.environ
@@ -57,13 +31,22 @@ def are_acls_working():
     with unopened_tempfile() as filepath:
         open(filepath, "w").close()
         try:
-            access = b"user::rw-\ngroup::r--\nmask::rw-\nother::---\nuser:root:rw-:9999\ngroup:root:rw-:9999\n"
+            if is_freebsd:
+                access = b"user::rw-\ngroup::r--\nmask::rw-\nother::---\nuser:root:rw-\n"
+                contained = b"user:root:rw-"
+            elif is_linux:
+                access = b"user::rw-\ngroup::r--\nmask::rw-\nother::---\nuser:root:rw-:0\n"
+                contained = b"user:root:rw-:0"
+            elif is_darwin:
+                return True  # improve?
+            else:
+                return False  # unsupported platform
             acl = {"acl_access": access}
             acl_set(filepath, acl)
             read_acl = {}
             acl_get(filepath, read_acl, os.stat(filepath))
             read_acl_access = read_acl.get("acl_access", None)
-            if read_acl_access and b"user::rw-" in read_acl_access:
+            if read_acl_access and contained in read_acl_access:
                 return True
         except PermissionError:
             pass
diff --git a/src/borg/testsuite/platform_freebsd.py b/src/borg/testsuite/platform_freebsd.py
--- a/src/borg/testsuite/platform_freebsd.py
+++ b/src/borg/testsuite/platform_freebsd.py
@@ -1,30 +1,94 @@
-"""Dummy file for now, will eventually contain FreeBSD ACL tests."""
-import pytest
+import os
+import tempfile
 
-from .platform import skipif_not_freebsd
+from ..platform import acl_get, acl_set
+from .platform import skipif_not_freebsd, skipif_acls_not_working
 
 # set module-level skips
 pytestmark = [skipif_not_freebsd]
 
 
-def get_acl():
-    return
+ACCESS_ACL = """\
+user::rw-
+user:root:rw-
+user:9999:r--
+group::r--
+group:wheel:r--
+group:9999:r--
+mask::rw-
+other::r--
+""".encode(
+    "ascii"
+)
 
+DEFAULT_ACL = """\
+user::rw-
+user:root:r--
+user:8888:r--
+group::r--
+group:wheel:r--
+group:8888:r--
+mask::rw-
+other::r--
+""".encode(
+    "ascii"
+)
 
-def get_set_acl():
-    return
 
+def get_acl(path, numeric_ids=False):
+    item = {}
+    acl_get(path, item, os.stat(path), numeric_ids=numeric_ids)
+    return item
 
-@pytest.mark.skip(reason="not yet implemented")
+
+def set_acl(path, access=None, default=None, nfs4=None, numeric_ids=False):
+    item = {"acl_access": access, "acl_default": default, "acl_nfs4": nfs4}
+    acl_set(path, item, numeric_ids=numeric_ids)
+
+
+@skipif_acls_not_working
 def test_access_acl():
-    pass
+    file1 = tempfile.NamedTemporaryFile()
+    set_acl(
+        file1.name,
+        access=b"user::rw-\ngroup::r--\nmask::rw-\nother::---\nuser:root:rw-\ngroup:wheel:rw-\n",
+        numeric_ids=False,
+    )
+    acl_access_names = get_acl(file1.name, numeric_ids=False)["acl_access"]
+    assert b"user:root:rw-" in acl_access_names
+    assert b"group:wheel:rw-" in acl_access_names
+    acl_access_ids = get_acl(file1.name, numeric_ids=True)["acl_access"]
+    assert b"user:0:rw-" in acl_access_ids
+    assert b"group:0:rw-" in acl_access_ids
+
+    file2 = tempfile.NamedTemporaryFile()
+    set_acl(
+        file2.name, access=b"user::rw-\ngroup::r--\nmask::rw-\nother::---\nuser:0:rw-\ngroup:0:rw-\n", numeric_ids=True
+    )
+    acl_access_names = get_acl(file2.name, numeric_ids=False)["acl_access"]
+    assert b"user:root:rw-" in acl_access_names
+    assert b"group:wheel:rw-" in acl_access_names
+    acl_access_ids = get_acl(file2.name, numeric_ids=True)["acl_access"]
+    assert b"user:0:rw-" in acl_access_ids
+    assert b"group:0:rw-" in acl_access_ids
+
+    file3 = tempfile.NamedTemporaryFile()
+    set_acl(
+        file3.name,
+        access=b"user::rw-\ngroup::r--\nmask::rw-\nother::---\nuser:root:rw-:9999\ngroup:wheel:rw-:9999\n",
+        numeric_ids=True,
+    )
+    acl_access_ids = get_acl(file3.name, numeric_ids=True)["acl_access"]
+    assert b"user:9999:rw-" in acl_access_ids
+    assert b"group:9999:rw-" in acl_access_ids
 
 
-@pytest.mark.skip(reason="not yet implemented")
+@skipif_acls_not_working
 def test_default_acl():
-    pass
+    tmpdir = tempfile.mkdtemp()
+    set_acl(tmpdir, access=ACCESS_ACL, default=DEFAULT_ACL)
+    assert get_acl(tmpdir)["acl_access"] == ACCESS_ACL
+    assert get_acl(tmpdir)["acl_default"] == DEFAULT_ACL
 
 
-@pytest.mark.skip(reason="not yet implemented")
-def test_nfs4_acl():
-    pass
+# nfs4 acls testing not implemented.
diff --git a/src/borg/testsuite/platform_linux.py b/src/borg/testsuite/platform_linux.py
--- a/src/borg/testsuite/platform_linux.py
+++ b/src/borg/testsuite/platform_linux.py
@@ -2,19 +2,39 @@
 import tempfile
 
 from ..platform import acl_get, acl_set
-from .platform import (
-    DEFAULT_ACL,
-    ACCESS_ACL,
-    skipif_not_linux,
-    skipif_fakeroot_detected,
-    skipif_acls_not_working,
-    skipif_no_ubel_user,
-)
+from .platform import skipif_not_linux, skipif_fakeroot_detected, skipif_acls_not_working, skipif_no_ubel_user
 
 # set module-level skips
 pytestmark = [skipif_not_linux, skipif_fakeroot_detected]
 
 
+ACCESS_ACL = """\
+user::rw-
+user:root:rw-:0
+user:9999:r--:9999
+group::r--
+group:root:r--:0
+group:9999:r--:9999
+mask::rw-
+other::r--\
+""".encode(
+    "ascii"
+)
+
+DEFAULT_ACL = """\
+user::rw-
+user:root:r--:0
+user:8888:r--:8888
+group::r--
+group:root:r--:0
+group:8888:r--:8888
+mask::rw-
+other::r--\
+""".encode(
+    "ascii"
+)
+
+
 def get_acl(path, numeric_ids=False):
     item = {}
     acl_get(path, item, os.stat(path), numeric_ids=numeric_ids)


Test patch: