Merge pull request #3960 from ThomasWaldmann/acl-fd-based

ACLs fd-based
This commit is contained in:
TW 2018-07-07 17:28:11 +02:00 committed by GitHub
commit 2dd22df751
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 76 additions and 32 deletions

View File

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

View File

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

View File

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

View File

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