diff --git a/src/borg/archive.py b/src/borg/archive.py index adfcf7a28..020193ff0 100644 --- a/src/borg/archive.py +++ b/src/borg/archive.py @@ -719,7 +719,7 @@ Utilization of max. archive size: {csize_max:.0%} 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) @@ -954,7 +954,7 @@ class MetadataCollector: 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: diff --git a/src/borg/platform/darwin.pyx b/src/borg/platform/darwin.pyx index e9d142160..ecd287ec7 100644 --- a/src/borg/platform/darwin.pyx +++ b/src/borg/platform/darwin.pyx @@ -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 @@ -108,13 +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(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) @@ -129,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: @@ -142,7 +147,9 @@ def acl_set(path, item, numeric_owner=False): return if isinstance(path, str): path = os.fsencode(path) - if acl_set_link_np(path, ACL_TYPE_EXTENDED, acl): - return + 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) diff --git a/src/borg/platform/freebsd.pyx b/src/borg/platform/freebsd.pyx index be0d47864..92ee430a2 100644 --- a/src/borg/platform/freebsd.pyx +++ b/src/borg/platform/freebsd.pyx @@ -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 @@ -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: @@ -101,7 +106,7 @@ 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 @@ -114,13 +119,13 @@ def acl_get(path, item, st, numeric_owner=False): return flags |= ACL_TEXT_NUMERIC_IDS if numeric_owner else 0 if ret > 0: - _get_acl(path, ACL_TYPE_NFS4, item, 'acl_nfs4', flags) + _get_acl(path, ACL_TYPE_NFS4, item, 'acl_nfs4', flags, fd=fd) else: - _get_acl(path, ACL_TYPE_ACCESS, item, 'acl_access', flags) - _get_acl(path, 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: @@ -130,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(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) @@ -148,7 +156,7 @@ 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 @@ -156,6 +164,6 @@ def acl_set(path, item, numeric_owner=False): """ if isinstance(path, str): path = os.fsencode(path) - _set_acl(path, ACL_TYPE_NFS4, item, 'acl_nfs4', numeric_owner) - _set_acl(path, ACL_TYPE_ACCESS, item, 'acl_access', numeric_owner) - _set_acl(path, ACL_TYPE_DEFAULT, item, 'acl_default', numeric_owner) + _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) diff --git a/src/borg/platform/linux.pyx b/src/borg/platform/linux.pyx index 22051d45c..41476ed02 100644 --- a/src/borg/platform/linux.pyx +++ b/src/borg/platform/linux.pyx @@ -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) @@ -221,27 +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 - if isinstance(path, str): + if fd is None and isinstance(path, str): path = os.fsencode(path) - if stat.S_ISLNK(st.st_mode) or acl_extended_file(path) <= 0: + 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(path, 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(path, ACL_TYPE_DEFAULT) if default_acl: default_text = acl_to_text(default_acl, NULL) if default_text: @@ -253,11 +266,11 @@ 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 - if isinstance(path, str): + if fd is None and isinstance(path, str): path = os.fsencode(path) if numeric_owner: converter = posix_acl_use_stored_uid_gid @@ -268,7 +281,10 @@ def acl_set(path, item, numeric_owner=False): try: access_acl = acl_from_text(converter(access_text)) if access_acl: - acl_set_file(path, 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') @@ -276,7 +292,11 @@ def acl_set(path, item, numeric_owner=False): try: default_acl = acl_from_text(converter(default_text)) if default_acl: - acl_set_file(path, 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)