mirror of
https://github.com/systemd/systemd.git
synced 2025-02-03 17:47:28 +03:00
Merge pull request #16308 from bluca/root_image_options
service: add new RootImageOptions feature
This commit is contained in:
commit
7e62257219
@ -145,6 +145,19 @@
|
||||
<xi:include href="system-only.xml" xpointer="singular"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>RootImageOptions=</varname></term>
|
||||
|
||||
<listitem><para>Takes a comma-separated list of mount options that will be used on disk images specified by
|
||||
<varname>RootImage=</varname>. Optionally a partition number can be prefixed, followed by colon, in
|
||||
case the image has multiple partitions, otherwise partition number 0 is implied.
|
||||
Options for multiple partitions can be specified in a single line with space separators. Assigning an empty
|
||||
string removes previous assignments. For a list of valid mount options, please refer to
|
||||
<citerefentry><refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
|
||||
|
||||
<xi:include href="system-only.xml" xpointer="singular"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>RootHash=</varname></term>
|
||||
|
||||
|
@ -169,3 +169,18 @@
|
||||
|
||||
#define LIST_IS_EMPTY(head) \
|
||||
(!(head))
|
||||
|
||||
/* Join two lists tail to head: a->b, c->d to a->b->c->d and de-initialise second list */
|
||||
#define LIST_JOIN(name,a,b) \
|
||||
do { \
|
||||
assert(b); \
|
||||
if (!(a)) \
|
||||
(a) = (b); \
|
||||
else { \
|
||||
typeof(*(a)) *_head = (b), *_tail; \
|
||||
LIST_FIND_TAIL(name, (a), _tail); \
|
||||
_tail->name##_next = _head; \
|
||||
_head->name##_prev = _tail; \
|
||||
} \
|
||||
(b) = NULL; \
|
||||
} while (false)
|
||||
|
@ -784,6 +784,37 @@ static int property_get_root_hash_sig(
|
||||
return sd_bus_message_append_array(reply, 'y', c->root_hash_sig, c->root_hash_sig_size);
|
||||
}
|
||||
|
||||
static int property_get_root_image_options(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
ExecContext *c = userdata;
|
||||
MountOptions *m;
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
assert(c);
|
||||
assert(property);
|
||||
assert(reply);
|
||||
|
||||
r = sd_bus_message_open_container(reply, 'a', "(us)");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
LIST_FOREACH(mount_options, m, c->root_image_options) {
|
||||
r = sd_bus_message_append(reply, "(us)", m->partition_number, m->options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return sd_bus_message_close_container(reply);
|
||||
}
|
||||
|
||||
const sd_bus_vtable bus_exec_vtable[] = {
|
||||
SD_BUS_VTABLE_START(0),
|
||||
SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(ExecContext, environment), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
@ -826,6 +857,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
|
||||
SD_BUS_PROPERTY("WorkingDirectory", "s", property_get_working_directory, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(ExecContext, root_directory), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("RootImage", "s", NULL, offsetof(ExecContext, root_image), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("RootImageOptions", "a(us)", property_get_root_image_options, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("RootHash", "ay", property_get_root_hash, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("RootHashPath", "s", NULL, offsetof(ExecContext, root_hash_path), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("RootHashSignature", "ay", property_get_root_hash_sig, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
@ -1301,6 +1333,62 @@ int bus_exec_context_set_transient_property(
|
||||
if (streq(name, "RootImage"))
|
||||
return bus_set_transient_path(u, name, &c->root_image, message, flags, error);
|
||||
|
||||
if (streq(name, "RootImageOptions")) {
|
||||
_cleanup_(mount_options_free_allp) MountOptions *options = NULL;
|
||||
_cleanup_free_ char *format_str = NULL;
|
||||
const char *mount_options;
|
||||
unsigned partition_number;
|
||||
|
||||
r = sd_bus_message_enter_container(message, 'a', "(us)");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
while ((r = sd_bus_message_read(message, "(us)", &partition_number, &mount_options)) > 0) {
|
||||
_cleanup_free_ char *previous = TAKE_PTR(format_str);
|
||||
_cleanup_free_ MountOptions *o = NULL;
|
||||
|
||||
if (chars_intersect(mount_options, WHITESPACE))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
|
||||
"Invalid mount options string, contains whitespace character(s): %s", mount_options);
|
||||
|
||||
if (asprintf(&format_str, "%s%s%u:%s", strempty(previous), previous ? " " : "", partition_number, mount_options) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
o = new(MountOptions, 1);
|
||||
if (!o)
|
||||
return -ENOMEM;
|
||||
*o = (MountOptions) {
|
||||
.partition_number = partition_number,
|
||||
.options = strdup(mount_options),
|
||||
};
|
||||
if (!o->options)
|
||||
return -ENOMEM;
|
||||
LIST_APPEND(mount_options, options, TAKE_PTR(o));
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_exit_container(message);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
||||
if (LIST_IS_EMPTY(options)) {
|
||||
c->root_image_options = mount_options_free_all(c->root_image_options);
|
||||
unit_write_settingf(u, flags, name, "%s=", name);
|
||||
} else {
|
||||
LIST_JOIN(mount_options, c->root_image_options, options);
|
||||
unit_write_settingf(
|
||||
u, flags|UNIT_ESCAPE_SPECIFIERS, name,
|
||||
"%s=%s",
|
||||
name,
|
||||
format_str);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (streq(name, "RootHash")) {
|
||||
const void *roothash_decoded;
|
||||
size_t roothash_decoded_size;
|
||||
|
@ -2660,7 +2660,7 @@ static int apply_mount_namespace(
|
||||
if (context->mount_flags == MS_SHARED)
|
||||
log_unit_debug(u, "shared mount propagation hidden by other fs namespacing unit settings: ignoring");
|
||||
|
||||
r = setup_namespace(root_dir, root_image,
|
||||
r = setup_namespace(root_dir, root_image, context->root_image_options,
|
||||
&ns_info, context->read_write_paths,
|
||||
needs_sandboxing ? context->read_only_paths : NULL,
|
||||
needs_sandboxing ? context->inaccessible_paths : NULL,
|
||||
@ -4207,6 +4207,7 @@ void exec_context_done(ExecContext *c) {
|
||||
c->working_directory = mfree(c->working_directory);
|
||||
c->root_directory = mfree(c->root_directory);
|
||||
c->root_image = mfree(c->root_image);
|
||||
c->root_image_options = mount_options_free_all(c->root_image_options);
|
||||
c->root_hash = mfree(c->root_hash);
|
||||
c->root_hash_size = 0;
|
||||
c->root_hash_path = mfree(c->root_hash_path);
|
||||
@ -4618,6 +4619,16 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
|
||||
if (c->root_image)
|
||||
fprintf(f, "%sRootImage: %s\n", prefix, c->root_image);
|
||||
|
||||
if (c->root_image_options) {
|
||||
MountOptions *o;
|
||||
|
||||
fprintf(f, "%sRootImageOptions:", prefix);
|
||||
LIST_FOREACH(mount_options, o, c->root_image_options)
|
||||
if (!isempty(o->options))
|
||||
fprintf(f, " %u:%s", o->partition_number, o->options);
|
||||
fprintf(f, "\n");
|
||||
}
|
||||
|
||||
if (c->root_hash) {
|
||||
_cleanup_free_ char *encoded = NULL;
|
||||
encoded = hexmem(c->root_hash, c->root_hash_size);
|
||||
|
@ -158,6 +158,7 @@ struct ExecContext {
|
||||
char *working_directory, *root_directory, *root_image, *root_verity, *root_hash_path, *root_hash_sig_path;
|
||||
void *root_hash, *root_hash_sig;
|
||||
size_t root_hash_size, root_hash_sig_size;
|
||||
LIST_HEAD(MountOptions, root_image_options);
|
||||
bool working_directory_missing_ok:1;
|
||||
bool working_directory_home:1;
|
||||
|
||||
|
@ -23,6 +23,7 @@ m4_define(`EXEC_CONTEXT_CONFIG_ITEMS',
|
||||
`$1.WorkingDirectory, config_parse_working_directory, 0, offsetof($1, exec_context)
|
||||
$1.RootDirectory, config_parse_unit_path_printf, true, offsetof($1, exec_context.root_directory)
|
||||
$1.RootImage, config_parse_unit_path_printf, true, offsetof($1, exec_context.root_image)
|
||||
$1.RootImageOptions, config_parse_root_image_options, 0, offsetof($1, exec_context)
|
||||
$1.RootHash, config_parse_exec_root_hash, 0, offsetof($1, exec_context)
|
||||
$1.RootHashSignature, config_parse_exec_root_hash_sig, 0, offsetof($1, exec_context)
|
||||
$1.RootVerity, config_parse_unit_path_printf, true, offsetof($1, exec_context.root_verity)
|
||||
|
@ -1416,6 +1416,97 @@ int config_parse_exec_cpu_sched_prio(const char *unit,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_root_image_options(
|
||||
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) {
|
||||
|
||||
_cleanup_(mount_options_free_allp) MountOptions *options = NULL;
|
||||
ExecContext *c = data;
|
||||
const Unit *u = userdata;
|
||||
const char *p = rvalue;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
c->root_image_options = mount_options_free_all(c->root_image_options);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *mount_options_resolved = NULL, *first = NULL, *tuple = NULL;
|
||||
const char *mount_options = NULL, *second = NULL;
|
||||
MountOptions *o = NULL;
|
||||
unsigned int partition_number = 0;
|
||||
|
||||
r = extract_first_word(&p, &tuple, WHITESPACE, EXTRACT_UNQUOTE);
|
||||
if (r == 0)
|
||||
break;
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s: %s", lvalue, rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
second = tuple;
|
||||
r = extract_first_word(&second, &first, ":", EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
|
||||
if (r == 0)
|
||||
continue;
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s: %s", lvalue, rvalue);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Format is either '0:foo' or 'foo' (0 is implied) */
|
||||
if (!isempty(second) && second[-1] == ':') {
|
||||
mount_options = second;
|
||||
r = safe_atou(first, &partition_number);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse partition number from \"%s\", ignoring: %m", first);
|
||||
continue;
|
||||
}
|
||||
} else
|
||||
mount_options = first;
|
||||
|
||||
r = unit_full_printf(u, mount_options, &mount_options_resolved);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", mount_options);
|
||||
continue;
|
||||
}
|
||||
|
||||
o = new(MountOptions, 1);
|
||||
if (!o)
|
||||
return log_oom();
|
||||
*o = (MountOptions) {
|
||||
.partition_number = partition_number,
|
||||
.options = TAKE_PTR(mount_options_resolved),
|
||||
};
|
||||
LIST_APPEND(mount_options, options, o);
|
||||
}
|
||||
|
||||
/* empty spaces/separators only */
|
||||
if (LIST_IS_EMPTY(options))
|
||||
c->root_image_options = mount_options_free_all(c->root_image_options);
|
||||
else
|
||||
LIST_JOIN(mount_options, c->root_image_options, options);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_exec_root_hash(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
|
@ -44,6 +44,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_exec_cpu_sched_policy);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_exec_cpu_sched_prio);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_exec_cpu_affinity);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_exec_secure_bits);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_root_image_options);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_exec_root_hash);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_exec_root_hash_sig);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_capability_set);
|
||||
|
@ -1257,6 +1257,7 @@ static bool home_read_only(
|
||||
int setup_namespace(
|
||||
const char* root_directory,
|
||||
const char* root_image,
|
||||
const MountOptions *root_image_options,
|
||||
const NamespaceInfo *ns_info,
|
||||
char** read_write_paths,
|
||||
char** read_only_paths,
|
||||
@ -1331,6 +1332,7 @@ int setup_namespace(
|
||||
root_hash ?: root_hash_decoded,
|
||||
root_hash_size,
|
||||
root_verity ?: verity_data,
|
||||
root_image_options,
|
||||
dissect_image_flags,
|
||||
&dissected_image);
|
||||
if (r < 0)
|
||||
|
@ -75,6 +75,7 @@ struct TemporaryFileSystem {
|
||||
int setup_namespace(
|
||||
const char *root_directory,
|
||||
const char *root_image,
|
||||
const MountOptions *root_image_options,
|
||||
const NamespaceInfo *ns_info,
|
||||
char **read_write_paths,
|
||||
char **read_only_paths,
|
||||
|
@ -244,7 +244,7 @@ static int run(int argc, char *argv[]) {
|
||||
return log_error_errno(r, "Failed to read verity artefacts for %s: %m", arg_image);
|
||||
arg_flags |= arg_verity_data ? DISSECT_IMAGE_NO_PARTITION_TABLE : 0;
|
||||
|
||||
r = dissect_image_and_warn(d->fd, arg_image, arg_root_hash, arg_root_hash_size, arg_verity_data, arg_flags, &m);
|
||||
r = dissect_image_and_warn(d->fd, arg_image, arg_root_hash, arg_root_hash_size, arg_verity_data, NULL, arg_flags, &m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -933,7 +933,7 @@ static int setup_image(char **ret_mount_dir, LoopDevice **ret_loop_device, Decry
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set up loopback device: %m");
|
||||
|
||||
r = dissect_image_and_warn(d->fd, arg_image, NULL, 0, NULL, f, &dissected_image);
|
||||
r = dissect_image_and_warn(d->fd, arg_image, NULL, 0, NULL, NULL, f, &dissected_image);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -665,7 +665,7 @@ static int enumerate_partitions(dev_t devnum) {
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
r = dissect_image(fd, NULL, 0, NULL, DISSECT_IMAGE_GPT_ONLY|DISSECT_IMAGE_NO_UDEV, &m);
|
||||
r = dissect_image(fd, NULL, 0, NULL, NULL, DISSECT_IMAGE_GPT_ONLY|DISSECT_IMAGE_NO_UDEV, &m);
|
||||
if (r == -ENOPKG) {
|
||||
log_debug_errno(r, "No suitable partition table found, ignoring.");
|
||||
return 0;
|
||||
|
@ -5223,6 +5223,7 @@ static int run(int argc, char *argv[]) {
|
||||
arg_image,
|
||||
arg_root_hash, arg_root_hash_size,
|
||||
arg_verity_data,
|
||||
NULL,
|
||||
dissect_image_flags,
|
||||
&dissected_image);
|
||||
if (r == -ENOPKG) {
|
||||
|
@ -380,7 +380,7 @@ static int portable_extract_by_path(
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to create temporary directory: %m");
|
||||
|
||||
r = dissect_image(d->fd, NULL, 0, NULL, DISSECT_IMAGE_READ_ONLY|DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK, &m);
|
||||
r = dissect_image(d->fd, NULL, 0, NULL, NULL, DISSECT_IMAGE_READ_ONLY|DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK, &m);
|
||||
if (r == -ENOPKG)
|
||||
sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Couldn't identify a suitable partition table or file system in '%s'.", path);
|
||||
else if (r == -EADDRNOTAVAIL)
|
||||
|
@ -1454,6 +1454,74 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
|
||||
return bus_append_byte_array(m, field, roothash_sig_decoded, roothash_sig_decoded_size);
|
||||
}
|
||||
|
||||
if (streq(field, "RootImageOptions")) {
|
||||
const char *p = eq;
|
||||
|
||||
r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_open_container(m, 'v', "a(us)");
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_open_container(m, 'a', "(us)");
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *first = NULL, *tuple = NULL;
|
||||
const char *mount_options = NULL, *second = NULL;
|
||||
unsigned partition_number = 0;
|
||||
|
||||
r = extract_first_word(&p, &tuple, WHITESPACE, EXTRACT_UNQUOTE);
|
||||
if (r == 0)
|
||||
break;
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse argument: %m");
|
||||
|
||||
second = tuple;
|
||||
r = extract_first_word(&second, &first, ":", EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
|
||||
if (r == 0)
|
||||
continue;
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse argument: %m");
|
||||
|
||||
/* Format is either '0:foo' or 'foo' (0 is implied) */
|
||||
if (!isempty(second) && second[-1] == ':') {
|
||||
mount_options = second;
|
||||
r = safe_atou(first, &partition_number);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to parse partition number from %s: %m", first);
|
||||
continue;
|
||||
}
|
||||
} else
|
||||
mount_options = first;
|
||||
|
||||
r = sd_bus_message_append(m, "(us)", partition_number, mount_options);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
}
|
||||
|
||||
r = sd_bus_message_close_container(m);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_close_container(m);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_close_container(m);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -308,6 +308,7 @@ int dissect_image(
|
||||
const void *root_hash,
|
||||
size_t root_hash_size,
|
||||
const char *verity_data,
|
||||
const MountOptions *mount_options,
|
||||
DissectImageFlags flags,
|
||||
DissectedImage **ret) {
|
||||
|
||||
@ -400,8 +401,8 @@ int dissect_image(
|
||||
|
||||
(void) blkid_probe_lookup_value(b, "USAGE", &usage, NULL);
|
||||
if (STRPTR_IN_SET(usage, "filesystem", "crypto")) {
|
||||
_cleanup_free_ char *t = NULL, *n = NULL;
|
||||
const char *fstype = NULL;
|
||||
_cleanup_free_ char *t = NULL, *n = NULL, *o = NULL;
|
||||
const char *fstype = NULL, *options = NULL;
|
||||
|
||||
/* OK, we have found a file system, that's our root partition then. */
|
||||
(void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
|
||||
@ -420,6 +421,13 @@ int dissect_image(
|
||||
m->verity = root_hash && verity_data;
|
||||
m->can_verity = !!verity_data;
|
||||
|
||||
options = mount_options_from_part(mount_options, 0);
|
||||
if (options) {
|
||||
o = strdup(options);
|
||||
if (!o)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
m->partitions[PARTITION_ROOT] = (DissectedPartition) {
|
||||
.found = true,
|
||||
.rw = !m->verity,
|
||||
@ -427,6 +435,7 @@ int dissect_image(
|
||||
.architecture = _ARCHITECTURE_INVALID,
|
||||
.fstype = TAKE_PTR(t),
|
||||
.node = TAKE_PTR(n),
|
||||
.mount_options = TAKE_PTR(o),
|
||||
};
|
||||
|
||||
m->encrypted = streq_ptr(fstype, "crypto_LUKS");
|
||||
@ -691,7 +700,8 @@ int dissect_image(
|
||||
}
|
||||
|
||||
if (designator != _PARTITION_DESIGNATOR_INVALID) {
|
||||
_cleanup_free_ char *t = NULL, *n = NULL;
|
||||
_cleanup_free_ char *t = NULL, *n = NULL, *o = NULL;
|
||||
const char *options = NULL;
|
||||
|
||||
/* First one wins */
|
||||
if (m->partitions[designator].found)
|
||||
@ -707,6 +717,13 @@ int dissect_image(
|
||||
if (!n)
|
||||
return -ENOMEM;
|
||||
|
||||
options = mount_options_from_part(mount_options, nr);
|
||||
if (options) {
|
||||
o = strdup(options);
|
||||
if (!o)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
m->partitions[designator] = (DissectedPartition) {
|
||||
.found = true,
|
||||
.partno = nr,
|
||||
@ -715,6 +732,7 @@ int dissect_image(
|
||||
.node = TAKE_PTR(n),
|
||||
.fstype = TAKE_PTR(t),
|
||||
.uuid = id,
|
||||
.mount_options = TAKE_PTR(o),
|
||||
};
|
||||
}
|
||||
|
||||
@ -740,9 +758,9 @@ int dissect_image(
|
||||
break;
|
||||
|
||||
case 0xEA: { /* Boot Loader Spec extended $BOOT partition */
|
||||
_cleanup_free_ char *n = NULL;
|
||||
_cleanup_free_ char *n = NULL, *o = NULL;
|
||||
sd_id128_t id = SD_ID128_NULL;
|
||||
const char *sid;
|
||||
const char *sid, *options = NULL;
|
||||
|
||||
/* First one wins */
|
||||
if (m->partitions[PARTITION_XBOOTLDR].found)
|
||||
@ -756,6 +774,13 @@ int dissect_image(
|
||||
if (!n)
|
||||
return -ENOMEM;
|
||||
|
||||
options = mount_options_from_part(mount_options, nr);
|
||||
if (options) {
|
||||
o = strdup(options);
|
||||
if (!o)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
m->partitions[PARTITION_XBOOTLDR] = (DissectedPartition) {
|
||||
.found = true,
|
||||
.partno = nr,
|
||||
@ -763,6 +788,7 @@ int dissect_image(
|
||||
.architecture = _ARCHITECTURE_INVALID,
|
||||
.node = TAKE_PTR(n),
|
||||
.uuid = id,
|
||||
.mount_options = TAKE_PTR(o),
|
||||
};
|
||||
|
||||
break;
|
||||
@ -785,6 +811,8 @@ int dissect_image(
|
||||
zero(m->partitions[PARTITION_ROOT_SECONDARY_VERITY]);
|
||||
|
||||
} else if (flags & DISSECT_IMAGE_REQUIRE_ROOT) {
|
||||
_cleanup_free_ char *o = NULL;
|
||||
const char *options = NULL;
|
||||
|
||||
/* If the root has was set, then we won't fallback to a generic node, because the root hash
|
||||
* decides */
|
||||
@ -800,6 +828,13 @@ int dissect_image(
|
||||
if (multiple_generic)
|
||||
return -ENOTUNIQ;
|
||||
|
||||
options = mount_options_from_part(mount_options, generic_nr);
|
||||
if (options) {
|
||||
o = strdup(options);
|
||||
if (!o)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
m->partitions[PARTITION_ROOT] = (DissectedPartition) {
|
||||
.found = true,
|
||||
.rw = generic_rw,
|
||||
@ -807,6 +842,7 @@ int dissect_image(
|
||||
.architecture = _ARCHITECTURE_INVALID,
|
||||
.node = TAKE_PTR(generic_node),
|
||||
.uuid = generic_uuid,
|
||||
.mount_options = TAKE_PTR(o),
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -869,6 +905,7 @@ DissectedImage* dissected_image_unref(DissectedImage *m) {
|
||||
free(m->partitions[i].node);
|
||||
free(m->partitions[i].decrypted_fstype);
|
||||
free(m->partitions[i].decrypted_node);
|
||||
free(m->partitions[i].mount_options);
|
||||
}
|
||||
|
||||
free(m->hostname);
|
||||
@ -1008,6 +1045,10 @@ static int mount_partition(
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (!isempty(m->mount_options))
|
||||
if (!strextend_with_separator(&options, ",", m->mount_options, NULL))
|
||||
return -ENOMEM;
|
||||
|
||||
r = mount_verbose(LOG_DEBUG, node, p, fstype, MS_NODEV|(rw ? 0 : MS_RDONLY), options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -1819,6 +1860,7 @@ int dissect_image_and_warn(
|
||||
const void *root_hash,
|
||||
size_t root_hash_size,
|
||||
const char *verity_data,
|
||||
const MountOptions *mount_options,
|
||||
DissectImageFlags flags,
|
||||
DissectedImage **ret) {
|
||||
|
||||
@ -1833,7 +1875,7 @@ int dissect_image_and_warn(
|
||||
name = buffer;
|
||||
}
|
||||
|
||||
r = dissect_image(fd, root_hash, root_hash_size, verity_data, flags, ret);
|
||||
r = dissect_image(fd, root_hash, root_hash_size, verity_data, mount_options, flags, ret);
|
||||
|
||||
switch (r) {
|
||||
|
||||
@ -1880,6 +1922,27 @@ bool dissected_image_has_verity(const DissectedImage *image, unsigned partition_
|
||||
return k >= 0 && image->partitions[k].found;
|
||||
}
|
||||
|
||||
MountOptions* mount_options_free_all(MountOptions *options) {
|
||||
MountOptions *m;
|
||||
|
||||
while ((m = options)) {
|
||||
LIST_REMOVE(mount_options, options, m);
|
||||
free(m->options);
|
||||
free(m);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* mount_options_from_part(const MountOptions *options, unsigned int partition_number) {
|
||||
MountOptions *m;
|
||||
|
||||
LIST_FOREACH(mount_options, m, (MountOptions *)options)
|
||||
if (partition_number == m->partition_number && !isempty(m->options))
|
||||
return m->options;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *const partition_designator_table[] = {
|
||||
[PARTITION_ROOT] = "root",
|
||||
[PARTITION_ROOT_SECONDARY] = "root-secondary",
|
||||
|
@ -5,11 +5,13 @@
|
||||
|
||||
#include "sd-id128.h"
|
||||
|
||||
#include "list.h"
|
||||
#include "macro.h"
|
||||
|
||||
typedef struct DissectedImage DissectedImage;
|
||||
typedef struct DissectedPartition DissectedPartition;
|
||||
typedef struct DecryptedImage DecryptedImage;
|
||||
typedef struct MountOptions MountOptions;
|
||||
|
||||
struct DissectedPartition {
|
||||
bool found:1;
|
||||
@ -21,6 +23,7 @@ struct DissectedPartition {
|
||||
char *node;
|
||||
char *decrypted_node;
|
||||
char *decrypted_fstype;
|
||||
char *mount_options;
|
||||
};
|
||||
|
||||
enum {
|
||||
@ -81,9 +84,19 @@ struct DissectedImage {
|
||||
char **os_release;
|
||||
};
|
||||
|
||||
struct MountOptions {
|
||||
unsigned partition_number;
|
||||
char *options;
|
||||
LIST_FIELDS(MountOptions, mount_options);
|
||||
};
|
||||
|
||||
MountOptions* mount_options_free_all(MountOptions *options);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(MountOptions*, mount_options_free_all);
|
||||
const char* mount_options_from_part(const MountOptions *options, unsigned int partition_number);
|
||||
|
||||
int probe_filesystem(const char *node, char **ret_fstype);
|
||||
int dissect_image(int fd, const void *root_hash, size_t root_hash_size, const char *verity_data, DissectImageFlags flags, DissectedImage **ret);
|
||||
int dissect_image_and_warn(int fd, const char *name, const void *root_hash, size_t root_hash_size, const char *verity_data, DissectImageFlags flags, DissectedImage **ret);
|
||||
int dissect_image(int fd, const void *root_hash, size_t root_hash_size, const char *verity_data, const MountOptions *mount_options, DissectImageFlags flags, DissectedImage **ret);
|
||||
int dissect_image_and_warn(int fd, const char *name, const void *root_hash, size_t root_hash_size, const char *verity_data, const MountOptions *mount_options, DissectImageFlags flags, DissectedImage **ret);
|
||||
|
||||
DissectedImage* dissected_image_unref(DissectedImage *m);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DissectedImage*, dissected_image_unref);
|
||||
|
@ -1171,7 +1171,7 @@ int image_read_metadata(Image *i) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dissect_image(d->fd, NULL, 0, NULL, DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_RELAX_VAR_CHECK, &m);
|
||||
r = dissect_image(d->fd, NULL, 0, NULL, NULL, DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_RELAX_VAR_CHECK, &m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -28,7 +28,7 @@ int main(int argc, char *argv[]) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
r = dissect_image(d->fd, NULL, 0, NULL, DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_RELAX_VAR_CHECK, &m);
|
||||
r = dissect_image(d->fd, NULL, 0, NULL, NULL, DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_RELAX_VAR_CHECK, &m);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to dissect image: %m");
|
||||
return EXIT_FAILURE;
|
||||
|
@ -12,11 +12,14 @@ int main(int argc, const char *argv[]) {
|
||||
LIST_FIELDS(struct list_item, item);
|
||||
} list_item;
|
||||
LIST_HEAD(list_item, head);
|
||||
LIST_HEAD(list_item, head2);
|
||||
list_item items[4];
|
||||
list_item *cursor;
|
||||
|
||||
LIST_HEAD_INIT(head);
|
||||
LIST_HEAD_INIT(head2);
|
||||
assert_se(head == NULL);
|
||||
assert_se(head2 == NULL);
|
||||
|
||||
for (i = 0; i < ELEMENTSOF(items); i++) {
|
||||
LIST_INIT(item, &items[i]);
|
||||
@ -203,5 +206,49 @@ int main(int argc, const char *argv[]) {
|
||||
|
||||
assert_se(head == NULL);
|
||||
|
||||
for (i = 0; i < ELEMENTSOF(items) / 2; i++) {
|
||||
LIST_INIT(item, &items[i]);
|
||||
assert_se(LIST_JUST_US(item, &items[i]));
|
||||
LIST_PREPEND(item, head, &items[i]);
|
||||
}
|
||||
|
||||
for (i = ELEMENTSOF(items) / 2; i < ELEMENTSOF(items); i++) {
|
||||
LIST_INIT(item, &items[i]);
|
||||
assert_se(LIST_JUST_US(item, &items[i]));
|
||||
LIST_PREPEND(item, head2, &items[i]);
|
||||
}
|
||||
|
||||
assert_se(items[0].item_next == NULL);
|
||||
assert_se(items[1].item_next == &items[0]);
|
||||
assert_se(items[2].item_next == NULL);
|
||||
assert_se(items[3].item_next == &items[2]);
|
||||
|
||||
assert_se(items[0].item_prev == &items[1]);
|
||||
assert_se(items[1].item_prev == NULL);
|
||||
assert_se(items[2].item_prev == &items[3]);
|
||||
assert_se(items[3].item_prev == NULL);
|
||||
|
||||
LIST_JOIN(item, head2, head);
|
||||
assert_se(head == NULL);
|
||||
|
||||
assert_se(items[0].item_next == NULL);
|
||||
assert_se(items[1].item_next == &items[0]);
|
||||
assert_se(items[2].item_next == &items[1]);
|
||||
assert_se(items[3].item_next == &items[2]);
|
||||
|
||||
assert_se(items[0].item_prev == &items[1]);
|
||||
assert_se(items[1].item_prev == &items[2]);
|
||||
assert_se(items[2].item_prev == &items[3]);
|
||||
assert_se(items[3].item_prev == NULL);
|
||||
|
||||
LIST_JOIN(item, head, head2);
|
||||
assert_se(head2 == NULL);
|
||||
assert_se(!LIST_IS_EMPTY(head));
|
||||
|
||||
for (i = 0; i < ELEMENTSOF(items); i++)
|
||||
LIST_REMOVE(item, head, &items[i]);
|
||||
|
||||
assert_se(head == NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -150,6 +150,7 @@ static void test_protect_kernel_logs(void) {
|
||||
assert_se(fd > 0);
|
||||
|
||||
r = setup_namespace(NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
&ns_info,
|
||||
NULL,
|
||||
|
@ -62,6 +62,7 @@ int main(int argc, char *argv[]) {
|
||||
log_info("Not chrooted");
|
||||
|
||||
r = setup_namespace(root_directory,
|
||||
NULL,
|
||||
NULL,
|
||||
&ns_info,
|
||||
(char **) writable,
|
||||
|
@ -34,6 +34,7 @@ test_create_image() {
|
||||
BASICTOOLS=(
|
||||
bash
|
||||
cat
|
||||
mount
|
||||
)
|
||||
oldinitdir=$initdir
|
||||
export initdir=$TESTDIR/minimal
|
||||
|
@ -124,6 +124,37 @@ umount ${image_dir}/mount
|
||||
|
||||
systemd-run -t --property RootImage=${image}.gpt --property RootHash=${roothash} /usr/bin/cat /usr/lib/os-release | grep -q -F "MARKER=1"
|
||||
|
||||
systemd-run -t --property RootImage=${image}.raw --property RootImageOptions="1:ro,noatime 2:ro,dev nosuid,dev" --property MountAPIVFS=yes mount | grep -F "squashfs" | grep -q -F "nosuid"
|
||||
systemd-run -t --property RootImage=${image}.gpt --property RootImageOptions="1:ro,noatime 1:ro,dev" --property MountAPIVFS=yes mount | grep -F "squashfs" | grep -q -F "noatime"
|
||||
|
||||
cat > /run/systemd/system/testservice-50a.service <<EOF
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=mount
|
||||
MountAPIVFS=yes
|
||||
RootImage=${image}.raw
|
||||
RootImageOptions=1:ro,noatime,nosuid 2:ro,dev noatime,dev
|
||||
RootImageOptions=nosuid,dev
|
||||
EOF
|
||||
systemctl start testservice-50a.service
|
||||
journalctl -b -u testservice-50a.service | grep -F "squashfs" | grep -q -F "noatime"
|
||||
journalctl -b -u testservice-50a.service | grep -F "squashfs" | grep -q -F -v "nosuid"
|
||||
|
||||
cat > /run/systemd/system/testservice-50b.service <<EOF
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=mount
|
||||
MountAPIVFS=yes
|
||||
RootImage=${image}.gpt
|
||||
RootImageOptions=1:ro,noatime,nosuid 2:ro,dev nosuid,dev
|
||||
RootImageOptions=2:ro,dev nosuid,dev,%%foo
|
||||
EOF
|
||||
systemctl start testservice-50b.service
|
||||
journalctl -b -u testservice-50b.service | grep -F "squashfs" | grep -q -F "noatime"
|
||||
|
||||
# Check that specifier escape is applied %%foo -> %foo
|
||||
busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1/unit/testservice_2d50b_2eservice org.freedesktop.systemd1.Service RootImageOptions | grep -F "nosuid,dev,%foo"
|
||||
|
||||
echo OK > /testok
|
||||
|
||||
exit 0
|
||||
|
Loading…
x
Reference in New Issue
Block a user