Browse Source

Merge pull request #7442 from ThomasWaldmann/freebsd-xattr-fixes-master

xattrs: fix namespace processing on FreeBSD, fixes #6997
TW 2 years ago
parent
commit
270b705248
1 changed files with 43 additions and 25 deletions
  1. 43 25
      src/borg/platform/freebsd.pyx

+ 43 - 25
src/borg/platform/freebsd.pyx

@@ -50,59 +50,77 @@ cdef extern from "unistd.h":
     int _PC_ACL_NFS4
 
 
-def listxattr(path, *, follow_symlinks=False):
-    ns, prefix = EXTATTR_NAMESPACE_USER, b'user.'
+# On FreeBSD, borg currently only deals with the USER namespace as it is unclear
+# whether (and if so, how exactly) it should deal with the SYSTEM namespace.
+NS_ID_MAP = {b"user": EXTATTR_NAMESPACE_USER, }
+
+
+def split_ns(ns_name, default_ns):
+    # split ns_name (which is in the form of b"namespace.name") into namespace and name.
+    # if there is no namespace given in ns_name, default to default_ns.
+    # note:
+    # borg < 1.1.10 on FreeBSD did not prefix the namespace to the names, see #3952.
+    # we also need to deal with "unexpected" namespaces here, they could come
+    # from borg archives made on other operating systems.
+    ns_name_tuple = ns_name.split(b".", 1)
+    if len(ns_name_tuple) == 2:
+        # we have a namespace prefix in the given name
+        ns, name = ns_name_tuple
+    else:
+        # no namespace given in ns_name (== no dot found), maybe data coming from an old borg archive.
+        ns, name = default_ns, ns_name
+    return ns, name
+
 
+def listxattr(path, *, follow_symlinks=False):
     def func(path, buf, size):
         if isinstance(path, int):
-            return c_extattr_list_fd(path, ns, <char *> buf, size)
+            return c_extattr_list_fd(path, ns_id, <char *> buf, size)
         else:
             if follow_symlinks:
-                return c_extattr_list_file(path, ns, <char *> buf, size)
+                return c_extattr_list_file(path, ns_id, <char *> buf, size)
             else:
-                return c_extattr_list_link(path, ns, <char *> buf, size)
+                return c_extattr_list_link(path, ns_id, <char *> buf, size)
 
+    ns = b"user"
+    ns_id = NS_ID_MAP[ns]
     n, buf = _listxattr_inner(func, path)
-    return [prefix + name for name in split_lstring(buf[:n]) if name]
+    return [ns + b"." + name for name in split_lstring(buf[:n]) if name]
 
 
 def getxattr(path, name, *, follow_symlinks=False):
-    ns, prefix = EXTATTR_NAMESPACE_USER, b'user.'
-
     def func(path, name, buf, size):
         if isinstance(path, int):
-            return c_extattr_get_fd(path, ns, name, <char *> buf, size)
+            return c_extattr_get_fd(path, ns_id, name, <char *> buf, size)
         else:
             if follow_symlinks:
-                return c_extattr_get_file(path, ns, name, <char *> buf, size)
+                return c_extattr_get_file(path, ns_id, name, <char *> buf, size)
             else:
-                return c_extattr_get_link(path, ns, name, <char *> buf, size)
+                return c_extattr_get_link(path, ns_id, name, <char *> buf, size)
 
-    # strip namespace if there, but ignore if not there.
-    # older borg / attic versions did not prefix the namespace to the names.
-    if name.startswith(prefix):
-        name = name[len(prefix):]
+    ns, name = split_ns(name, b"user")
+    ns_id = NS_ID_MAP[ns]  # this will raise a KeyError it the namespace is unsupported
     n, buf = _getxattr_inner(func, path, name)
     return bytes(buf[:n])
 
 
 def setxattr(path, name, value, *, follow_symlinks=False):
-    ns, prefix = EXTATTR_NAMESPACE_USER, b'user.'
-
     def func(path, name, value, size):
         if isinstance(path, int):
-            return c_extattr_set_fd(path, ns, name, <char *> value, size)
+            return c_extattr_set_fd(path, ns_id, name, <char *> value, size)
         else:
             if follow_symlinks:
-                return c_extattr_set_file(path, ns, name, <char *> value, size)
+                return c_extattr_set_file(path, ns_id, name, <char *> value, size)
             else:
-                return c_extattr_set_link(path, ns, name, <char *> value, size)
+                return c_extattr_set_link(path, ns_id, name, <char *> value, size)
 
-    # strip namespace if there, but ignore if not there.
-    # older borg / attic versions did not prefix the namespace to the names.
-    if name.startswith(prefix):
-        name = name[len(prefix):]
-    _setxattr_inner(func, path, name, value)
+    ns, name = split_ns(name, b"user")
+    try:
+        ns_id = NS_ID_MAP[ns]  # this will raise a KeyError it the namespace is unsupported
+    except KeyError:
+        pass
+    else:
+        _setxattr_inner(func, path, name, value)
 
 
 cdef _get_acl(p, type, item, attribute, flags, fd=None):