From d4d94fcebaeee2085a4b72bf2e2f1477afebfb5b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 21 Aug 2025 12:28:06 +0200 Subject: [PATCH] tar-util: include xattrs in generated tarballs We can already unpack them, let's pack them up to. --- src/shared/libarchive-util.c | 2 ++ src/shared/libarchive-util.h | 1 + src/shared/tar-util.c | 61 +++++++++++++++++++++++++++++++----- 3 files changed, 56 insertions(+), 8 deletions(-) diff --git a/src/shared/libarchive-util.c b/src/shared/libarchive-util.c index 961d9f6a039..65e9f5a511e 100644 --- a/src/shared/libarchive-util.c +++ b/src/shared/libarchive-util.c @@ -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), diff --git a/src/shared/libarchive-util.h b/src/shared/libarchive-util.h index 83ee19d27a5..2f40c64d85d 100644 --- a/src/shared/libarchive-util.h +++ b/src/shared/libarchive-util.h @@ -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); diff --git a/src/shared/tar-util.c b/src/shared/tar-util.c index c75d7eb6668..95b5ba820c4 100644 --- a/src/shared/tar-util.c +++ b/src/shared/tar-util.c @@ -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"); -- 2.47.3