From: Daan De Meyer Date: Fri, 29 Mar 2024 14:56:01 +0000 (+0100) Subject: repart: Add DefaultSubvolume= setting X-Git-Tag: v256-rc1~353 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3799fa803efb04cdd1f1b239c6c64803fe85d13a;p=thirdparty%2Fsystemd.git repart: Add DefaultSubvolume= setting We already have Subvolumes= to create subvolumes, let's add DefaultSubvolume= as well to set the default subvolume. --- diff --git a/man/repart.d.xml b/man/repart.d.xml index 91db8a91d42..0a2ae624914 100644 --- a/man/repart.d.xml +++ b/man/repart.d.xml @@ -551,6 +551,22 @@ + + DefaultSubvolume= + + Takes an absolute path specifying the default subvolume within the new filesystem. + Note that this setting does not create the subvolume itself, that can be configured with + Subvolumes=. + + Note that this option only takes effect if the target filesystem supports subvolumes, such as + btrfs. + + Note that due to limitations of mkfs.btrfs, this option is only supported + when running with . + + + + Encrypt= diff --git a/src/partition/repart.c b/src/partition/repart.c index ddb7fcb046d..fa556874dff 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -300,6 +300,7 @@ typedef struct Partition { char **exclude_files_target; char **make_directories; char **subvolumes; + char *default_subvolume; EncryptMode encrypt; VerityMode verity; char *verity_match_key; @@ -1668,6 +1669,41 @@ static int config_parse_make_dirs( } } +static int config_parse_default_subvolume( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + char **subvol = ASSERT_PTR(data); + _cleanup_free_ char *p = NULL; + int r; + + if (isempty(rvalue)) { + *subvol = mfree(*subvol); + return 0; + } + + r = specifier_printf(rvalue, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &p); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to expand specifiers in DefaultSubvolume= parameter, ignoring: %s", rvalue); + return 0; + } + + r = path_simplify_and_warn(p, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue); + if (r < 0) + return 0; + + return free_and_replace(*subvol, p); +} + static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_encrypt, encrypt_mode, EncryptMode, ENCRYPT_OFF, "Invalid encryption mode"); static int config_parse_gpt_flags( @@ -1861,37 +1897,38 @@ static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_minimize, minimize_mod 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_make_dirs, 0, &p->subvolumes }, - { "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_make_dirs, 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 }, {} }; int r; @@ -2018,6 +2055,14 @@ static int partition_read_definition(Partition *p, const char *path, const char return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EOPNOTSUPP), "Subvolumes= cannot be used with --offline=yes"); + if (p->default_subvolume && arg_offline > 0) + return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EOPNOTSUPP), + "DefaultSubvolume= cannot be used with --offline=yes"); + + if (p->default_subvolume && !path_strv_contains(p->subvolumes, p->default_subvolume)) + return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL), + "DefaultSubvolume= must be one of the paths in Subvolumes="); + /* Verity partitions are read only, let's imply the RO flag hence, unless explicitly configured otherwise. */ if ((IN_SET(p->type.designator, PARTITION_ROOT_VERITY, @@ -3736,6 +3781,11 @@ static int prepare_temporary_file(PartitionTarget *t, uint64_t size) { return 0; } +static bool loop_device_error_is_fatal(const Partition *p, int r) { + assert(p); + return arg_offline == 0 || (r != -ENOENT && !ERRNO_IS_PRIVILEGE(r)) || !strv_isempty(p->subvolumes) || p->default_subvolume; +} + static int partition_target_prepare( Context *context, Partition *p, @@ -3775,7 +3825,7 @@ static int partition_target_prepare( if (arg_offline <= 0) { r = loop_device_make(whole_fd, O_RDWR, p->offset, size, context->sector_size, 0, LOCK_EX, &d); - if (r < 0 && (arg_offline == 0 || (r != -ENOENT && !ERRNO_IS_PRIVILEGE(r)) || !strv_isempty(p->subvolumes))) + if (r < 0 && loop_device_error_is_fatal(p, r)) return log_error_errno(r, "Failed to make loopback device of future partition %" PRIu64 ": %m", p->partno); if (r >= 0) { t->loop = TAKE_PTR(d); @@ -4878,6 +4928,27 @@ static int do_make_directories(Partition *p, const char *root) { return 0; } +static int set_default_subvolume(Partition *p, const char *root) { + _cleanup_free_ char *path = NULL; + int r; + + assert(p); + assert(root); + + if (!p->default_subvolume) + return 0; + + path = path_join(root, p->default_subvolume); + if (!path) + return log_oom(); + + r = btrfs_subvol_make_default(path); + if (r < 0) + return log_error_errno(r, "Failed to make '%s' the default subvolume: %m", p->default_subvolume); + + return 0; +} + static bool partition_needs_populate(Partition *p) { assert(p); return !strv_isempty(p->copy_files) || !strv_isempty(p->make_directories); @@ -4952,6 +5023,9 @@ static int partition_populate_filesystem(Context *context, Partition *p, const c if (do_make_directories(p, fs) < 0) _exit(EXIT_FAILURE); + if (set_default_subvolume(p, fs) < 0) + _exit(EXIT_FAILURE); + r = syncfs_path(AT_FDCWD, fs); if (r < 0) { log_error_errno(r, "Failed to synchronize written files: %m"); @@ -6547,7 +6621,7 @@ static int context_minimize(Context *context) { if (arg_offline <= 0) { r = loop_device_make(fd, O_RDWR, 0, UINT64_MAX, context->sector_size, 0, LOCK_EX, &d); - if (r < 0 && (arg_offline == 0 || (r != -ENOENT && !ERRNO_IS_PRIVILEGE(r)) || !strv_isempty(p->subvolumes))) + if (r < 0 && loop_device_error_is_fatal(p, r)) return log_error_errno(r, "Failed to make loopback device of %s: %m", temp); } diff --git a/src/shared/btrfs-util.c b/src/shared/btrfs-util.c index 2ed6bf24a2d..5db88221f37 100644 --- a/src/shared/btrfs-util.c +++ b/src/shared/btrfs-util.c @@ -1785,6 +1785,24 @@ int btrfs_subvol_auto_qgroup(const char *path, uint64_t subvol_id, bool create_i return btrfs_subvol_auto_qgroup_fd(fd, subvol_id, create_intermediary_qgroup); } +int btrfs_subvol_make_default(const char *path) { + _cleanup_close_ int fd = -EBADF; + uint64_t id; + int r; + + assert(path); + + fd = open(path, O_NOCTTY|O_CLOEXEC|O_DIRECTORY); + if (fd < 0) + return -errno; + + r = btrfs_subvol_get_id_fd(fd, &id); + if (r < 0) + return r; + + return RET_NERRNO(ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &id)); +} + int btrfs_subvol_get_parent(int fd, uint64_t subvol_id, uint64_t *ret) { struct btrfs_ioctl_search_args args = { diff --git a/src/shared/btrfs-util.h b/src/shared/btrfs-util.h index cd80903190d..6108a264c0c 100644 --- a/src/shared/btrfs-util.h +++ b/src/shared/btrfs-util.h @@ -108,6 +108,8 @@ int btrfs_subvol_set_subtree_quota_limit_fd(int fd, uint64_t subvol_id, uint64_t int btrfs_subvol_auto_qgroup_fd(int fd, uint64_t subvol_id, bool new_qgroup); int btrfs_subvol_auto_qgroup(const char *path, uint64_t subvol_id, bool create_intermediary_qgroup); +int btrfs_subvol_make_default(const char *path); + int btrfs_qgroupid_make(uint64_t level, uint64_t id, uint64_t *ret); int btrfs_qgroupid_split(uint64_t qgroupid, uint64_t *level, uint64_t *id);