]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
repart: Add squashfs support 24753/head
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Mon, 19 Sep 2022 18:26:15 +0000 (20:26 +0200)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Wed, 21 Sep 2022 12:16:13 +0000 (14:16 +0200)
To make this work, we have to set up everything in a temporary
directory tree that we can pass to mksquashfs as a single directory.

To make the most common scenario more efficient, we skip the temporary
setup directory if we only get a single source tree destined to root
in the squashfs filesystem.

man/repart.d.xml
src/partition/repart.c

index bdbf054fcf699ca42df12b05df180abea1ef5e55..df5338c92af12af70d768b7af9b8d409ab6bd203 100644 (file)
         <term><varname>Format=</varname></term>
 
         <listitem><para>Takes a file system name, such as <literal>ext4</literal>, <literal>btrfs</literal>,
-        <literal>xfs</literal> or <literal>vfat</literal>, or the special value <literal>swap</literal>. If
-        specified and the partition is newly created it is formatted with the specified file system (or as
-        swap device). The file system UUID and label are automatically derived from the partition UUID and
-        label. If this option is used, the size allocation algorithm is slightly altered: the partition is
-        created as least as big as required for the minimal file system of the specified type (or 4KiB if the
-        minimal size is not known).</para>
+        <literal>xfs</literal>, <literal>vfat</literal>, <literal>squashfs</literal>, or the special value
+        <literal>swap</literal>. If specified and the partition is newly created it is formatted with the
+        specified file system (or as swap device). The file system UUID and label are automatically derived
+        from the partition UUID and label. If this option is used, the size allocation algorithm is slightly
+        altered: the partition is created as least as big as required for the minimal file system of the
+        specified type (or 4KiB if the minimal size is not known).</para>
 
         <para>This option has no effect if the partition already exists.</para>
 
index db37a3ee3a6af8d54a47cf33679d91c7fb462e1c..c35183861f78fec9ea0bf01166c6f7286843119c 100644 (file)
@@ -55,6 +55,7 @@
 #include "process-util.h"
 #include "random-util.h"
 #include "resize-fs.h"
+#include "rm-rf.h"
 #include "sort-util.h"
 #include "specifier.h"
 #include "stdio-util.h"
@@ -62,6 +63,7 @@
 #include "string-util.h"
 #include "strv.h"
 #include "sync-util.h"
+#include "tmpfile-util.h"
 #include "terminal-util.h"
 #include "tpm-pcr.h"
 #include "tpm2-util.h"
@@ -3280,12 +3282,67 @@ static int do_make_directories(Partition *p, const char *root) {
         return 0;
 }
 
-static int partition_populate(Partition *p, const char *node) {
+static int partition_populate_directory(Partition *p, char **ret_root, char **ret_tmp_root) {
+        _cleanup_(rm_rf_physical_and_freep) char *root = NULL;
+        int r;
+
+        assert(ret_root);
+        assert(ret_tmp_root);
+
+        /* When generating squashfs, we need the source tree to be available when we generate the squashfs
+         * filesystem. Because we might have multiple source trees, we build a temporary source tree
+         * beforehand where we merge all our inputs. We then use this merged source tree to create the
+         * squashfs filesystem. */
+
+        if (!streq(p->format, "squashfs")) {
+                *ret_root = NULL;
+                *ret_tmp_root = NULL;
+                return 0;
+        }
+
+        /* If we only have a single directory that's meant to become the root directory of the filesystem,
+         * we can shortcut this function and just use that directory as the root directory instead. If we
+         * allocate a temporary directory, it's stored in "ret_tmp_root" to indicate it should be removed.
+         * Otherwise, we return the directory to use in "root" to indicate it should not be removed. */
+
+        if (strv_length(p->copy_files) == 2 && strv_length(p->make_directories) == 0 && streq(p->copy_files[1], "/")) {
+                _cleanup_free_ char *s = NULL;
+
+                s = strdup(p->copy_files[0]);
+                if (!s)
+                        return log_oom();
+
+                *ret_root = TAKE_PTR(s);
+                *ret_tmp_root = NULL;
+                return 0;
+        }
+
+        r = mkdtemp_malloc("/var/tmp/repart-XXXXXX", &root);
+        if (r < 0)
+                return log_error_errno(r, "Failed to create temporary directory: %m");
+
+        r = do_copy_files(p, root);
+        if (r < 0)
+                return r;
+
+        r = do_make_directories(p, root);
+        if (r < 0)
+                return r;
+
+        *ret_root = NULL;
+        *ret_tmp_root = TAKE_PTR(root);
+        return 0;
+}
+
+static int partition_populate_filesystem(Partition *p, const char *node) {
         int r;
 
         assert(p);
         assert(node);
 
+        if (streq(p->format, "squashfs"))
+                return 0;
+
         if (strv_isempty(p->copy_files) && strv_isempty(p->make_directories))
                 return 0;
 
@@ -3340,7 +3397,8 @@ static int context_mkfs(Context *context) {
         LIST_FOREACH(partitions, p, context->partitions) {
                 _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
                 _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
-                _cleanup_free_ char *encrypted = NULL;
+                _cleanup_(rm_rf_physical_and_freep) char *tmp_root = NULL;
+                _cleanup_free_ char *encrypted = NULL, *root = NULL;
                 _cleanup_close_ int encrypted_dev_fd = -1;
                 const char *fsdev;
                 sd_id128_t fs_uuid;
@@ -3387,7 +3445,16 @@ static int context_mkfs(Context *context) {
                 if (r < 0)
                         return r;
 
-                r = make_filesystem(fsdev, p->format, strempty(p->new_label), NULL, fs_uuid, arg_discard);
+                /* Ideally, we populate filesystems using our own code after creating the filesystem to
+                 * ensure consistent handling of chattrs, xattrs and other similar things. However, when
+                 * using squashfs, we can't populate after creating the filesystem because it's read-only, so
+                 * instead we create a temporary root to use as the source tree when generating the squashfs
+                 * filesystem. */
+                r = partition_populate_directory(p, &root, &tmp_root);
+                if (r < 0)
+                        return r;
+
+                r = make_filesystem(fsdev, p->format, strempty(p->new_label), root ?: tmp_root, fs_uuid, arg_discard);
                 if (r < 0) {
                         encrypted_dev_fd = safe_close(encrypted_dev_fd);
                         (void) deactivate_luks(cd, encrypted);
@@ -3401,7 +3468,8 @@ static int context_mkfs(Context *context) {
                         if (flock(encrypted_dev_fd, LOCK_UN) < 0)
                                 return log_error_errno(errno, "Failed to unlock LUKS device: %m");
 
-                r = partition_populate(p, fsdev);
+                /* Now, we can populate all the other filesystems that aren't squashfs. */
+                r = partition_populate_filesystem(p, fsdev);
                 if (r < 0) {
                         encrypted_dev_fd = safe_close(encrypted_dev_fd);
                         (void) deactivate_luks(cd, encrypted);