]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
tar-util: include xattrs in generated tarballs
authorLennart Poettering <lennart@poettering.net>
Thu, 21 Aug 2025 10:28:06 +0000 (12:28 +0200)
committerLennart Poettering <lennart@poettering.net>
Tue, 4 Nov 2025 13:12:39 +0000 (14:12 +0100)
We can already unpack them, let's pack them up to.

src/shared/libarchive-util.c
src/shared/libarchive-util.h
src/shared/tar-util.c

index 961d9f6a03961dcb40f5df381873b9648bbf0a2f..65e9f5a511e27bb643693df8fb5fb08948ffa70c 100644 (file)
@@ -41,6 +41,7 @@ DLSYM_PROTOTYPE(archive_entry_uid) = NULL;
 #if HAVE_LIBARCHIVE_UID_IS_SET
 DLSYM_PROTOTYPE(archive_entry_uid_is_set) = NULL;
 #endif
+DLSYM_PROTOTYPE(archive_entry_xattr_add_entry) = NULL;
 DLSYM_PROTOTYPE(archive_entry_xattr_next) = NULL;
 DLSYM_PROTOTYPE(archive_entry_xattr_reset) = NULL;
 DLSYM_PROTOTYPE(archive_error_string) = NULL;
@@ -105,6 +106,7 @@ int dlopen_libarchive(void) {
 #if HAVE_LIBARCHIVE_UID_IS_SET
                         DLSYM_ARG(archive_entry_uid_is_set),
 #endif
+                        DLSYM_ARG(archive_entry_xattr_add_entry),
                         DLSYM_ARG(archive_entry_xattr_next),
                         DLSYM_ARG(archive_entry_xattr_reset),
                         DLSYM_ARG(archive_error_string),
index 83ee19d27a5ef3cb0e71fb6204883c7ad68d433a..2f40c64d85d2ac6964eda783eb0ed109cb4ef6b1 100644 (file)
@@ -34,6 +34,7 @@ extern DLSYM_PROTOTYPE(archive_entry_set_symlink);
 extern DLSYM_PROTOTYPE(archive_entry_set_uid);
 extern DLSYM_PROTOTYPE(archive_entry_symlink);
 extern DLSYM_PROTOTYPE(archive_entry_uid);
+extern DLSYM_PROTOTYPE(archive_entry_xattr_add_entry);
 extern DLSYM_PROTOTYPE(archive_entry_xattr_next);
 extern DLSYM_PROTOTYPE(archive_entry_xattr_reset);
 extern DLSYM_PROTOTYPE(archive_error_string);
index c75d7eb6668fd023a56c3393c12c468d35ac61d6..95b5ba820c4f5f79b9fe50827c74fa68f59da501 100644 (file)
@@ -13,6 +13,7 @@
 #include "fs-util.h"
 #include "iovec-util.h"
 #include "libarchive-util.h"
+#include "nulstr-util.h"
 #include "path-util.h"
 #include "recurse-dir.h"
 #include "stat-util.h"
@@ -332,6 +333,8 @@ static int archive_entry_read_stat(
                 size_t *n_xa,
                 TarFlags flags) {
 
+        int r;
+
         assert(entry);
 
         /* Fills in all fields that are present in the archive entry. Doesn't change the fields if the entry
@@ -357,16 +360,26 @@ static int archive_entry_read_stat(
         for (;;) {
                 const char *name = NULL;
                 struct iovec data;
-                (void) sym_archive_entry_xattr_next(entry, &name, (const void**) &data.iov_base, &data.iov_len);
-                if (!name)
+                r = sym_archive_entry_xattr_next(entry, &name, (const void**) &data.iov_base, &data.iov_len);
+                if (r != ARCHIVE_OK)
                         break;
 
+                assert(name);
                 if (xattr_is_acl(name))
                         continue;
 
                 if (!FLAGS_SET(flags, TAR_SELINUX) && xattr_is_selinux(name))
                         continue;
 
+                bool duplicate = false;
+                FOREACH_ARRAY(i, *xa, *n_xa)
+                        if (streq(i->name, name)) {
+                                duplicate = true;
+                                break;
+                        }
+                if (duplicate)
+                        continue;
+
                 _cleanup_free_ char *n = strdup(name);
                 if (!n)
                         return log_oom();
@@ -675,6 +688,11 @@ int tar_x(int input_fd, int tree_fd, TarFlags flags) {
         return 0;
 }
 
+struct make_archive_data {
+        struct archive *archive;
+        TarFlags flags;
+};
+
 static int archive_item(
                 RecurseDirEvent event,
                 const char *path,
@@ -684,7 +702,7 @@ static int archive_item(
                 const struct statx *sx,
                 void *userdata) {
 
-        struct archive *a = ASSERT_PTR(userdata);
+        struct make_archive_data *d = ASSERT_PTR(userdata);
         int r;
 
         assert(path);
@@ -745,8 +763,32 @@ static int archive_item(
                 sym_archive_entry_set_symlink(entry, s);
         }
 
-        if (sym_archive_write_header(a, entry) != ARCHIVE_OK)
-                return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to write archive entry header: %s", sym_archive_error_string(a));
+        _cleanup_free_ char *xattrs = NULL;
+        r = flistxattr_malloc(inode_fd, &xattrs);
+        if (r < 0 && !ERRNO_IS_NEG_NOT_SUPPORTED(r) && r != -ENODATA)
+                return log_error_errno(r, "Failed to read xattr list of '%s': %m", path);
+
+        NULSTR_FOREACH(xa, xattrs) {
+                _cleanup_free_ char *buf = NULL;
+                size_t size;
+
+                if (xattr_is_acl(xa))
+                        continue;
+
+                if (!FLAGS_SET(d->flags, TAR_SELINUX) && xattr_is_selinux(xa))
+                        continue;
+
+                r = fgetxattr_malloc(inode_fd, xa, &buf, &size);
+                if (r == -ENODATA) /* deleted by now? ignore... */
+                        continue;
+                if (r < 0)
+                        return log_error_errno(r, "Failed to read xattr '%s' of '%s': %m", xa, path);
+
+                sym_archive_entry_xattr_add_entry(entry, xa, buf, size);
+        }
+
+        if (sym_archive_write_header(d->archive, entry) != ARCHIVE_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to write archive entry header: %s", sym_archive_error_string(d->archive));
 
         if (S_ISREG(sx->stx_mode)) {
                 _cleanup_close_ int data_fd = -EBADF;
@@ -767,9 +809,9 @@ static int archive_item(
                                 break;
 
                         la_ssize_t k;
-                        k = sym_archive_write_data(a, buffer, l);
+                        k = sym_archive_write_data(d->archive, buffer, l);
                         if (k < 0)
-                                return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to write archive data: %s", sym_archive_error_string(a));
+                                return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to write archive data: %s", sym_archive_error_string(d->archive));
                 }
         }
 
@@ -803,7 +845,10 @@ int tar_c(int tree_fd, int output_fd, const char *filename, TarFlags flags) {
                         UINT_MAX,
                         RECURSE_DIR_SORT|RECURSE_DIR_INODE_FD|RECURSE_DIR_TOPLEVEL,
                         archive_item,
-                        a);
+                        &(struct make_archive_data) {
+                                .archive = a,
+                                .flags = flags,
+                        });
         if (r < 0)
                 return log_error_errno(r, "Failed to make archive: %m");