diff --git a/man/repart.d.xml b/man/repart.d.xml index 2e3aa68f44..a3c42e15e0 100644 --- a/man/repart.d.xml +++ b/man/repart.d.xml @@ -811,6 +811,68 @@ + + + Compression= + + Specify the compression algorithm to use for the filesystem configured with + Format=. Takes a single argument specifying the compression algorithm. + + Note that this setting is only taken into account when the filesystem configured with + Format= supports compression (btrfs, squashfs, erofs). Here's an incomplete list + of compression algorithms supported by the filesystems known to + systemd-repart: + + + File System Compression Algorithms + + + + + + + + + File System + Compression Algorithms + Documentation + + + + + + squashfs + gzip, lzo, lz4, xz, zstd, lzma + mksquashfs1 + + + + erofs + lz4, lz4hc, lzma, deflate, libdeflate, zstd + mkfs.erofs1 + + + +
+ +
+
+ + + CompressionLevel= + + Specify the compression level to use for the filesystem configured with + Format=. Takes a single argument specifying the compression level to use for the + configured compression algorithm. The possible compression levels and their meaning are filesystem + specific (refer to the filesystem's documentation for the exact meaning of a particular compression + level). + + Note that this setting is only taken into account when the filesystem configured with + Format= supports compression and the Compression= setting is + configured explicitly. + + + diff --git a/src/home/homework-luks.c b/src/home/homework-luks.c index ed5ee930c1..b859658f00 100644 --- a/src/home/homework-luks.c +++ b/src/home/homework-luks.c @@ -2378,6 +2378,8 @@ int home_create_luks( user_record_luks_discard(h), /* quiet = */ true, /* sector_size = */ 0, + /* compression = */ NULL, + /* compression_level= */ NULL, extra_mkfs_options); if (r < 0) return r; diff --git a/src/partition/makefs.c b/src/partition/makefs.c index 53439a4bbc..c76e81d976 100644 --- a/src/partition/makefs.c +++ b/src/partition/makefs.c @@ -78,6 +78,8 @@ static int run(int argc, char *argv[]) { /* discard = */ true, /* quiet = */ true, /* sector_size = */ 0, + /* compression = */ NULL, + /* compression_level = */ NULL, /* extra_mkfs_options = */ NULL); } diff --git a/src/partition/repart.c b/src/partition/repart.c index d770382789..6e76cb011a 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -385,6 +385,8 @@ typedef struct Partition { MinimizeMode minimize; uint64_t verity_data_block_size; uint64_t verity_hash_block_size; + char *compression; + char *compression_level; uint64_t gpt_flags; int no_auto; @@ -545,6 +547,8 @@ static Partition* partition_free(Partition *p) { ordered_hashmap_free(p->subvolumes); free(p->default_subvolume); free(p->verity_match_key); + free(p->compression); + free(p->compression_level); iovec_done(&p->roothash); @@ -581,6 +585,8 @@ static void partition_foreignize(Partition *p) { p->subvolumes = ordered_hashmap_free(p->subvolumes); p->default_subvolume = mfree(p->default_subvolume); p->verity_match_key = mfree(p->verity_match_key); + p->compression = mfree(p->compression); + p->compression_level = mfree(p->compression_level); p->priority = 0; p->weight = 1000; @@ -2088,38 +2094,40 @@ static int partition_finalize_fstype(Partition *p, const char *path) { static int partition_read_definition(Partition *p, const char *path, const char *const *conf_file_dirs) { ConfigTableItem table[] = { - { "Partition", "Type", config_parse_type, 0, &p->type }, - { "Partition", "Label", config_parse_label, 0, &p->new_label }, - { "Partition", "UUID", config_parse_uuid, 0, p }, - { "Partition", "Priority", config_parse_int32, 0, &p->priority }, - { "Partition", "Weight", config_parse_weight, 0, &p->weight }, - { "Partition", "PaddingWeight", config_parse_weight, 0, &p->padding_weight }, - { "Partition", "SizeMinBytes", config_parse_size4096, -1, &p->size_min }, - { "Partition", "SizeMaxBytes", config_parse_size4096, 1, &p->size_max }, - { "Partition", "PaddingMinBytes", config_parse_size4096, -1, &p->padding_min }, - { "Partition", "PaddingMaxBytes", config_parse_size4096, 1, &p->padding_max }, - { "Partition", "FactoryReset", config_parse_bool, 0, &p->factory_reset }, - { "Partition", "CopyBlocks", config_parse_copy_blocks, 0, p }, - { "Partition", "Format", config_parse_fstype, 0, &p->format }, - { "Partition", "CopyFiles", config_parse_copy_files, 0, &p->copy_files }, - { "Partition", "ExcludeFiles", config_parse_exclude_files, 0, &p->exclude_files_source }, - { "Partition", "ExcludeFilesTarget", config_parse_exclude_files, 0, &p->exclude_files_target }, - { "Partition", "MakeDirectories", config_parse_make_dirs, 0, &p->make_directories }, - { "Partition", "Encrypt", config_parse_encrypt, 0, &p->encrypt }, - { "Partition", "Verity", config_parse_verity, 0, &p->verity }, - { "Partition", "VerityMatchKey", config_parse_string, 0, &p->verity_match_key }, - { "Partition", "Flags", config_parse_gpt_flags, 0, &p->gpt_flags }, - { "Partition", "ReadOnly", config_parse_tristate, 0, &p->read_only }, - { "Partition", "NoAuto", config_parse_tristate, 0, &p->no_auto }, - { "Partition", "GrowFileSystem", config_parse_tristate, 0, &p->growfs }, - { "Partition", "SplitName", config_parse_string, 0, &p->split_name_format }, - { "Partition", "Minimize", config_parse_minimize, 0, &p->minimize }, - { "Partition", "Subvolumes", config_parse_subvolumes, 0, &p->subvolumes }, - { "Partition", "DefaultSubvolume", config_parse_default_subvolume, 0, &p->default_subvolume }, - { "Partition", "VerityDataBlockSizeBytes", config_parse_block_size, 0, &p->verity_data_block_size }, - { "Partition", "VerityHashBlockSizeBytes", config_parse_block_size, 0, &p->verity_hash_block_size }, - { "Partition", "MountPoint", config_parse_mountpoint, 0, p }, - { "Partition", "EncryptedVolume", config_parse_encrypted_volume, 0, p }, + { "Partition", "Type", config_parse_type, 0, &p->type }, + { "Partition", "Label", config_parse_label, 0, &p->new_label }, + { "Partition", "UUID", config_parse_uuid, 0, p }, + { "Partition", "Priority", config_parse_int32, 0, &p->priority }, + { "Partition", "Weight", config_parse_weight, 0, &p->weight }, + { "Partition", "PaddingWeight", config_parse_weight, 0, &p->padding_weight }, + { "Partition", "SizeMinBytes", config_parse_size4096, -1, &p->size_min }, + { "Partition", "SizeMaxBytes", config_parse_size4096, 1, &p->size_max }, + { "Partition", "PaddingMinBytes", config_parse_size4096, -1, &p->padding_min }, + { "Partition", "PaddingMaxBytes", config_parse_size4096, 1, &p->padding_max }, + { "Partition", "FactoryReset", config_parse_bool, 0, &p->factory_reset }, + { "Partition", "CopyBlocks", config_parse_copy_blocks, 0, p }, + { "Partition", "Format", config_parse_fstype, 0, &p->format }, + { "Partition", "CopyFiles", config_parse_copy_files, 0, &p->copy_files }, + { "Partition", "ExcludeFiles", config_parse_exclude_files, 0, &p->exclude_files_source }, + { "Partition", "ExcludeFilesTarget", config_parse_exclude_files, 0, &p->exclude_files_target }, + { "Partition", "MakeDirectories", config_parse_make_dirs, 0, &p->make_directories }, + { "Partition", "Encrypt", config_parse_encrypt, 0, &p->encrypt }, + { "Partition", "Verity", config_parse_verity, 0, &p->verity }, + { "Partition", "VerityMatchKey", config_parse_string, 0, &p->verity_match_key }, + { "Partition", "Flags", config_parse_gpt_flags, 0, &p->gpt_flags }, + { "Partition", "ReadOnly", config_parse_tristate, 0, &p->read_only }, + { "Partition", "NoAuto", config_parse_tristate, 0, &p->no_auto }, + { "Partition", "GrowFileSystem", config_parse_tristate, 0, &p->growfs }, + { "Partition", "SplitName", config_parse_string, 0, &p->split_name_format }, + { "Partition", "Minimize", config_parse_minimize, 0, &p->minimize }, + { "Partition", "Subvolumes", config_parse_subvolumes, 0, &p->subvolumes }, + { "Partition", "DefaultSubvolume", config_parse_default_subvolume, 0, &p->default_subvolume }, + { "Partition", "VerityDataBlockSizeBytes", config_parse_block_size, 0, &p->verity_data_block_size }, + { "Partition", "VerityHashBlockSizeBytes", config_parse_block_size, 0, &p->verity_hash_block_size }, + { "Partition", "MountPoint", config_parse_mountpoint, 0, p }, + { "Partition", "EncryptedVolume", config_parse_encrypted_volume, 0, p }, + { "Partition", "Compression", config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, &p->compression }, + { "Partition", "CompressionLevel", config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, &p->compression_level }, {} }; _cleanup_free_ char *filename = NULL; @@ -5469,7 +5477,8 @@ static int context_mkfs(Context *context) { r = make_filesystem(partition_target_path(t), p->format, strempty(p->new_label), root, p->fs_uuid, arg_discard, /* quiet = */ false, - context->fs_sector_size, extra_mkfs_options); + context->fs_sector_size, p->compression, p->compression_level, + extra_mkfs_options); if (r < 0) return r; @@ -7033,6 +7042,8 @@ static int context_minimize(Context *context) { fs_uuid, arg_discard, /* quiet = */ false, context->fs_sector_size, + p->compression, + p->compression_level, extra_mkfs_options); if (r < 0) return r; @@ -7114,6 +7125,8 @@ static int context_minimize(Context *context) { arg_discard, /* quiet = */ false, context->fs_sector_size, + p->compression, + p->compression_level, extra_mkfs_options); if (r < 0) return r; diff --git a/src/shared/mkfs-util.c b/src/shared/mkfs-util.c index 7624989929..15652cf57a 100644 --- a/src/shared/mkfs-util.c +++ b/src/shared/mkfs-util.c @@ -323,6 +323,8 @@ int make_filesystem( bool discard, bool quiet, uint64_t sector_size, + char *compression, + char *compression_level, char * const *extra_mkfs_args) { _cleanup_free_ char *mkfs = NULL, *mangled_label = NULL; @@ -570,12 +572,19 @@ int make_filesystem( root, node, "-noappend"); + if (compression) { + if (strv_extend_many(&argv, "-comp", compression) < 0) + return log_oom(); + + if (compression_level && strv_extend_many(&argv, "-Xcompression-level", compression_level) < 0) + return log_oom(); + } + /* mksquashfs -quiet option is pretty new so let's redirect stdout to /dev/null instead. */ if (quiet) stdio_fds[1] = -EBADF; } else if (streq(fstype, "erofs")) { - argv = strv_new(mkfs, "-U", vol_id, node, root); @@ -583,6 +592,20 @@ int make_filesystem( if (quiet && strv_extend(&argv, "--quiet") < 0) return log_oom(); + if (compression) { + _cleanup_free_ char *c = NULL; + + c = strjoin("-z", compression); + if (!c) + return log_oom(); + + if (compression_level && !strextend(&c, ",level=", compression_level)) + return log_oom(); + + if (strv_extend(&argv, c) < 0) + return log_oom(); + } + } else /* Generic fallback for all other file systems */ argv = strv_new(mkfs, node); diff --git a/src/shared/mkfs-util.h b/src/shared/mkfs-util.h index 9a1cb585d6..165862e7e1 100644 --- a/src/shared/mkfs-util.h +++ b/src/shared/mkfs-util.h @@ -20,6 +20,8 @@ int make_filesystem( bool discard, bool quiet, uint64_t sector_size, + char *compression, + char *compression_level, char * const *extra_mkfs_args); int mkfs_options_from_env(const char *component, const char *fstype, char ***ret); diff --git a/src/test/test-loop-block.c b/src/test/test-loop-block.c index 15c635781b..e69c0d5caf 100644 --- a/src/test/test-loop-block.c +++ b/src/test/test-loop-block.c @@ -251,16 +251,16 @@ static int run(int argc, char *argv[]) { assert_se(r >= 0); assert_se(sd_id128_randomize(&id) >= 0); - assert_se(make_filesystem(dissected->partitions[PARTITION_ESP].node, "vfat", "EFI", NULL, id, true, false, 0, NULL) >= 0); + assert_se(make_filesystem(dissected->partitions[PARTITION_ESP].node, "vfat", "EFI", NULL, id, true, false, 0, NULL, NULL, NULL) >= 0); assert_se(sd_id128_randomize(&id) >= 0); - assert_se(make_filesystem(dissected->partitions[PARTITION_XBOOTLDR].node, "vfat", "xbootldr", NULL, id, true, false, 0, NULL) >= 0); + assert_se(make_filesystem(dissected->partitions[PARTITION_XBOOTLDR].node, "vfat", "xbootldr", NULL, id, true, false, 0, NULL, NULL, NULL) >= 0); assert_se(sd_id128_randomize(&id) >= 0); - assert_se(make_filesystem(dissected->partitions[PARTITION_ROOT].node, "ext4", "root", NULL, id, true, false, 0, NULL) >= 0); + assert_se(make_filesystem(dissected->partitions[PARTITION_ROOT].node, "ext4", "root", NULL, id, true, false, 0, NULL, NULL, NULL) >= 0); assert_se(sd_id128_randomize(&id) >= 0); - assert_se(make_filesystem(dissected->partitions[PARTITION_HOME].node, "ext4", "home", NULL, id, true, false, 0, NULL) >= 0); + assert_se(make_filesystem(dissected->partitions[PARTITION_HOME].node, "ext4", "home", NULL, id, true, false, 0, NULL, NULL, NULL) >= 0); dissected = dissected_image_unref(dissected); diff --git a/test/units/TEST-58-REPART.sh b/test/units/TEST-58-REPART.sh index 968d332f06..b2a3be686c 100755 --- a/test/units/TEST-58-REPART.sh +++ b/test/units/TEST-58-REPART.sh @@ -1312,6 +1312,41 @@ testcase_list_devices() { systemd-repart --list-devices } +testcase_compression() { + local workdir image defs + + workdir="$(mktemp --directory "/tmp/test-repart.compression.XXXXXXXXXX")" + # shellcheck disable=SC2064 + trap "rm -rf '${workdir:?}'" RETURN + + image="$workdir/image.img" + defs="$workdir/defs" + mkdir "$defs" + + # TODO: add btrfs once btrfs-progs v6.11 is available in distributions. + for format in squashfs erofs; do + if ! command -v "mkfs.$format" && ! command -v mksquashfs >/dev/null; then + continue + fi + + [[ "$format" == "squashfs" ]] && compression=zstd + [[ "$format" == "erofs" ]] && compression=lz4hc + + tee "$defs/10-root.conf" <