Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/borg/archive.py
Original file line number Diff line number Diff line change
Expand Up @@ -719,7 +719,7 @@ def restore_attrs(self, path, item, symlink=False, fd=None):
except OSError:
# some systems don't support calling utime on a symlink
pass
acl_set(path, item, self.numeric_owner)
acl_set(path, item, self.numeric_owner, fd=fd)
# chown removes Linux capabilities, so set the extended attributes at the end, after chown, since they include
# the Linux capabilities in the "security.capability" attribute.
warning = xattr.set_all(fd or path, item.get('xattrs', {}), follow_symlinks=False)
Expand Down Expand Up @@ -954,7 +954,7 @@ def stat_ext_attrs(self, st, path, fd=None):
xattrs = xattr.get_all(fd or path, follow_symlinks=False)
if not self.nobsdflags:
bsdflags = get_flags(path, st)
acl_get(path, attrs, st, self.numeric_owner)
acl_get(path, attrs, st, self.numeric_owner, fd=fd)
if xattrs:
attrs['xattrs'] = StableDict(xattrs)
if bsdflags:
Expand Down
21 changes: 16 additions & 5 deletions src/borg/platform/darwin.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ cdef extern from "sys/acl.h":

int acl_free(void *obj)
acl_t acl_get_link_np(const char *path, int type)
acl_t acl_get_fd_np(int fd, int type)
int acl_set_link_np(const char *path, int type, acl_t acl)
int acl_set_fd_np(int fd, acl_t acl, int type)
acl_t acl_from_text(const char *buf)
char *acl_to_text(acl_t acl, ssize_t *len_p)
int ACL_TYPE_EXTENDED
Expand Down Expand Up @@ -108,11 +110,16 @@ def _remove_non_numeric_identifier(acl):
return safe_encode('\n'.join(entries))


def acl_get(path, item, st, numeric_owner=False):
def acl_get(path, item, st, numeric_owner=False, fd=None):
cdef acl_t acl = NULL
cdef char *text = NULL
if isinstance(path, str):
path = os.fsencode(path)
try:
acl = acl_get_link_np(<bytes>os.fsencode(path), ACL_TYPE_EXTENDED)
if fd is not None:
acl = acl_get_fd_np(fd, ACL_TYPE_EXTENDED)
else:
acl = acl_get_link_np(path, ACL_TYPE_EXTENDED)
if acl == NULL:
return
text = acl_to_text(acl, NULL)
Expand All @@ -127,7 +134,7 @@ def acl_get(path, item, st, numeric_owner=False):
acl_free(acl)


def acl_set(path, item, numeric_owner=False):
def acl_set(path, item, numeric_owner=False, fd=None):
cdef acl_t acl = NULL
acl_text = item.get('acl_extended')
if acl_text is not None:
Expand All @@ -138,7 +145,11 @@ def acl_set(path, item, numeric_owner=False):
acl = acl_from_text(<bytes>_remove_numeric_id_if_possible(acl_text))
if acl == NULL:
return
if acl_set_link_np(<bytes>os.fsencode(path), ACL_TYPE_EXTENDED, acl):
return
if isinstance(path, str):
path = os.fsencode(path)
if fd is not None:
acl_set_fd_np(fd, acl, ACL_TYPE_EXTENDED)
else:
acl_set_link_np(path, ACL_TYPE_EXTENDED, acl)
finally:
acl_free(acl)
40 changes: 25 additions & 15 deletions src/borg/platform/freebsd.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ cdef extern from "sys/acl.h":

int acl_free(void *obj)
acl_t acl_get_link_np(const char *path, int type)
acl_t acl_get_fd_np(int fd, int type)
int acl_set_link_np(const char *path, int type, acl_t acl)
int acl_set_fd_np(int fd, acl_t acl, int type)
acl_t acl_from_text(const char *buf)
char *acl_to_text_np(acl_t acl, ssize_t *len, int flags)
int ACL_TEXT_NUMERIC_IDS
Expand Down Expand Up @@ -89,10 +91,13 @@ def setxattr(path, name, value, *, follow_symlinks=True):
_setxattr_inner(func, path, name, value)


cdef _get_acl(p, type, item, attribute, int flags):
cdef _get_acl(p, type, item, attribute, flags, fd=None):
cdef acl_t acl
cdef char *text
acl = acl_get_link_np(p, type)
if fd is not None:
acl = acl_get_fd_np(fd, type)
else:
acl = acl_get_link_np(p, type)
if acl:
text = acl_to_text_np(acl, NULL, flags)
if text:
Expand All @@ -101,25 +106,26 @@ cdef _get_acl(p, type, item, attribute, int flags):
acl_free(acl)


def acl_get(path, item, st, numeric_owner=False):
def acl_get(path, item, st, numeric_owner=False, fd=None):
"""Saves ACL Entries

If `numeric_owner` is True the user/group field is not preserved only uid/gid
"""
cdef int flags = ACL_TEXT_APPEND_ID
p = os.fsencode(path)
ret = lpathconf(p, _PC_ACL_NFS4)
if isinstance(path, str):
path = os.fsencode(path)
ret = lpathconf(path, _PC_ACL_NFS4)
if ret < 0 and errno == EINVAL:
return
flags |= ACL_TEXT_NUMERIC_IDS if numeric_owner else 0
if ret > 0:
_get_acl(p, ACL_TYPE_NFS4, item, 'acl_nfs4', flags)
_get_acl(path, ACL_TYPE_NFS4, item, 'acl_nfs4', flags, fd=fd)
else:
_get_acl(p, ACL_TYPE_ACCESS, item, 'acl_access', flags)
_get_acl(p, ACL_TYPE_DEFAULT, item, 'acl_default', flags)
_get_acl(path, ACL_TYPE_ACCESS, item, 'acl_access', flags, fd=fd)
_get_acl(path, ACL_TYPE_DEFAULT, item, 'acl_default', flags, fd=fd)


cdef _set_acl(p, type, item, attribute, numeric_owner=False):
cdef _set_acl(p, type, item, attribute, numeric_owner=False, fd=None):
cdef acl_t acl
text = item.get(attribute)
if text:
Expand All @@ -129,7 +135,10 @@ cdef _set_acl(p, type, item, attribute, numeric_owner=False):
text = posix_acl_use_stored_uid_gid(text)
acl = acl_from_text(<bytes>text)
if acl:
acl_set_link_np(p, type, acl)
if fd is not None:
acl_set_fd_np(fd, acl, type)
else:
acl_set_link_np(p, type, acl)
acl_free(acl)


Expand All @@ -147,13 +156,14 @@ cdef _nfs4_use_stored_uid_gid(acl):
return safe_encode('\n'.join(entries))


def acl_set(path, item, numeric_owner=False):
def acl_set(path, item, numeric_owner=False, fd=None):
"""Restore ACL Entries

If `numeric_owner` is True the stored uid/gid is used instead
of the user/group names
"""
p = os.fsencode(path)
_set_acl(p, ACL_TYPE_NFS4, item, 'acl_nfs4', numeric_owner)
_set_acl(p, ACL_TYPE_ACCESS, item, 'acl_access', numeric_owner)
_set_acl(p, ACL_TYPE_DEFAULT, item, 'acl_default', numeric_owner)
if isinstance(path, str):
path = os.fsencode(path)
_set_acl(path, ACL_TYPE_NFS4, item, 'acl_nfs4', numeric_owner, fd=fd)
_set_acl(path, ACL_TYPE_ACCESS, item, 'acl_access', numeric_owner, fd=fd)
_set_acl(path, ACL_TYPE_DEFAULT, item, 'acl_default', numeric_owner, fd=fd)
43 changes: 33 additions & 10 deletions src/borg/platform/linux.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,15 @@ cdef extern from "sys/acl.h":

int acl_free(void *obj)
acl_t acl_get_file(const char *path, int type)
acl_t acl_get_fd(int fd)
int acl_set_file(const char *path, int type, acl_t acl)
int acl_set_fd(int fd, acl_t acl)
acl_t acl_from_text(const char *buf)
char *acl_to_text(acl_t acl, ssize_t *len)

cdef extern from "acl/libacl.h":
int acl_extended_file(const char *path)
int acl_extended_fd(int fd)

cdef extern from "fcntl.h":
int sync_file_range(int fd, int64_t offset, int64_t nbytes, unsigned int flags)
Expand Down Expand Up @@ -221,26 +224,37 @@ cdef acl_numeric_ids(acl):
return safe_encode('\n'.join(entries))


def acl_get(path, item, st, numeric_owner=False):
def acl_get(path, item, st, numeric_owner=False, fd=None):
cdef acl_t default_acl = NULL
cdef acl_t access_acl = NULL
cdef char *default_text = NULL
cdef char *access_text = NULL

p = <bytes>os.fsencode(path)
if stat.S_ISLNK(st.st_mode) or acl_extended_file(p) <= 0:
if fd is None and isinstance(path, str):
path = os.fsencode(path)
if stat.S_ISLNK(st.st_mode):
return
if (fd is not None and acl_extended_fd(fd) <= 0
or
fd is None and acl_extended_file(path) <= 0):
return
if numeric_owner:
converter = acl_numeric_ids
else:
converter = acl_append_numeric_ids
try:
access_acl = acl_get_file(p, ACL_TYPE_ACCESS)
if fd is not None:
# we only have a fd for FILES (not other fs objects), so we can get the access_acl:
assert stat.S_ISREG(st.st_mode)
access_acl = acl_get_fd(fd)
else:
# if we have no fd, it can be anything
access_acl = acl_get_file(path, ACL_TYPE_ACCESS)
default_acl = acl_get_file(path, ACL_TYPE_DEFAULT)
if access_acl:
access_text = acl_to_text(access_acl, NULL)
if access_text:
item['acl_access'] = converter(access_text)
default_acl = acl_get_file(p, ACL_TYPE_DEFAULT)
if default_acl:
default_text = acl_to_text(default_acl, NULL)
if default_text:
Expand All @@ -252,32 +266,41 @@ def acl_get(path, item, st, numeric_owner=False):
acl_free(access_acl)


def acl_set(path, item, numeric_owner=False):
def acl_set(path, item, numeric_owner=False, fd=None):
cdef acl_t access_acl = NULL
cdef acl_t default_acl = NULL

p = <bytes>os.fsencode(path)
if fd is None and isinstance(path, str):
path = os.fsencode(path)
if numeric_owner:
converter = posix_acl_use_stored_uid_gid
else:
converter = acl_use_local_uid_gid
access_text = item.get('acl_access')
default_text = item.get('acl_default')
if access_text:
try:
access_acl = acl_from_text(<bytes>converter(access_text))
if access_acl:
acl_set_file(p, ACL_TYPE_ACCESS, access_acl)
if fd is not None:
acl_set_fd(fd, access_acl)
else:
acl_set_file(path, ACL_TYPE_ACCESS, access_acl)
finally:
acl_free(access_acl)
default_text = item.get('acl_default')
if default_text:
try:
default_acl = acl_from_text(<bytes>converter(default_text))
if default_acl:
acl_set_file(p, ACL_TYPE_DEFAULT, default_acl)
# default acls apply only to directories
if False and fd is not None: # Linux API seems to not support this
acl_set_fd(fd, default_acl)
else:
acl_set_file(path, ACL_TYPE_DEFAULT, default_acl)
finally:
acl_free(default_acl)


cdef _sync_file_range(fd, offset, length, flags):
assert offset & PAGE_MASK == 0, "offset %d not page-aligned" % offset
assert length & PAGE_MASK == 0, "length %d not page-aligned" % length
Expand Down