tar-util: include xattrs in generated tarballs

We can already unpack them, let's pack them up to.
This commit is contained in:
Lennart Poettering
2025-08-21 12:28:06 +02:00
parent f89c914968
commit d4d94fceba
3 changed files with 56 additions and 8 deletions

View 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),

View 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);

View 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");