From 334c71eed898152672d408ee8308be80338ae662 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 13 Mar 2026 09:52:23 +0100 Subject: [PATCH 1/4] shared/tar-util: wrap some long lines, normalize indentation --- src/shared/tar-util.c | 73 ++++++++++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 29 deletions(-) diff --git a/src/shared/tar-util.c b/src/shared/tar-util.c index 17c03e910b..d2a991ba13 100644 --- a/src/shared/tar-util.c +++ b/src/shared/tar-util.c @@ -472,9 +472,8 @@ static int archive_unpack_special_inode( return log_error_errno(errno, "Failed to fstat() '%s': %m", path); if (((st.st_mode ^ filetype) & S_IFMT) != 0) - return log_error_errno( - SYNTHETIC_ERRNO(ENODEV), - "Special node '%s' we just created is of a wrong type: %m", path); + return log_error_errno(SYNTHETIC_ERRNO(ENODEV), + "Special node '%s' we just created is of a wrong type: %m", path); return TAKE_FD(fd); } @@ -552,7 +551,8 @@ static int archive_entry_read_acl( if (r == ARCHIVE_EOF) break; if (r != ARCHIVE_OK) - return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Unexpected error while iterating through ACLs."); + return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), + "Unexpected error while iterating through ACLs."); assert(rtype == type); @@ -781,7 +781,8 @@ int tar_x(int input_fd, int tree_fd, TarFlags flags) { ar = sym_archive_read_open_fd(a, input_fd, 64 * 1024); if (ar != ARCHIVE_OK) - return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to initialize archive context: %s", sym_archive_error_string(a)); + return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), + "Failed to initialize archive context: %s", sym_archive_error_string(a)); OpenInode *open_inodes = NULL; @@ -811,14 +812,16 @@ int tar_x(int input_fd, int tree_fd, TarFlags flags) { if (ar == ARCHIVE_EOF) break; if (!IN_SET(ar, ARCHIVE_OK, ARCHIVE_WARN)) - return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Failed to parse archive: %s", sym_archive_error_string(a)); + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Failed to parse archive: %s", + sym_archive_error_string(a)); const char *p = NULL; r = archive_entry_pathname_safe(entry, &p); if (r < 0) return log_error_errno(r, "Invalid path name in entry, refusing."); if (ar == ARCHIVE_WARN) - log_warning("Non-critical error found while parsing '%s' from the archive, ignoring: %s", p ?: ".", sym_archive_error_string(a)); + log_warning("Non-critical error found while parsing '%s' from the archive, ignoring: %s", + p ?: ".", sym_archive_error_string(a)); if (!p) { /* This is the root inode */ @@ -838,7 +841,8 @@ int tar_x(int input_fd, int tree_fd, TarFlags flags) { if (r < 0) return r; if (open_inodes[0].filetype != S_IFDIR) - return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Archives root inode is not a directory, refusing."); + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), + "Archives root inode is not a directory, refusing."); continue; } @@ -930,7 +934,9 @@ int tar_x(int input_fd, int tree_fd, TarFlags flags) { "Invalid hardlink path name '%s' in entry, refusing.", target); _cleanup_close_ int target_fd = -EBADF; - r = chaseat(tree_fd, target, CHASE_PROHIBIT_SYMLINKS|CHASE_AT_RESOLVE_IN_ROOT|CHASE_NOFOLLOW, /* ret_path= */ NULL, &target_fd); + r = chaseat(tree_fd, target, + CHASE_PROHIBIT_SYMLINKS|CHASE_AT_RESOLVE_IN_ROOT|CHASE_NOFOLLOW, + /* ret_path= */ NULL, &target_fd); if (r < 0) return log_error_errno( r, @@ -942,9 +948,9 @@ int tar_x(int input_fd, int tree_fd, TarFlags flags) { /* Refuse hardlinking directories early. */ if (!inode_type_can_hardlink(verify_st.st_mode)) - return log_error_errno( - SYNTHETIC_ERRNO(EBADF), - "Refusing to hardlink inode '%s' of type '%s': %m", target, inode_type_to_string(verify_st.st_mode)); + return log_error_errno(SYNTHETIC_ERRNO(EBADF), + "Refusing to hardlink inode '%s' of type '%s': %m", + target, inode_type_to_string(verify_st.st_mode)); if (linkat(target_fd, "", parent_fd, e, AT_EMPTY_PATH) < 0) { if (errno != ENOENT) @@ -959,16 +965,16 @@ int tar_x(int input_fd, int tree_fd, TarFlags flags) { _cleanup_close_ int target_parent_fd = -EBADF; _cleanup_free_ char *target_filename = NULL; - r = chaseat(tree_fd, target, CHASE_PROHIBIT_SYMLINKS|CHASE_AT_RESOLVE_IN_ROOT|CHASE_PARENT|CHASE_EXTRACT_FILENAME|CHASE_NOFOLLOW, &target_filename, &target_parent_fd); + r = chaseat(tree_fd, target, + CHASE_PROHIBIT_SYMLINKS|CHASE_AT_RESOLVE_IN_ROOT|CHASE_PARENT|CHASE_EXTRACT_FILENAME|CHASE_NOFOLLOW, + &target_filename, &target_parent_fd); if (r < 0) - return log_error_errno( - r, - "Failed to find inode '%s' which shall be hardlinked as '%s': %m", target, j); + return log_error_errno(r, "Failed to find inode '%s' which shall be hardlinked as '%s': %m", + target, j); if (linkat(target_parent_fd, target_filename, parent_fd, e, /* flags= */ 0) < 0) - return log_error_errno( - errno, - "Failed to hardlink inode '%s' as '%s': %m", target, j); + return log_error_errno(errno, "Failed to hardlink inode '%s' as '%s': %m", + target, j); } continue; @@ -1006,7 +1012,8 @@ int tar_x(int input_fd, int tree_fd, TarFlags flags) { const char *w = startswith(e, ".wh."); if (w) { if (!filename_is_valid(w)) - return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Invalid whiteout file entry '%s', refusing.", e); + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), + "Invalid whiteout file entry '%s', refusing.", e); r = archive_unpack_whiteout(a, entry, parent_fd, empty_to_root(parent_path), w, j); if (r < 0) @@ -1044,9 +1051,9 @@ int tar_x(int input_fd, int tree_fd, TarFlags flags) { break; default: - return log_error_errno( - SYNTHETIC_ERRNO(ENOTRECOVERABLE), - "Unexpected file type %i of '%s', refusing.", (int) filetype, j); + return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), + "Unexpected file type %i of '%s', refusing.", + (int) filetype, j); } } else { /* This is some intermediary node in the path that we haven't opened yet. Create it with default attributes */ @@ -1397,7 +1404,8 @@ static int archive_item( sym_archive_entry_set_hardlink(entry, hardlink); 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)); + return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), + "Failed to write archive entry header: %s", sym_archive_error_string(d->archive)); return RECURSE_DIR_CONTINUE; } @@ -1525,7 +1533,9 @@ static int archive_item( } 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)); + 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)) { assert(data_fd >= 0); @@ -1543,7 +1553,9 @@ static int archive_item( la_ssize_t k; 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(d->archive)); + return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), + "Failed to write archive data: %s", + sym_archive_error_string(d->archive)); } } @@ -1574,11 +1586,13 @@ int tar_c(int tree_fd, int output_fd, const char *filename, TarFlags flags) { else r = sym_archive_write_set_format_pax(a); if (r != ARCHIVE_OK) - return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to set libarchive output format: %s", sym_archive_error_string(a)); + return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), + "Failed to set libarchive output format: %s", sym_archive_error_string(a)); r = sym_archive_write_open_fd(a, output_fd); if (r != ARCHIVE_OK) - return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to set libarchive output file: %s", sym_archive_error_string(a)); + return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), + "Failed to set libarchive output file: %s", sym_archive_error_string(a)); _cleanup_(make_archive_data_done) struct make_archive_data data = { .archive = a, @@ -1599,7 +1613,8 @@ int tar_c(int tree_fd, int output_fd, const char *filename, TarFlags flags) { r = sym_archive_write_close(a); if (r != ARCHIVE_OK) - return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Unable to finish writing archive: %s", sym_archive_error_string(a)); + return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), + "Unable to finish writing archive: %s", sym_archive_error_string(a)); return 0; } From aa8cf865b98a23ee65b32c56b1f1d338ac9cf0a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 13 Mar 2026 10:11:22 +0100 Subject: [PATCH 2/4] test-tar-extract: fix error value in messages --- src/import/test-tar-extract.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/import/test-tar-extract.c b/src/import/test-tar-extract.c index f7fa06f044..0d44196c38 100644 --- a/src/import/test-tar-extract.c +++ b/src/import/test-tar-extract.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include "errno-util.h" #include "fd-util.h" #include "libarchive-util.h" #include "main-func.h" @@ -19,11 +20,11 @@ static int run(int argc, char **argv) { if (r < 0) return r; - _cleanup_close_ int input_fd = open(argv[1], O_RDONLY | O_CLOEXEC); + _cleanup_close_ int input_fd = RET_NERRNO(open(argv[1], O_RDONLY | O_CLOEXEC)); if (input_fd < 0) return log_error_errno(input_fd, "Cannot open %s: %m", argv[1]); - _cleanup_close_ int output_fd = open(argv[2], O_DIRECTORY | O_CLOEXEC); + _cleanup_close_ int output_fd = RET_NERRNO(open(argv[2], O_DIRECTORY | O_CLOEXEC)); if (output_fd < 0) return log_error_errno(output_fd, "Cannot open %s: %m", argv[2]); From 4f0989d42d8f4f81c41284582937e38b066d5e59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 13 Mar 2026 10:42:46 +0100 Subject: [PATCH 3/4] test-tar-extract: rename and add support for creating archives This makes it much easier to test importd code without the surrounding machinery. --- src/import/meson.build | 2 +- src/import/test-tar-extract.c | 38 ----------------------- src/import/test-tar.c | 58 +++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 39 deletions(-) delete mode 100644 src/import/test-tar-extract.c create mode 100644 src/import/test-tar.c diff --git a/src/import/meson.build b/src/import/meson.build index c0589ddd4e..8349202a32 100644 --- a/src/import/meson.build +++ b/src/import/meson.build @@ -88,7 +88,7 @@ executables += [ 'sources' : files('import-generator.c'), }, test_template + { - 'sources' : files('test-tar-extract.c'), + 'sources' : files('test-tar.c'), 'type' : 'manual', }, test_template + { diff --git a/src/import/test-tar-extract.c b/src/import/test-tar-extract.c deleted file mode 100644 index 0d44196c38..0000000000 --- a/src/import/test-tar-extract.c +++ /dev/null @@ -1,38 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "errno-util.h" -#include "fd-util.h" -#include "libarchive-util.h" -#include "main-func.h" -#include "tar-util.h" -#include "tests.h" - -static int run(int argc, char **argv) { - int r; - - test_setup_logging(LOG_DEBUG); - - if (argc != 3) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Need two arguments exactly: "); - - r = dlopen_libarchive(); - if (r < 0) - return r; - - _cleanup_close_ int input_fd = RET_NERRNO(open(argv[1], O_RDONLY | O_CLOEXEC)); - if (input_fd < 0) - return log_error_errno(input_fd, "Cannot open %s: %m", argv[1]); - - _cleanup_close_ int output_fd = RET_NERRNO(open(argv[2], O_DIRECTORY | O_CLOEXEC)); - if (output_fd < 0) - return log_error_errno(output_fd, "Cannot open %s: %m", argv[2]); - - r = tar_x(input_fd, output_fd, /* flags= */ TAR_SELINUX); - if (r < 0) - return log_error_errno(r, "tar_x failed: %m"); - - return 0; -} - -DEFINE_MAIN_FUNCTION(run); diff --git a/src/import/test-tar.c b/src/import/test-tar.c new file mode 100644 index 0000000000..c0e55c3597 --- /dev/null +++ b/src/import/test-tar.c @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include + +#include "errno-util.h" +#include "fd-util.h" +#include "libarchive-util.h" +#include "main-func.h" +#include "tar-util.h" +#include "tests.h" + +static int run(int argc, char **argv) { + int r; + + test_setup_logging(LOG_DEBUG); + + if (argc != 4) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Need three arguments exactly: -c | -x "); + + bool create; + if (streq(argv[1], "-c")) + create = true; + else if (streq(argv[1], "-x")) + create = false; + else + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown operation '%s'.", argv[1]); + + r = dlopen_libarchive(); + if (r < 0) + return r; + + int flags = create ? O_CREAT | O_WRONLY | O_TRUNC : O_RDONLY; + _cleanup_close_ int fd1 = RET_NERRNO(open(argv[2], flags | O_CLOEXEC, 0666)); + if (fd1 < 0) + return log_error_errno(fd1, "Cannot open %s: %m", argv[2]); + + if (!create) { + r = RET_NERRNO(mkdir(argv[3], 0777)); + if (r < 0 && r != -EEXIST) + return log_error_errno(r, "Failed to mkdir %s: %m", argv[3]); + } + + _cleanup_close_ int fd2 = RET_NERRNO(open(argv[3], O_DIRECTORY | O_CLOEXEC)); + if (fd2 < 0) + return log_error_errno(fd2, "Cannot open %s: %m", argv[3]); + + if (create) + r = tar_c(fd2, fd1, argv[2], /* flags= */ TAR_SELINUX); + else + r = tar_x(fd1, fd2, /* flags= */ TAR_SELINUX); + if (r < 0) + return log_error_errno(r, "tar %s failed: %m", argv[1]); + + return 0; +} + +DEFINE_MAIN_FUNCTION(run); From 4b4e0947530f3d7f80c44111c7212e16ea188d2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 13 Mar 2026 11:08:07 +0100 Subject: [PATCH 4/4] import: skip sockets and fifos when creating archives Fixes #40239. $ SYSTEMD_LOG_LEVEL=debug SYSTEMD_LOG_LOCATION=1 build/test-tar -c /var/tmp/tar1.tar /var/tmp/with-fifo/ src/basic/dlfcn-util.c:66: Loaded shared library 'libarchive.so.13' via dlopen(). src/shared/tar-util.c:1422: Archiving '.'... src/basic/dlfcn-util.c:66: Loaded shared library 'libacl.so.1' via dlopen(). src/shared/tar-util.c:1152: Skipping './fifo' (fifo). src/shared/tar-util.c:1152: Skipping './unix' (sock). --- src/shared/tar-util.c | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/shared/tar-util.c b/src/shared/tar-util.c index d2a991ba13..33395e96bc 100644 --- a/src/shared/tar-util.c +++ b/src/shared/tar-util.c @@ -1130,6 +1130,36 @@ struct make_archive_data { int have_unique_mount_id; }; +static int filter_item( + int inode_fd, + const struct statx *sx, + const char *path) { + mode_t m; + int r; + + assert(inode_fd >= 0); + assert(sx); + assert(path); + + if (FLAGS_SET(sx->stx_mask, STATX_TYPE)) + m = sx->stx_mode; + else { + struct stat st; + r = RET_NERRNO(fstat(inode_fd, &st)); + if (r < 0) + return log_error_errno(r, "Failed to stat '%s': %m", path); + m = st.st_mode; + } + + /* Filter out sockets, fifos, and weird misc fds such as eventfds() that have no inode type. */ + if (IN_SET(m & S_IFMT, S_IFSOCK, S_IFIFO, 0)) { + log_debug("Skipping '%s' (%s).", path, inode_type_to_string(m) ?: "unknown"); + return false; + } + + return true; +} + static int hardlink_lookup( struct make_archive_data *d, int inode_fd, @@ -1387,7 +1417,13 @@ static int archive_item( assert(inode_fd >= 0); assert(sx); - log_debug("Archiving %s\n", path); + r = filter_item(inode_fd, sx, path); + if (r < 0) + return r; + if (r == 0) + return RECURSE_DIR_CONTINUE; + + log_debug("Archiving '%s'...", path); _cleanup_(archive_entry_freep) struct archive_entry *entry = NULL; entry = sym_archive_entry_new();