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" <