Browse Source

acls: use fd instead of path

Thomas Waldmann 7 năm trước cách đây
mục cha
commit
7e47e68e29

+ 2 - 2
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:

+ 12 - 5
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)

+ 20 - 12
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(<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)
 
 
@@ -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)

+ 29 - 9
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(<bytes>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(<bytes>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)