瀏覽代碼

NetBSD: copy xattr implementation of FreeBSD, fixes #1332

Thomas Waldmann 3 周之前
父節點
當前提交
0286fb6dc4
共有 3 個文件被更改,包括 108 次插入2 次删除
  1. 8 1
      setup.py
  2. 11 1
      src/borg/platform/__init__.py
  3. 89 0
      src/borg/platform/netbsd.pyx

+ 8 - 1
setup.py

@@ -61,6 +61,7 @@ platform_linux_source = "src/borg/platform/linux.pyx"
 platform_syncfilerange_source = "src/borg/platform/syncfilerange.pyx"
 platform_darwin_source = "src/borg/platform/darwin.pyx"
 platform_freebsd_source = "src/borg/platform/freebsd.pyx"
+platform_netbsd_source = "src/borg/platform/netbsd.pyx"
 platform_windows_source = "src/borg/platform/windows.pyx"
 
 cython_sources = [
@@ -76,6 +77,7 @@ cython_sources = [
     platform_linux_source,
     platform_syncfilerange_source,
     platform_freebsd_source,
+    platform_netbsd_source,
     platform_darwin_source,
     platform_windows_source,
 ]
@@ -197,6 +199,7 @@ if not on_rtd:
         "borg.platform.syncfilerange", [platform_syncfilerange_source], extra_compile_args=cflags
     )
     freebsd_ext = Extension("borg.platform.freebsd", [platform_freebsd_source], extra_compile_args=cflags)
+    netbsd_ext = Extension("borg.platform.netbsd", [platform_netbsd_source], extra_compile_args=cflags)
     darwin_ext = Extension("borg.platform.darwin", [platform_darwin_source], extra_compile_args=cflags)
     windows_ext = Extension("borg.platform.windows", [platform_windows_source], extra_compile_args=cflags)
 
@@ -209,6 +212,8 @@ if not on_rtd:
         ext_modules.append(syncfilerange_ext)
     elif sys.platform.startswith("freebsd"):
         ext_modules.append(freebsd_ext)
+    elif sys.platform.startswith("netbsd"):
+        ext_modules.append(netbsd_ext)
     elif sys.platform == "darwin":
         ext_modules.append(darwin_ext)
 
@@ -230,7 +235,9 @@ if not on_rtd:
 
         # generate C code from Cython for ALL supported platforms, so we have them in the sdist.
         # the sdist does not require Cython at install time, so we need all as C.
-        cythonize([posix_ext, linux_ext, syncfilerange_ext, freebsd_ext, darwin_ext, windows_ext], **cython_opts)
+        cythonize(
+            [posix_ext, linux_ext, syncfilerange_ext, freebsd_ext, netbsd_ext, darwin_ext, windows_ext], **cython_opts
+        )
         # generate C code from Cython for THIS platform (and for all platform-independent Cython parts).
         ext_modules = cythonize(ext_modules, **cython_opts)
 

+ 11 - 1
src/borg/platform/__init__.py

@@ -4,7 +4,7 @@ Platform-specific APIs.
 Public APIs are documented in platform.base.
 """
 
-from ..platformflags import is_win32, is_linux, is_freebsd, is_darwin, is_cygwin
+from ..platformflags import is_win32, is_linux, is_freebsd, is_netbsd, is_darwin, is_cygwin
 
 from .base import ENOATTR, API_VERSION
 from .base import SaveFile, sync_dir, fdatasync, safe_fadvise
@@ -31,6 +31,16 @@ elif is_freebsd:  # pragma: freebsd only
     from .posix import swidth
     from .posix import get_errno
     from .posix import uid2user, user2uid, gid2group, group2gid, getosusername
+elif is_netbsd:  # pragma: netbsd only
+    from .netbsd import API_VERSION as OS_API_VERSION
+    from .netbsd import listxattr, getxattr, setxattr
+    from .base import acl_get, acl_set
+    from .base import set_flags, get_flags
+    from .base import SyncFile
+    from .posix import process_alive, local_pid_alive
+    from .posix import swidth
+    from .posix import get_errno
+    from .posix import uid2user, user2uid, gid2group, group2gid, getosusername
 elif is_darwin:  # pragma: darwin only
     from .darwin import API_VERSION as OS_API_VERSION
     from .darwin import listxattr, getxattr, setxattr

+ 89 - 0
src/borg/platform/netbsd.pyx

@@ -0,0 +1,89 @@
+from .xattr import _listxattr_inner, _getxattr_inner, _setxattr_inner, split_lstring
+
+API_VERSION = '1.2_05'
+
+cdef extern from "sys/extattr.h":
+    ssize_t c_extattr_list_file "extattr_list_file" (const char *path, int attrnamespace, void *data, size_t nbytes)
+    ssize_t c_extattr_list_link "extattr_list_link" (const char *path, int attrnamespace, void *data, size_t nbytes)
+    ssize_t c_extattr_list_fd "extattr_list_fd" (int fd, int attrnamespace, void *data, size_t nbytes)
+
+    ssize_t c_extattr_get_file "extattr_get_file" (const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes)
+    ssize_t c_extattr_get_link "extattr_get_link" (const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes)
+    ssize_t c_extattr_get_fd "extattr_get_fd" (int fd, int attrnamespace, const char *attrname, void *data, size_t nbytes)
+
+    int c_extattr_set_file "extattr_set_file" (const char *path, int attrnamespace, const char *attrname, const void *data, size_t nbytes)
+    int c_extattr_set_link "extattr_set_link" (const char *path, int attrnamespace, const char *attrname, const void *data, size_t nbytes)
+    int c_extattr_set_fd "extattr_set_fd" (int fd, int attrnamespace, const char *attrname, const void *data, size_t nbytes)
+
+    int EXTATTR_NAMESPACE_USER
+
+
+# On NetBSD, 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 b"namespace.name") into namespace and name.
+    # If there is no namespace given in ns_name, default to default_ns.
+    # 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 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_id, <char *> buf, size)
+        else:
+            if follow_symlinks:
+                return c_extattr_list_file(path, ns_id, <char *> buf, size)
+            else:
+                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 [ns + b"." + name for name in split_lstring(buf[:n]) if name]
+
+
+def getxattr(path, name, *, follow_symlinks=False):
+    def func(path, name, buf, size):
+        if isinstance(path, int):
+            return c_extattr_get_fd(path, ns_id, name, <char *> buf, size)
+        else:
+            if follow_symlinks:
+                return c_extattr_get_file(path, ns_id, name, <char *> buf, size)
+            else:
+                return c_extattr_get_link(path, ns_id, name, <char *> buf, size)
+
+    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):
+    def func(path, name, value, size):
+        if isinstance(path, int):
+            return c_extattr_set_fd(path, ns_id, name, <char *> value, size)
+        else:
+            if follow_symlinks:
+                return c_extattr_set_file(path, ns_id, name, <char *> value, size)
+            else:
+                return c_extattr_set_link(path, ns_id, name, <char *> value, size)
+
+    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)