NetBSD: copy xattr implementation of FreeBSD, fixes #1332

This commit is contained in:
Thomas Waldmann 2025-11-12 01:49:04 +01:00
parent 9a0122995c
commit 0286fb6dc4
No known key found for this signature in database
GPG key ID: 243ACFA951F78E01
3 changed files with 108 additions and 2 deletions

View file

@ -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)

View file

@ -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

View file

@ -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)