]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
dissect: add --make-archive option to convert DDI to tarball
authorLennart Poettering <lennart@poettering.net>
Wed, 24 Jan 2024 11:28:41 +0000 (12:28 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 25 Jan 2024 17:47:39 +0000 (18:47 +0100)
18 files changed:
.packit.yml
man/systemd-dissect.xml
meson.build
meson_options.txt
mkosi.images/base/mkosi.build.chroot
mkosi.images/base/mkosi.conf.d/10-arch.conf
mkosi.images/base/mkosi.conf.d/10-centos-fedora.conf
mkosi.images/base/mkosi.conf.d/10-debian-ubuntu.conf
mkosi.images/base/mkosi.conf.d/10-opensuse.conf
src/basic/build.c
src/dissect/dissect.c
src/shared/libarchive-util.c [new file with mode: 0644]
src/shared/libarchive-util.h [new file with mode: 0644]
src/shared/meson.build
src/test/test-dlopen-so.c
test/TEST-50-DISSECT/test.sh
test/test-functions
test/units/testsuite-50.sh

index 1775221bd1c7063a9b588e72ecb078be349a769b..8fb2cc878ade7e51803b7d97fbfd8b956ceb20b0 100644 (file)
@@ -41,6 +41,9 @@ actions:
     - 'sed -i "/^CONFIGURE_OPTS=(/a--werror" .packit_rpm/systemd.spec'
     # Ignore unpackaged standalone binaries
     - "sed -i 's/assert False,.*/pass/' .packit_rpm/split-files.py"
+    # Temporarily add libarchive-devel build dep until the change propagates to
+    # Rawhide's specfile
+    - "sed -ri '0,/^BuildRequires: .+$/s//&\\nBuildRequires: libarchive-devel/' .packit_rpm/systemd.spec"
 
 jobs:
 - job: copr_build
index 85166f23d352ce6aace565e40b2fbc8f92b0d5cf..e72c66386aa0cc47e2e63f177e82045fc207a125 100644 (file)
@@ -53,6 +53,9 @@
     <cmdsynopsis>
       <command>systemd-dissect</command> <arg choice="opt" rep="repeat">OPTIONS</arg> <arg>--copy-to</arg> <arg choice="plain"><replaceable>IMAGE</replaceable></arg> <arg choice="opt"><replaceable>SOURCE</replaceable></arg> <arg choice="plain"><replaceable>PATH</replaceable></arg>
     </cmdsynopsis>
+    <cmdsynopsis>
+      <command>systemd-dissect</command> <arg choice="opt" rep="repeat">OPTIONS</arg> <arg>--make-archive</arg> <arg choice="plain"><replaceable>IMAGE</replaceable></arg> <arg choice="opt"><replaceable>TARGET</replaceable></arg>
+    </cmdsynopsis>
     <cmdsynopsis>
       <command>systemd-dissect</command> <arg choice="opt" rep="repeat">OPTIONS</arg> <arg>--discover</arg>
     </cmdsynopsis>
         <xi:include href="version-info.xml" xpointer="v247"/></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--make-archive</option></term>
+
+        <listitem><para>Generates an archive file from the specified disk image. Expects two arguments: the
+        path to the disk image and optionally the output archive file path. If the latter is omitted the
+        archive is written to standard output. The archive file format is determined automatically from the
+        specified output archive file name, e.g. any path suffixed with <literal>.tar.xz</literal> will
+        result in an xz compressed UNIX tarball (if the path is omitted an uncompressed UNIX tarball is
+        created). See
+        <citerefentry><refentrytitle>libarchive</refentrytitle><manvolnum>3</manvolnum></citerefentry> for a
+        list of supported archive formats and compression schemes.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>--discover</option></term>
 
     <title>Examples</title>
 
     <example>
-      <title>Generate a tarball from an OS disk image</title>
+      <title>Generate a tarball from an OS disk image (<option>--with</option>)</title>
 
       <programlisting># systemd-dissect --with foo.raw tar cz . >foo.tar.gz</programlisting>
     </example>
+
+    <para>or alternatively just:</para>
+
+    <example>
+      <title>Generate a tarball from an OS disk image (<option>--make-archive</option>)</title>
+
+      <programlisting># systemd-dissect --make-archive foo.raw foo.tar.gz</programlisting>
+    </example>
   </refsect1>
 
   <refsect1>
index 02a0e9b8f82d7b9e0eda69323b239da97e0cdd6b..136b1eac214088f0f2e035531f59e59669481d7d 100644 (file)
@@ -1413,6 +1413,11 @@ elif compression == 'xz' and not libxz.found()
 endif
 conf.set('DEFAULT_COMPRESSION', 'COMPRESSION_@0@'.format(compression.to_upper()))
 
+libarchive = dependency('libarchive',
+                        version : '>= 3.0',
+                        required : get_option('libarchive'))
+conf.set10('HAVE_LIBARCHIVE', libarchive.found())
+
 libxkbcommon = dependency('xkbcommon',
                           version : '>= 0.3.0',
                           required : get_option('xkbcommon'))
index b74f949189635f0d802d892e56ffda79ca1ff1e5..9a6abd4981a3751bf4cdff05a416da170aa39e5b 100644 (file)
@@ -451,6 +451,8 @@ option('glib', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'd
        description : 'libglib support (for tests only)')
 option('dbus', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'libdbus support (for tests only)')
+option('libarchive', type : 'feature',
+       description : 'libarchive support')
 
 option('bootloader', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'sd-boot/stub and userspace tools')
index 9524fdb103a6bc4f940e1671990e5b3fadcb4d96..f164c954b7e7cb8dded0c482f0cb7304dbc57582 100755 (executable)
@@ -153,6 +153,7 @@ if [ ! -f "$BUILDDIR"/build.ninja ]; then
         -D initrd=true
         -D fexecve=true
         -D default-keymap="$DEFAULT_KEYMAP"
+        -D libarchive=enabled
     )
 
     # On debian-like systems the library directory is not /usr/lib64 but /usr/lib/<arch-triplet>/.
index 7ab0c712aec43fb5ba408aba8d2606216a0c41bb..385c73979f1b81b5dd97553e24f6d3b1da21d857 100644 (file)
@@ -8,6 +8,7 @@ Packages=
         cryptsetup
         dbus
         gnutls
+        libarchive
         libbpf
         libfido2
         libmicrohttpd
index 4dec24cc2070dadd2e32ffc38d8f7add368f72df..def5eaa2ded9a77958731d4974a14a4ca1d7bc09 100644 (file)
@@ -9,6 +9,7 @@ Packages=
         audit-libs
         cryptsetup-libs
         gnutls
+        libarchive
         libasan
         libbpf
         libfido2
@@ -39,6 +40,7 @@ BuildPackages=
         pkgconfig(glib-2.0)
         pkgconfig(gnutls)
         pkgconfig(libacl)
+        pkgconfig(libarchive)
         pkgconfig(libbpf)
         pkgconfig(libcap)
         pkgconfig(libcryptsetup)
index c529e0b1768998d6b25009584107fb402f3a3b4d..fc684a4482f72a2bfbd896c2cbb2d1b8d7dea859 100644 (file)
@@ -8,6 +8,7 @@ Distribution=|ubuntu
 Packages=
         dmsetup
         libapparmor1
+        libarchive13
         libfdisk1
         libfido2-1
         libglib2.0-0
@@ -30,6 +31,7 @@ BuildPackages=
         g++
         libacl1-dev
         libapparmor-dev
+        libarchive-dev
         libaudit-dev
         libblkid-dev
         libbpf-dev
index ec91b4901f22db8e2e8d067c3927bfd1459e2aaf..1c00b78322eacc51ffa5ffb8a37ea8555ed3bd15 100644 (file)
@@ -14,6 +14,7 @@ Packages=
         grep
         gzip
         libbpf1
+        libarchive13
         libcrypt1
         libcryptsetup12
         libdw1
@@ -53,6 +54,7 @@ BuildPackages=
         intltool
         libacl-devel
         libapparmor-devel
+        libarchive-devel
         libblkid-devel
         libbpf-devel
         libcap-devel
index 8fb32ab9b6eed7842e5d9e3d4d852729228e233e..1c52d9a935a6b6e4c9588795d5ac5b1608327924 100644 (file)
@@ -238,6 +238,12 @@ const char* const systemd_features =
         " -SYSVINIT"
 #endif
 
+#if HAVE_LIBARCHIVE
+        " +LIBARCHIVE"
+#else
+        " -LIBARCHIVE"
+#endif
+
         " default-hierarchy=" DEFAULT_HIERARCHY_NAME
         ;
 
index 4e44a237939291851fa0973bf782749641c83e89..9ef991426cadf81deed8225fa64ac26ef3279abf 100644 (file)
@@ -27,6 +27,7 @@
 #include "format-util.h"
 #include "fs-util.h"
 #include "hexdecoct.h"
+#include "libarchive-util.h"
 #include "log.h"
 #include "loop-util.h"
 #include "main-func.h"
@@ -63,6 +64,7 @@ static enum {
         ACTION_COPY_TO,
         ACTION_DISCOVER,
         ACTION_VALIDATE,
+        ACTION_MAKE_ARCHIVE,
 } arg_action = ACTION_DISSECT;
 static char *arg_image = NULL;
 static char *arg_root = NULL;
@@ -116,6 +118,7 @@ static int help(void) {
                "%1$s [OPTIONS...] --with IMAGE [COMMAND…]\n"
                "%1$s [OPTIONS...] --copy-from IMAGE PATH [TARGET]\n"
                "%1$s [OPTIONS...] --copy-to IMAGE [SOURCE] PATH\n"
+               "%1$s [OPTIONS...] --make-archive IMAGE [TARGET]\n"
                "%1$s [OPTIONS...] --discover\n"
                "%1$s [OPTIONS...] --validate IMAGE\n"
                "\n%5$sDissect a Discoverable Disk Image (DDI).%6$s\n\n"
@@ -157,6 +160,7 @@ static int help(void) {
                "     --with               Mount, run command, unmount\n"
                "  -x --copy-from          Copy files from image to host\n"
                "  -a --copy-to            Copy files from host to image\n"
+               "     --make-archive       Convert the DDI to an archive file\n"
                "     --discover           Discover DDIs in well known directories\n"
                "     --validate           Validate image and image policy\n"
                "\nSee the %2$s for details.\n",
@@ -263,6 +267,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_IMAGE_POLICY,
                 ARG_VALIDATE,
                 ARG_MTREE_HASH,
+                ARG_MAKE_ARCHIVE,
         };
 
         static const struct option options[] = {
@@ -295,6 +300,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "image-policy",  required_argument, NULL, ARG_IMAGE_POLICY  },
                 { "validate",      no_argument,       NULL, ARG_VALIDATE      },
                 { "mtree-hash",    required_argument, NULL, ARG_MTREE_HASH    },
+                { "make-archive",  no_argument,       NULL, ARG_MAKE_ARCHIVE  },
                 {}
         };
 
@@ -518,6 +524,15 @@ static int parse_argv(int argc, char *argv[]) {
                                 return r;
                         break;
 
+                case ARG_MAKE_ARCHIVE:
+
+                        r = dlopen_libarchive();
+                        if (r < 0)
+                                return log_error_errno(r, "Archive support not available (compiled without libarchive, or libarchive not installed?).");
+
+                        arg_action = ACTION_MAKE_ARCHIVE;
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -600,6 +615,19 @@ static int parse_argv(int argc, char *argv[]) {
                 arg_flags |= DISSECT_IMAGE_READ_ONLY | DISSECT_IMAGE_REQUIRE_ROOT;
                 break;
 
+        case ACTION_MAKE_ARCHIVE:
+                if (argc < optind + 1 || argc > optind + 2)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                               "Expected an image file, and an optional target path as only arguments.");
+
+                r = parse_image_path_argument(argv[optind], &arg_root, &arg_image);
+                if (r < 0)
+                        return r;
+
+                arg_target = argc > optind + 1 ? empty_or_dash_to_null(argv[optind + 1]) : NULL;
+                arg_flags |= DISSECT_IMAGE_READ_ONLY | DISSECT_IMAGE_REQUIRE_ROOT;
+                break;
+
         case ACTION_COPY_FROM:
                 if (argc < optind + 2 || argc > optind + 3)
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
@@ -1274,13 +1302,116 @@ static int mtree_print_item(
         return RECURSE_DIR_CONTINUE;
 }
 
-static int action_list_or_mtree_or_copy(DissectedImage *m, LoopDevice *d) {
+#if HAVE_LIBARCHIVE
+static int archive_item(
+                RecurseDirEvent event,
+                const char *path,
+                int dir_fd,
+                int inode_fd,
+                const struct dirent *de,
+                const struct statx *sx,
+                void *userdata) {
+
+        struct archive *a = ASSERT_PTR(userdata);
+        int r;
+
+        assert(path);
+
+        if (!IN_SET(event, RECURSE_DIR_ENTER, RECURSE_DIR_ENTRY))
+                return RECURSE_DIR_CONTINUE;
+
+        assert(inode_fd >= 0);
+        assert(sx);
+
+        log_debug("Archiving %s\n", path);
+
+        _cleanup_(sym_archive_entry_freep) struct archive_entry *entry = NULL;
+        entry = sym_archive_entry_new();
+        if (!entry)
+                return log_oom();
+
+        assert(FLAGS_SET(sx->stx_mask, STATX_TYPE|STATX_MODE));
+        sym_archive_entry_set_pathname(entry, path);
+        sym_archive_entry_set_filetype(entry, sx->stx_mode);
+
+        if (!S_ISLNK(sx->stx_mode))
+                sym_archive_entry_set_perm(entry, sx->stx_mode);
+
+        if (FLAGS_SET(sx->stx_mask, STATX_UID))
+                sym_archive_entry_set_uid(entry, sx->stx_uid);
+        if (FLAGS_SET(sx->stx_mask, STATX_GID))
+                sym_archive_entry_set_gid(entry, sx->stx_gid);
+
+        if (S_ISREG(sx->stx_mode)) {
+                if (!FLAGS_SET(sx->stx_mask, STATX_SIZE))
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO), "Unable to determine file size of '%s'.", path);
+
+                sym_archive_entry_set_size(entry, sx->stx_size);
+        }
+
+        if (S_ISCHR(sx->stx_mode) || S_ISBLK(sx->stx_mode)) {
+                sym_archive_entry_set_rdevmajor(entry, sx->stx_rdev_major);
+                sym_archive_entry_set_rdevminor(entry, sx->stx_rdev_minor);
+        }
+
+        /* We care about a modicum of reproducibility here, hence we don't save atime/btime here */
+        if (FLAGS_SET(sx->stx_mask, STATX_MTIME))
+                sym_archive_entry_set_mtime(entry, sx->stx_mtime.tv_sec, sx->stx_mtime.tv_nsec);
+        if (FLAGS_SET(sx->stx_mask, STATX_CTIME))
+                sym_archive_entry_set_ctime(entry, sx->stx_ctime.tv_sec, sx->stx_ctime.tv_nsec);
+
+        if (S_ISLNK(sx->stx_mode)) {
+                _cleanup_free_ char *s = NULL;
+
+                assert(dir_fd >= 0);
+                assert(de);
+
+                r = readlinkat_malloc(dir_fd, de->d_name, &s);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to read symlink target of '%s': %m", path);
+
+                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));
+
+        if (S_ISREG(sx->stx_mode)) {
+                _cleanup_close_ int data_fd = -EBADF;
+
+                /* Convert the O_PATH fd in a proper fd */
+                data_fd = fd_reopen(inode_fd, O_RDONLY|O_CLOEXEC);
+                if (data_fd < 0)
+                        return log_error_errno(data_fd, "Failed to open '%s': %m", path);
+
+                for (;;) {
+                        char buffer[64*1024];
+                        ssize_t l;
+
+                        l = read(data_fd, buffer, sizeof(buffer));
+                        if (l < 0)
+                                return log_error_errno(errno, "Failed to read '%s': %m",  path);
+                        if (l == 0)
+                                break;
+
+                        la_ssize_t k;
+                        k = sym_archive_write_data(a, buffer, l);
+                        if (k < 0)
+                                return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to write archive data: %s", sym_archive_error_string(a));
+                }
+        }
+
+        return RECURSE_DIR_CONTINUE;
+}
+#endif
+
+static int action_list_or_mtree_or_copy_or_make_archive(DissectedImage *m, LoopDevice *d) {
         _cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL;
         _cleanup_free_ char *t = NULL;
         const char *root;
         int r;
 
-        assert(IN_SET(arg_action, ACTION_LIST, ACTION_MTREE, ACTION_COPY_FROM, ACTION_COPY_TO));
+        assert(IN_SET(arg_action, ACTION_LIST, ACTION_MTREE, ACTION_COPY_FROM, ACTION_COPY_TO, ACTION_MAKE_ARCHIVE));
 
         if (arg_image) {
                 assert(m);
@@ -1466,6 +1597,68 @@ static int action_list_or_mtree_or_copy(DissectedImage *m, LoopDevice *d) {
                 return 0;
         }
 
+        case ACTION_MAKE_ARCHIVE: {
+#if HAVE_LIBARCHIVE
+                _cleanup_(unlink_and_freep) char *tar = NULL;
+                _cleanup_close_ int dfd = -EBADF;
+                _cleanup_fclose_ FILE *f = NULL;
+
+                dfd = open(root, O_DIRECTORY|O_CLOEXEC|O_RDONLY);
+                if (dfd < 0)
+                        return log_error_errno(errno, "Failed to open mount directory: %m");
+
+                _cleanup_(sym_archive_write_freep) struct archive *a = sym_archive_write_new();
+                if (!a)
+                        return log_oom();
+
+                if (arg_target)
+                        r = sym_archive_write_set_format_filter_by_ext(a, arg_target);
+                else
+                        r = sym_archive_write_set_format_gnutar(a);
+                if (r != ARCHIVE_OK)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to set libarchive output format: %s", sym_archive_error_string(a));
+
+                if (arg_target) {
+                        r = fopen_tmpfile_linkable(arg_target, O_WRONLY|O_CLOEXEC, &tar, &f);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to create target file '%s': %m", arg_target);
+
+                        r = sym_archive_write_open_FILE(a, f);
+                } else {
+                        if (isatty(STDOUT_FILENO))
+                                return log_error_errno(SYNTHETIC_ERRNO(EBADF), "Refusing to write archive to TTY.");
+
+                        r = sym_archive_write_open_fd(a, STDOUT_FILENO);
+                }
+                if (r != ARCHIVE_OK)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to set libarchive output file: %s", sym_archive_error_string(a));
+
+                r = recurse_dir(dfd,
+                                ".",
+                                STATX_TYPE|STATX_MODE|STATX_UID|STATX_GID|STATX_SIZE|STATX_ATIME|STATX_CTIME,
+                                UINT_MAX,
+                                RECURSE_DIR_SORT|RECURSE_DIR_INODE_FD|RECURSE_DIR_TOPLEVEL,
+                                archive_item,
+                                a);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to make archive: %m");
+
+                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));
+
+                if (arg_target) {
+                        r = flink_tmpfile(f, tar, arg_target, LINK_TMPFILE_REPLACE);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to move archive file into place: %m");
+                }
+
+                return 0;
+#else
+                assert_not_reached();
+#endif
+        }
+
         default:
                 assert_not_reached();
         }
@@ -1914,7 +2107,8 @@ static int run(int argc, char *argv[]) {
         case ACTION_MTREE:
         case ACTION_COPY_FROM:
         case ACTION_COPY_TO:
-                return action_list_or_mtree_or_copy(m, d);
+        case ACTION_MAKE_ARCHIVE:
+                return action_list_or_mtree_or_copy_or_make_archive(m, d);
 
         case ACTION_WITH:
                 return action_with(m, d);
diff --git a/src/shared/libarchive-util.c b/src/shared/libarchive-util.c
new file mode 100644 (file)
index 0000000..e6d6581
--- /dev/null
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "libarchive-util.h"
+
+#if HAVE_LIBARCHIVE
+static void *libarchive_dl = NULL;
+
+DLSYM_FUNCTION(archive_entry_free);
+DLSYM_FUNCTION(archive_entry_new);
+DLSYM_FUNCTION(archive_entry_set_ctime);
+DLSYM_FUNCTION(archive_entry_set_filetype);
+DLSYM_FUNCTION(archive_entry_set_gid);
+DLSYM_FUNCTION(archive_entry_set_mtime);
+DLSYM_FUNCTION(archive_entry_set_pathname);
+DLSYM_FUNCTION(archive_entry_set_perm);
+DLSYM_FUNCTION(archive_entry_set_rdevmajor);
+DLSYM_FUNCTION(archive_entry_set_rdevminor);
+DLSYM_FUNCTION(archive_entry_set_symlink);
+DLSYM_FUNCTION(archive_entry_set_size);
+DLSYM_FUNCTION(archive_entry_set_uid);
+DLSYM_FUNCTION(archive_error_string);
+DLSYM_FUNCTION(archive_write_close);
+DLSYM_FUNCTION(archive_write_data);
+DLSYM_FUNCTION(archive_write_free);
+DLSYM_FUNCTION(archive_write_header);
+DLSYM_FUNCTION(archive_write_new);
+DLSYM_FUNCTION(archive_write_open_FILE);
+DLSYM_FUNCTION(archive_write_open_fd);
+DLSYM_FUNCTION(archive_write_set_format_filter_by_ext);
+DLSYM_FUNCTION(archive_write_set_format_gnutar);
+
+int dlopen_libarchive(void) {
+        return dlopen_many_sym_or_warn(
+                        &libarchive_dl,
+                        "libarchive.so.13",
+                        LOG_DEBUG,
+                        DLSYM_ARG(archive_entry_free),
+                        DLSYM_ARG(archive_entry_new),
+                        DLSYM_ARG(archive_entry_set_ctime),
+                        DLSYM_ARG(archive_entry_set_filetype),
+                        DLSYM_ARG(archive_entry_set_gid),
+                        DLSYM_ARG(archive_entry_set_mtime),
+                        DLSYM_ARG(archive_entry_set_pathname),
+                        DLSYM_ARG(archive_entry_set_perm),
+                        DLSYM_ARG(archive_entry_set_rdevmajor),
+                        DLSYM_ARG(archive_entry_set_rdevminor),
+                        DLSYM_ARG(archive_entry_set_size),
+                        DLSYM_ARG(archive_entry_set_symlink),
+                        DLSYM_ARG(archive_entry_set_uid),
+                        DLSYM_ARG(archive_error_string),
+                        DLSYM_ARG(archive_write_close),
+                        DLSYM_ARG(archive_write_data),
+                        DLSYM_ARG(archive_write_free),
+                        DLSYM_ARG(archive_write_header),
+                        DLSYM_ARG(archive_write_new),
+                        DLSYM_ARG(archive_write_open_FILE),
+                        DLSYM_ARG(archive_write_open_fd),
+                        DLSYM_ARG(archive_write_set_format_filter_by_ext),
+                        DLSYM_ARG(archive_write_set_format_gnutar));
+}
+
+#endif
diff --git a/src/shared/libarchive-util.h b/src/shared/libarchive-util.h
new file mode 100644 (file)
index 0000000..8003da9
--- /dev/null
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "dlfcn-util.h"
+
+#if HAVE_LIBARCHIVE
+#include <archive.h>
+#include <archive_entry.h>
+
+DLSYM_PROTOTYPE(archive_entry_free);
+DLSYM_PROTOTYPE(archive_entry_new);
+DLSYM_PROTOTYPE(archive_entry_set_ctime);
+DLSYM_PROTOTYPE(archive_entry_set_filetype);
+DLSYM_PROTOTYPE(archive_entry_set_gid);
+DLSYM_PROTOTYPE(archive_entry_set_mtime);
+DLSYM_PROTOTYPE(archive_entry_set_pathname);
+DLSYM_PROTOTYPE(archive_entry_set_perm);
+DLSYM_PROTOTYPE(archive_entry_set_rdevmajor);
+DLSYM_PROTOTYPE(archive_entry_set_rdevminor);
+DLSYM_PROTOTYPE(archive_entry_set_symlink);
+DLSYM_PROTOTYPE(archive_entry_set_size);
+DLSYM_PROTOTYPE(archive_entry_set_uid);
+DLSYM_PROTOTYPE(archive_error_string);
+DLSYM_PROTOTYPE(archive_write_close);
+DLSYM_PROTOTYPE(archive_write_data);
+DLSYM_PROTOTYPE(archive_write_free);
+DLSYM_PROTOTYPE(archive_write_header);
+DLSYM_PROTOTYPE(archive_write_new);
+DLSYM_PROTOTYPE(archive_write_open_FILE);
+DLSYM_PROTOTYPE(archive_write_open_fd);
+DLSYM_PROTOTYPE(archive_write_set_format_filter_by_ext);
+DLSYM_PROTOTYPE(archive_write_set_format_gnutar);
+
+int dlopen_libarchive(void);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct archive_entry*, sym_archive_entry_free, NULL);
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct archive*, sym_archive_write_free, NULL);
+
+#else
+
+static inline int dlopen_libarchive(void) {
+        return -EOPNOTSUPP;
+}
+
+#endif
index 533c08d20a8ebf83e5d702c31d77842d37ebc2cc..2a39bb04df46993cceb896fc2c678216c8dc7f69 100644 (file)
@@ -103,6 +103,7 @@ shared_sources = files(
         'keyring-util.c',
         'killall.c',
         'label-util.c',
+        'libarchive-util.c',
         'libcrypt-util.c',
         'libfido2-util.c',
         'libmount-util.c',
index e98b8dae1e0fe7765088d2ceca03be01fb3b9b42..d2dbe7298183ee2cb522f0864bdfa927a270b2c6 100644 (file)
@@ -7,6 +7,7 @@
 #include "cryptsetup-util.h"
 #include "elf-util.h"
 #include "idn-util.h"
+#include "libarchive-util.h"
 #include "libfido2-util.h"
 #include "macro.h"
 #include "main-func.h"
@@ -70,6 +71,10 @@ static int run(int argc, char **argv) {
         assert_se(dlopen_p11kit() >= 0);
 #endif
 
+#if HAVE_LIBARCHIVE
+        assert_se(dlopen_libarchive() >= 0);
+#endif
+
         return 0;
 }
 
index f1abce8887328284197bac2e9f8aaf532468e6c7..613bb086ce3b630fcf189657b9f68c1d42acc93b 100755 (executable)
@@ -21,6 +21,7 @@ test_append_files() {
     generate_module_dependencies
     inst_binary wc
     inst_binary sha256sum
+    inst_binary tar
     if command -v openssl >/dev/null 2>&1; then
         inst_binary openssl
     fi
index 92bb7d532baab1cb3ded39ee40d8c2280649be62..80fdcd26b9485d0b1b00128fd4dabe855c427b39 100644 (file)
@@ -1551,7 +1551,7 @@ install_missing_libraries() {
     local lib path
     # A number of dependencies is now optional via dlopen, so the install
     # script will not pick them up, since it looks at linkage.
-    for lib in libcryptsetup libidn libidn2 pwquality libqrencode tss2-esys tss2-rc tss2-mu tss2-tcti-device libfido2 libbpf libelf libdw xkbcommon p11-kit-1; do
+    for lib in libcryptsetup libidn libidn2 pwquality libqrencode tss2-esys tss2-rc tss2-mu tss2-tcti-device libfido2 libbpf libelf libdw xkbcommon p11-kit-1 libarchive; do
         ddebug "Searching for $lib via pkg-config"
         if pkg-config --exists "$lib"; then
                 path="$(pkg-config --variable=libdir "$lib")"
index 0e33ec9a26f210fc00890f4c4b5b85f4bcec35d6..af379a4d8ba1a37afe6b6a736448c97683c30c0e 100755 (executable)
@@ -54,6 +54,17 @@ read -r SHA256SUM2 _ < <(systemd-dissect --read-only --with "${image}.raw" sha25
 test "$SHA256SUM2" != ""
 test "$SHA256SUM1" = "$SHA256SUM2"
 
+if systemctl --version | grep -qF -- "+LIBARCHIVE" ; then
+    # Make sure tarballs are reproducible
+    read -r SHA256SUM1 _ < <(systemd-dissect --make-archive "${image}.raw" | sha256sum)
+    test "$SHA256SUM1" != ""
+    read -r SHA256SUM2 _ < <(systemd-dissect --make-archive "${image}.raw" | sha256sum)
+    test "$SHA256SUM2" != ""
+    test "$SHA256SUM1" = "$SHA256SUM2"
+    # Also check that a file we expect to be there is there
+    systemd-dissect --make-archive "${image}.raw" | tar t | grep etc/os-release
+fi
+
 mv "${image}.verity" "${image}.fooverity"
 mv "${image}.roothash" "${image}.foohash"
 systemd-dissect --json=short "${image}.raw" --root-hash="${roothash}" --verity-data="${image}.fooverity" | grep -q -F '{"rw":"ro","designator":"root","partition_uuid":null,"partition_label":null,"fstype":"squashfs","architecture":null,"verity":"external"'