From df4bed3f923bbe5be8e84131aa035dbab2ddf49c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sep 21 2022 17:49:10 +0000 Subject: Merge pull request #24753 from DaanDeMeyer/repart-squashfs repart: Add squashfs support --- diff --git a/man/repart.d.xml b/man/repart.d.xml index bdbf054..df5338c 100644 --- a/man/repart.d.xml +++ b/man/repart.d.xml @@ -484,12 +484,12 @@ Format= Takes a file system name, such as ext4, btrfs, - xfs or vfat, or the special value swap. 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). + xfs, vfat, squashfs, or the special value + swap. 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). This option has no effect if the partition already exists. diff --git a/src/home/homework-luks.c b/src/home/homework-luks.c index f2dcabd..c57621a 100644 --- a/src/home/homework-luks.c +++ b/src/home/homework-luks.c @@ -2333,7 +2333,7 @@ int home_create_luks( log_info("Setting up LUKS device %s completed.", setup->dm_node); - r = make_filesystem(setup->dm_node, fstype, user_record_user_name_and_realm(h), fs_uuid, user_record_luks_discard(h)); + r = make_filesystem(setup->dm_node, fstype, user_record_user_name_and_realm(h), NULL, fs_uuid, user_record_luks_discard(h)); if (r < 0) return r; diff --git a/src/partition/makefs.c b/src/partition/makefs.c index b6979b7..e8aa813 100644 --- a/src/partition/makefs.c +++ b/src/partition/makefs.c @@ -65,7 +65,7 @@ static int run(int argc, char *argv[]) { if (r < 0) return log_error_errno(r, "Failed to generate UUID for file system: %m"); - return make_filesystem(device, fstype, basename(device), uuid, true); + return make_filesystem(device, fstype, basename(device), NULL, uuid, true); } DEFINE_MAIN_FUNCTION(run); diff --git a/src/partition/repart.c b/src/partition/repart.c index 35b2c64..c351838 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -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" @@ -3168,11 +3170,11 @@ static int context_copy_blocks(Context *context) { return 0; } -static int do_copy_files(Partition *p, const char *fs) { +static int do_copy_files(Partition *p, const char *root) { int r; assert(p); - assert(fs); + assert(root); STRV_FOREACH_PAIR(source, target, p->copy_files) { _cleanup_close_ int sfd = -1, pfd = -1, tfd = -1; @@ -3187,7 +3189,7 @@ static int do_copy_files(Partition *p, const char *fs) { return log_error_errno(r, "Failed to check type of source file '%s': %m", *source); /* We are looking at a directory */ - tfd = chase_symlinks_and_open(*target, fs, CHASE_PREFIX_ROOT|CHASE_WARN, O_RDONLY|O_DIRECTORY|O_CLOEXEC, NULL); + tfd = chase_symlinks_and_open(*target, root, CHASE_PREFIX_ROOT|CHASE_WARN, O_RDONLY|O_DIRECTORY|O_CLOEXEC, NULL); if (tfd < 0) { _cleanup_free_ char *dn = NULL, *fn = NULL; @@ -3202,11 +3204,11 @@ static int do_copy_files(Partition *p, const char *fs) { if (r < 0) return log_error_errno(r, "Failed to extract directory from '%s': %m", *target); - r = mkdir_p_root(fs, dn, UID_INVALID, GID_INVALID, 0755); + r = mkdir_p_root(root, dn, UID_INVALID, GID_INVALID, 0755); if (r < 0) return log_error_errno(r, "Failed to create parent directory '%s': %m", dn); - pfd = chase_symlinks_and_open(dn, fs, CHASE_PREFIX_ROOT|CHASE_WARN, O_RDONLY|O_DIRECTORY|O_CLOEXEC, NULL); + pfd = chase_symlinks_and_open(dn, root, CHASE_PREFIX_ROOT|CHASE_WARN, O_RDONLY|O_DIRECTORY|O_CLOEXEC, NULL); if (pfd < 0) return log_error_errno(pfd, "Failed to open parent directory of target: %m"); @@ -3239,11 +3241,11 @@ static int do_copy_files(Partition *p, const char *fs) { if (r < 0) return log_error_errno(r, "Failed to extract directory from '%s': %m", *target); - r = mkdir_p_root(fs, dn, UID_INVALID, GID_INVALID, 0755); + r = mkdir_p_root(root, dn, UID_INVALID, GID_INVALID, 0755); if (r < 0) return log_error_errno(r, "Failed to create parent directory: %m"); - pfd = chase_symlinks_and_open(dn, fs, CHASE_PREFIX_ROOT|CHASE_WARN, O_RDONLY|O_DIRECTORY|O_CLOEXEC, NULL); + pfd = chase_symlinks_and_open(dn, root, CHASE_PREFIX_ROOT|CHASE_WARN, O_RDONLY|O_DIRECTORY|O_CLOEXEC, NULL); if (pfd < 0) return log_error_errno(pfd, "Failed to open parent directory of target: %m"); @@ -3264,15 +3266,15 @@ static int do_copy_files(Partition *p, const char *fs) { return 0; } -static int do_make_directories(Partition *p, const char *fs) { +static int do_make_directories(Partition *p, const char *root) { int r; assert(p); - assert(fs); + assert(root); STRV_FOREACH(d, p->make_directories) { - r = mkdir_p_root(fs, *d, UID_INVALID, GID_INVALID, 0755); + r = mkdir_p_root(root, *d, UID_INVALID, GID_INVALID, 0755); if (r < 0) return log_error_errno(r, "Failed to create directory '%s' in file system: %m", *d); } @@ -3280,12 +3282,67 @@ static int do_make_directories(Partition *p, const char *fs) { 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), 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); diff --git a/src/shared/mkfs-util.c b/src/shared/mkfs-util.c index accd64f..4c31045 100644 --- a/src/shared/mkfs-util.c +++ b/src/shared/mkfs-util.c @@ -90,6 +90,7 @@ int make_filesystem( const char *node, const char *fstype, const char *label, + const char *root, sd_id128_t uuid, bool discard) { @@ -102,12 +103,28 @@ int make_filesystem( assert(label); if (streq(fstype, "swap")) { + if (root) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "A swap filesystem can't be populated, refusing"); r = find_executable("mkswap", &mkfs); if (r == -ENOENT) return log_error_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "mkswap binary not available."); if (r < 0) return log_error_errno(r, "Failed to determine whether mkswap binary exists: %m"); + } else if (streq(fstype, "squashfs")) { + if (!root) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Cannot generate squashfs filesystems without a source tree."); + + r = find_executable("mksquashfs", &mkfs); + if (r == -ENOENT) + return log_error_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "mksquashfs binary not available."); + if (r < 0) + return log_error_errno(r, "Failed to determine whether mksquashfs binary exists: %m"); } else { + if (root) + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "Populating with source tree is only supported for squashfs"); r = mkfs_exists(fstype); if (r < 0) return log_error_errno(r, "Failed to determine whether mkfs binary for %s exists: %m", fstype); @@ -225,6 +242,13 @@ int make_filesystem( "-U", vol_id, node, NULL); + else if (streq(fstype, "squashfs")) + + (void) execlp(mkfs, mkfs, + root, node, + "-quiet", + "-noappend", + NULL); else /* Generic fallback for all other file systems */ (void) execlp(mkfs, mkfs, node, NULL); diff --git a/src/shared/mkfs-util.h b/src/shared/mkfs-util.h index 7647afb..7cff116 100644 --- a/src/shared/mkfs-util.h +++ b/src/shared/mkfs-util.h @@ -7,4 +7,4 @@ int mkfs_exists(const char *fstype); -int make_filesystem(const char *node, const char *fstype, const char *label, sd_id128_t uuid, bool discard); +int make_filesystem(const char *node, const char *fstype, const char *label, const char *root, sd_id128_t uuid, bool discard); diff --git a/src/test/test-loop-block.c b/src/test/test-loop-block.c index ee28ad4..83d158f 100644 --- a/src/test/test-loop-block.c +++ b/src/test/test-loop-block.c @@ -234,16 +234,16 @@ static int run(int argc, char *argv[]) { assert_se(dissected->partitions[PARTITION_HOME].node); assert_se(sd_id128_randomize(&id) >= 0); - assert_se(make_filesystem(dissected->partitions[PARTITION_ESP].node, "vfat", "EFI", id, true) >= 0); + assert_se(make_filesystem(dissected->partitions[PARTITION_ESP].node, "vfat", "EFI", NULL, id, true) >= 0); assert_se(sd_id128_randomize(&id) >= 0); - assert_se(make_filesystem(dissected->partitions[PARTITION_XBOOTLDR].node, "vfat", "xbootldr", id, true) >= 0); + assert_se(make_filesystem(dissected->partitions[PARTITION_XBOOTLDR].node, "vfat", "xbootldr", NULL, id, true) >= 0); assert_se(sd_id128_randomize(&id) >= 0); - assert_se(make_filesystem(dissected->partitions[PARTITION_ROOT].node, "ext4", "root", id, true) >= 0); + assert_se(make_filesystem(dissected->partitions[PARTITION_ROOT].node, "ext4", "root", NULL, id, true) >= 0); assert_se(sd_id128_randomize(&id) >= 0); - assert_se(make_filesystem(dissected->partitions[PARTITION_HOME].node, "ext4", "home", id, true) >= 0); + assert_se(make_filesystem(dissected->partitions[PARTITION_HOME].node, "ext4", "home", NULL, id, true) >= 0); dissected = dissected_image_unref(dissected); assert_se(dissect_loop_device(loop, NULL, NULL, 0, &dissected) >= 0);