mirror of
https://github.com/systemd/systemd.git
synced 2025-03-19 22:50:17 +03:00
repart: Switch to new mkfs.btrfs subvolume API
In https://github.com/kdave/btrfs-progs/pull/877 the API is changing to "--subvol <path>:ro,default" so let's adapt our usage to match. This also adds support for read-only subvolumes. Fixes #34134
This commit is contained in:
parent
9023266a0e
commit
562881c178
@ -541,9 +541,31 @@
|
||||
<term><varname>Subvolumes=</varname></term>
|
||||
|
||||
<listitem><para>Takes one or more absolute paths, separated by whitespace, each declaring a directory
|
||||
that should be a subvolume within the new file system. This option may be used more than once to
|
||||
specify multiple directories. Note that this setting does not create the directories themselves, that
|
||||
can be configured with <varname>MakeDirectories=</varname> and <varname>CopyFiles=</varname>.</para>
|
||||
that should be a subvolume within the new file system. Each path may optionally be followed by a
|
||||
colon and a list of comma-separated subvolume flags. The following flags are understood:</para>
|
||||
|
||||
<table class='flags'>
|
||||
<title>Subvolume Flags</title>
|
||||
<tgroup cols='2' align='left' colsep='1' rowsep='1'>
|
||||
<colspec colname="spec" />
|
||||
<colspec colname="purpose" />
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Flag</entry>
|
||||
<entry>Purpose</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row id='R'>
|
||||
<entry><literal>ro</literal></entry>
|
||||
<entry>Make this subvolume read-only.</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
<para>Note that this option does not create the directories themselves, that can be configured with
|
||||
<varname>MakeDirectories=</varname> and <varname>CopyFiles=</varname>.</para>
|
||||
|
||||
<para>Note that this option only takes effect if the target filesystem supports subvolumes, such as
|
||||
<literal>btrfs</literal>.</para>
|
||||
|
@ -48,6 +48,7 @@
|
||||
#include "io-util.h"
|
||||
#include "json-util.h"
|
||||
#include "list.h"
|
||||
#include "logarithm.h"
|
||||
#include "loop-util.h"
|
||||
#include "main-func.h"
|
||||
#include "mkdir.h"
|
||||
@ -259,6 +260,78 @@ static PartitionEncryptedVolume* partition_encrypted_volume_free(PartitionEncryp
|
||||
return mfree(c);
|
||||
}
|
||||
|
||||
typedef enum SubvolumeFlags {
|
||||
SUBVOLUME_RO = 1 << 0,
|
||||
_SUBVOLUME_FLAGS_MASK = SUBVOLUME_RO,
|
||||
_SUBVOLUME_FLAGS_INVALID = -EINVAL,
|
||||
_SUBVOLUME_FLAGS_ERRNO_MAX = -ERRNO_MAX, /* Ensure the whole errno range fits into this enum */
|
||||
} SubvolumeFlags;
|
||||
|
||||
static SubvolumeFlags subvolume_flags_from_string_one(const char *s) {
|
||||
/* This is a bitmask (i.e. not dense), hence we don't use the "string-table.h" stuff here. */
|
||||
|
||||
assert(s);
|
||||
|
||||
if (streq(s, "ro"))
|
||||
return SUBVOLUME_RO;
|
||||
|
||||
return _SUBVOLUME_FLAGS_INVALID;
|
||||
}
|
||||
|
||||
static SubvolumeFlags subvolume_flags_from_string(const char *s) {
|
||||
SubvolumeFlags flags = 0;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *f = NULL;
|
||||
SubvolumeFlags ff;
|
||||
|
||||
r = extract_first_word(&s, &f, ",", EXTRACT_DONT_COALESCE_SEPARATORS);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
ff = subvolume_flags_from_string_one(f);
|
||||
if (ff < 0)
|
||||
return -EBADRQC; /* recognizable error */
|
||||
|
||||
flags |= ff;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
static char* subvolume_flags_to_string(SubvolumeFlags flags) {
|
||||
const char *l[CONST_LOG2U(_SUBVOLUME_FLAGS_MASK + 1) + 1]; /* one string per known flag at most */
|
||||
size_t m = 0;
|
||||
|
||||
if (FLAGS_SET(flags, SUBVOLUME_RO))
|
||||
l[m++] = "ro";
|
||||
|
||||
assert(m < ELEMENTSOF(l));
|
||||
l[m] = NULL;
|
||||
|
||||
return strv_join((char**) l, ",");
|
||||
}
|
||||
|
||||
typedef struct Subvolume {
|
||||
char *path;
|
||||
SubvolumeFlags flags;
|
||||
} Subvolume;
|
||||
|
||||
static Subvolume* subvolume_free(Subvolume *s) {
|
||||
if (!s)
|
||||
return NULL;
|
||||
|
||||
free(s->path);
|
||||
return mfree(s);
|
||||
}
|
||||
|
||||
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(subvolume_hash_ops, char, path_hash_func, path_compare, Subvolume, subvolume_free);
|
||||
|
||||
typedef struct Partition {
|
||||
char *definition_path;
|
||||
char **drop_in_files;
|
||||
@ -304,7 +377,7 @@ typedef struct Partition {
|
||||
char **exclude_files_source;
|
||||
char **exclude_files_target;
|
||||
char **make_directories;
|
||||
char **subvolumes;
|
||||
OrderedHashmap *subvolumes;
|
||||
char *default_subvolume;
|
||||
EncryptMode encrypt;
|
||||
VerityMode verity;
|
||||
@ -469,7 +542,7 @@ static Partition* partition_free(Partition *p) {
|
||||
strv_free(p->exclude_files_source);
|
||||
strv_free(p->exclude_files_target);
|
||||
strv_free(p->make_directories);
|
||||
strv_free(p->subvolumes);
|
||||
ordered_hashmap_free(p->subvolumes);
|
||||
free(p->default_subvolume);
|
||||
free(p->verity_match_key);
|
||||
|
||||
@ -505,7 +578,7 @@ static void partition_foreignize(Partition *p) {
|
||||
p->exclude_files_source = strv_free(p->exclude_files_source);
|
||||
p->exclude_files_target = strv_free(p->exclude_files_target);
|
||||
p->make_directories = strv_free(p->make_directories);
|
||||
p->subvolumes = strv_free(p->subvolumes);
|
||||
p->subvolumes = ordered_hashmap_free(p->subvolumes);
|
||||
p->default_subvolume = mfree(p->default_subvolume);
|
||||
p->verity_match_key = mfree(p->verity_match_key);
|
||||
|
||||
@ -1679,6 +1752,86 @@ static int config_parse_make_dirs(
|
||||
}
|
||||
}
|
||||
|
||||
static int config_parse_subvolumes(
|
||||
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) {
|
||||
|
||||
OrderedHashmap **subvolumes = ASSERT_PTR(data);
|
||||
const char *p = ASSERT_PTR(rvalue);
|
||||
int r;
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *word = NULL, *path = NULL, *f = NULL, *d = NULL;
|
||||
Subvolume *s = NULL;
|
||||
|
||||
r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
|
||||
return 0;
|
||||
}
|
||||
if (r == 0)
|
||||
return 0;
|
||||
|
||||
r = extract_many_words((const char **) &word, ":", EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS, &path, &f);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", word);
|
||||
continue;
|
||||
}
|
||||
|
||||
r = specifier_printf(path, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &d);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
||||
"Failed to expand specifiers in Subvolumes= parameter, ignoring: %s", path);
|
||||
continue;
|
||||
}
|
||||
|
||||
r = path_simplify_and_warn(d, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
|
||||
if (r < 0)
|
||||
continue;
|
||||
|
||||
s = ordered_hashmap_get(*subvolumes, d);
|
||||
if (!s) {
|
||||
s = new(Subvolume, 1);
|
||||
if (!s)
|
||||
return log_oom();
|
||||
|
||||
*s = (Subvolume) {
|
||||
.path = TAKE_PTR(d),
|
||||
};
|
||||
|
||||
r = ordered_hashmap_ensure_put(subvolumes, &subvolume_hash_ops, s->path, s);
|
||||
if (r < 0) {
|
||||
subvolume_free(s);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
if (f) {
|
||||
SubvolumeFlags flags = subvolume_flags_from_string(f);
|
||||
if (flags == -EBADRQC) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r, "Unknown subvolume flag in subvolume, ignoring: %s", f);
|
||||
continue;
|
||||
}
|
||||
if (flags < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse subvolume flags, ignoring: %s", f);
|
||||
continue;
|
||||
}
|
||||
|
||||
s->flags = flags;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int config_parse_default_subvolume(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
@ -1961,7 +2114,7 @@ static int partition_read_definition(Partition *p, const char *path, const char
|
||||
{ "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", "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 },
|
||||
@ -1969,9 +2122,9 @@ static int partition_read_definition(Partition *p, const char *path, const char
|
||||
{ "Partition", "EncryptedVolume", config_parse_encrypted_volume, 0, p },
|
||||
{}
|
||||
};
|
||||
int r;
|
||||
_cleanup_free_ char *filename = NULL;
|
||||
const char* dropin_dirname;
|
||||
int r;
|
||||
|
||||
r = path_extract_filename(path, &filename);
|
||||
if (r < 0)
|
||||
@ -2089,7 +2242,7 @@ static int partition_read_definition(Partition *p, const char *path, const char
|
||||
"SizeMinBytes=/SizeMaxBytes= cannot be used with Verity=%s.",
|
||||
verity_mode_to_string(p->verity));
|
||||
|
||||
if (p->default_subvolume && !path_strv_contains(p->subvolumes, p->default_subvolume))
|
||||
if (p->default_subvolume && !ordered_hashmap_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=.");
|
||||
|
||||
@ -4819,22 +4972,39 @@ static int add_subvolume_path(const char *path, Set **subvolumes) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int make_subvolumes_strv(const Partition *p, char ***ret) {
|
||||
_cleanup_strv_free_ char **subvolumes = NULL;
|
||||
Subvolume *subvolume;
|
||||
|
||||
assert(p);
|
||||
assert(ret);
|
||||
|
||||
ORDERED_HASHMAP_FOREACH(subvolume, p->subvolumes)
|
||||
if (strv_extend(&subvolumes, subvolume->path) < 0)
|
||||
return log_oom();
|
||||
|
||||
*ret = TAKE_PTR(subvolumes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int make_subvolumes_set(
|
||||
const Partition *p,
|
||||
const char *source,
|
||||
const char *target,
|
||||
Set **ret) {
|
||||
|
||||
_cleanup_set_free_ Set *subvolumes = NULL;
|
||||
Subvolume *subvolume;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
assert(target);
|
||||
assert(ret);
|
||||
|
||||
STRV_FOREACH(subvolume, p->subvolumes) {
|
||||
ORDERED_HASHMAP_FOREACH(subvolume, p->subvolumes) {
|
||||
_cleanup_free_ char *path = NULL;
|
||||
|
||||
const char *s = path_startswith(*subvolume, target);
|
||||
const char *s = path_startswith(subvolume->path, target);
|
||||
if (!s)
|
||||
continue;
|
||||
|
||||
@ -4876,11 +5046,16 @@ static usec_t epoch_or_infinity(void) {
|
||||
}
|
||||
|
||||
static int do_copy_files(Context *context, Partition *p, const char *root) {
|
||||
_cleanup_strv_free_ char **subvolumes = NULL;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
assert(root);
|
||||
|
||||
r = make_subvolumes_strv(p, &subvolumes);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* copy_tree_at() automatically copies the permissions of source directories to target directories if
|
||||
* it created them. However, the root directory is created by us, so we have to manually take care
|
||||
* that it is initialized. We use the first source directory targeting "/" as the metadata source for
|
||||
@ -4949,7 +5124,7 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to extract directory from '%s': %m", *target);
|
||||
|
||||
r = mkdir_p_root_full(root, dn, UID_INVALID, GID_INVALID, 0755, ts, p->subvolumes);
|
||||
r = mkdir_p_root_full(root, dn, UID_INVALID, GID_INVALID, 0755, ts, subvolumes);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create parent directory '%s': %m", dn);
|
||||
|
||||
@ -4989,7 +5164,7 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to extract directory from '%s': %m", *target);
|
||||
|
||||
r = mkdir_p_root_full(root, dn, UID_INVALID, GID_INVALID, 0755, ts, p->subvolumes);
|
||||
r = mkdir_p_root_full(root, dn, UID_INVALID, GID_INVALID, 0755, ts, subvolumes);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create parent directory: %m");
|
||||
|
||||
@ -5023,13 +5198,18 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
|
||||
}
|
||||
|
||||
static int do_make_directories(Partition *p, const char *root) {
|
||||
_cleanup_strv_free_ char **subvolumes = NULL;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
assert(root);
|
||||
|
||||
r = make_subvolumes_strv(p, &subvolumes);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
STRV_FOREACH(d, p->make_directories) {
|
||||
r = mkdir_p_root_full(root, *d, UID_INVALID, GID_INVALID, 0755, epoch_or_infinity(), p->subvolumes);
|
||||
r = mkdir_p_root_full(root, *d, UID_INVALID, GID_INVALID, 0755, epoch_or_infinity(), subvolumes);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create directory '%s' in file system: %m", *d);
|
||||
}
|
||||
@ -5037,6 +5217,27 @@ static int do_make_directories(Partition *p, const char *root) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int make_subvolumes_read_only(Partition *p, const char *root) {
|
||||
_cleanup_free_ char *path = NULL;
|
||||
Subvolume *subvolume;
|
||||
int r;
|
||||
|
||||
ORDERED_HASHMAP_FOREACH(subvolume, p->subvolumes) {
|
||||
if (!FLAGS_SET(subvolume->flags, SUBVOLUME_RO))
|
||||
continue;
|
||||
|
||||
path = path_join(root, subvolume->path);
|
||||
if (!path)
|
||||
return log_oom();
|
||||
|
||||
r = btrfs_subvol_set_read_only(path, true);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to make subvolume '%s' read-only: %m", subvolume->path);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_default_subvolume(Partition *p, const char *root) {
|
||||
_cleanup_free_ char *path = NULL;
|
||||
int r;
|
||||
@ -5132,6 +5333,9 @@ static int partition_populate_filesystem(Context *context, Partition *p, const c
|
||||
if (do_make_directories(p, fs) < 0)
|
||||
_exit(EXIT_FAILURE);
|
||||
|
||||
if (make_subvolumes_read_only(p, fs) < 0)
|
||||
_exit(EXIT_FAILURE);
|
||||
|
||||
if (set_default_subvolume(p, fs) < 0)
|
||||
_exit(EXIT_FAILURE);
|
||||
|
||||
@ -5149,8 +5353,8 @@ static int partition_populate_filesystem(Context *context, Partition *p, const c
|
||||
}
|
||||
|
||||
static int finalize_extra_mkfs_options(const Partition *p, const char *root, char ***ret) {
|
||||
int r;
|
||||
_cleanup_strv_free_ char **sv = NULL;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
assert(ret);
|
||||
@ -5162,17 +5366,26 @@ static int finalize_extra_mkfs_options(const Partition *p, const char *root, cha
|
||||
p->format);
|
||||
|
||||
if (partition_needs_populate(p) && root && streq(p->format, "btrfs")) {
|
||||
STRV_FOREACH(subvol, p->subvolumes) {
|
||||
if (streq_ptr(*subvol, p->default_subvolume))
|
||||
continue;
|
||||
Subvolume *subvolume;
|
||||
|
||||
r = strv_extend_many(&sv, "--subvol", *subvol);
|
||||
if (r < 0)
|
||||
ORDERED_HASHMAP_FOREACH(subvolume, p->subvolumes) {
|
||||
_cleanup_free_ char *s = NULL, *f = NULL;
|
||||
|
||||
s = strdup(subvolume->path);
|
||||
if (!s)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
if (p->default_subvolume) {
|
||||
r = strv_extend_many(&sv, "--default-subvol", p->default_subvolume);
|
||||
f = subvolume_flags_to_string(subvolume->flags);
|
||||
if (!f)
|
||||
return log_oom();
|
||||
|
||||
if (streq_ptr(subvolume->path, p->default_subvolume) && !strextend_with_separator(&f, ",", "default"))
|
||||
return log_oom();
|
||||
|
||||
if (!isempty(f) && !strextend_with_separator(&s, ":", f))
|
||||
return log_oom();
|
||||
|
||||
r = strv_extend_many(&sv, "--subvol", s);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user