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>
|
<xi:include href="system-only.xml" xpointer="singular"/></listitem>
|
||||||
</varlistentry>
|
</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>
|
<varlistentry>
|
||||||
<term><varname>RootHash=</varname></term>
|
<term><varname>RootHash=</varname></term>
|
||||||
|
|
||||||
|
|
|
@ -169,3 +169,18 @@
|
||||||
|
|
||||||
#define LIST_IS_EMPTY(head) \
|
#define LIST_IS_EMPTY(head) \
|
||||||
(!(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);
|
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[] = {
|
const sd_bus_vtable bus_exec_vtable[] = {
|
||||||
SD_BUS_VTABLE_START(0),
|
SD_BUS_VTABLE_START(0),
|
||||||
SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(ExecContext, environment), SD_BUS_VTABLE_PROPERTY_CONST),
|
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("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("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("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("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("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),
|
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"))
|
if (streq(name, "RootImage"))
|
||||||
return bus_set_transient_path(u, name, &c->root_image, message, flags, error);
|
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")) {
|
if (streq(name, "RootHash")) {
|
||||||
const void *roothash_decoded;
|
const void *roothash_decoded;
|
||||||
size_t roothash_decoded_size;
|
size_t roothash_decoded_size;
|
||||||
|
|
|
@ -2660,7 +2660,7 @@ static int apply_mount_namespace(
|
||||||
if (context->mount_flags == MS_SHARED)
|
if (context->mount_flags == MS_SHARED)
|
||||||
log_unit_debug(u, "shared mount propagation hidden by other fs namespacing unit settings: ignoring");
|
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,
|
&ns_info, context->read_write_paths,
|
||||||
needs_sandboxing ? context->read_only_paths : NULL,
|
needs_sandboxing ? context->read_only_paths : NULL,
|
||||||
needs_sandboxing ? context->inaccessible_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->working_directory = mfree(c->working_directory);
|
||||||
c->root_directory = mfree(c->root_directory);
|
c->root_directory = mfree(c->root_directory);
|
||||||
c->root_image = mfree(c->root_image);
|
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 = mfree(c->root_hash);
|
||||||
c->root_hash_size = 0;
|
c->root_hash_size = 0;
|
||||||
c->root_hash_path = mfree(c->root_hash_path);
|
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)
|
if (c->root_image)
|
||||||
fprintf(f, "%sRootImage: %s\n", prefix, 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) {
|
if (c->root_hash) {
|
||||||
_cleanup_free_ char *encoded = NULL;
|
_cleanup_free_ char *encoded = NULL;
|
||||||
encoded = hexmem(c->root_hash, c->root_hash_size);
|
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;
|
char *working_directory, *root_directory, *root_image, *root_verity, *root_hash_path, *root_hash_sig_path;
|
||||||
void *root_hash, *root_hash_sig;
|
void *root_hash, *root_hash_sig;
|
||||||
size_t root_hash_size, root_hash_sig_size;
|
size_t root_hash_size, root_hash_sig_size;
|
||||||
|
LIST_HEAD(MountOptions, root_image_options);
|
||||||
bool working_directory_missing_ok:1;
|
bool working_directory_missing_ok:1;
|
||||||
bool working_directory_home: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.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.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.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.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.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)
|
$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;
|
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(
|
int config_parse_exec_root_hash(
|
||||||
const char *unit,
|
const char *unit,
|
||||||
const char *filename,
|
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_sched_prio);
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_exec_cpu_affinity);
|
CONFIG_PARSER_PROTOTYPE(config_parse_exec_cpu_affinity);
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_exec_secure_bits);
|
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);
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_exec_root_hash_sig);
|
CONFIG_PARSER_PROTOTYPE(config_parse_exec_root_hash_sig);
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_capability_set);
|
CONFIG_PARSER_PROTOTYPE(config_parse_capability_set);
|
||||||
|
|
|
@ -1257,6 +1257,7 @@ static bool home_read_only(
|
||||||
int setup_namespace(
|
int setup_namespace(
|
||||||
const char* root_directory,
|
const char* root_directory,
|
||||||
const char* root_image,
|
const char* root_image,
|
||||||
|
const MountOptions *root_image_options,
|
||||||
const NamespaceInfo *ns_info,
|
const NamespaceInfo *ns_info,
|
||||||
char** read_write_paths,
|
char** read_write_paths,
|
||||||
char** read_only_paths,
|
char** read_only_paths,
|
||||||
|
@ -1331,6 +1332,7 @@ int setup_namespace(
|
||||||
root_hash ?: root_hash_decoded,
|
root_hash ?: root_hash_decoded,
|
||||||
root_hash_size,
|
root_hash_size,
|
||||||
root_verity ?: verity_data,
|
root_verity ?: verity_data,
|
||||||
|
root_image_options,
|
||||||
dissect_image_flags,
|
dissect_image_flags,
|
||||||
&dissected_image);
|
&dissected_image);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
|
|
@ -75,6 +75,7 @@ struct TemporaryFileSystem {
|
||||||
int setup_namespace(
|
int setup_namespace(
|
||||||
const char *root_directory,
|
const char *root_directory,
|
||||||
const char *root_image,
|
const char *root_image,
|
||||||
|
const MountOptions *root_image_options,
|
||||||
const NamespaceInfo *ns_info,
|
const NamespaceInfo *ns_info,
|
||||||
char **read_write_paths,
|
char **read_write_paths,
|
||||||
char **read_only_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);
|
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;
|
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)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
|
|
@ -933,7 +933,7 @@ static int setup_image(char **ret_mount_dir, LoopDevice **ret_loop_device, Decry
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to set up loopback device: %m");
|
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)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
|
|
@ -665,7 +665,7 @@ static int enumerate_partitions(dev_t devnum) {
|
||||||
if (r <= 0)
|
if (r <= 0)
|
||||||
return r;
|
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) {
|
if (r == -ENOPKG) {
|
||||||
log_debug_errno(r, "No suitable partition table found, ignoring.");
|
log_debug_errno(r, "No suitable partition table found, ignoring.");
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -5223,6 +5223,7 @@ static int run(int argc, char *argv[]) {
|
||||||
arg_image,
|
arg_image,
|
||||||
arg_root_hash, arg_root_hash_size,
|
arg_root_hash, arg_root_hash_size,
|
||||||
arg_verity_data,
|
arg_verity_data,
|
||||||
|
NULL,
|
||||||
dissect_image_flags,
|
dissect_image_flags,
|
||||||
&dissected_image);
|
&dissected_image);
|
||||||
if (r == -ENOPKG) {
|
if (r == -ENOPKG) {
|
||||||
|
|
|
@ -380,7 +380,7 @@ static int portable_extract_by_path(
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_debug_errno(r, "Failed to create temporary directory: %m");
|
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)
|
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);
|
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)
|
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);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -308,6 +308,7 @@ int dissect_image(
|
||||||
const void *root_hash,
|
const void *root_hash,
|
||||||
size_t root_hash_size,
|
size_t root_hash_size,
|
||||||
const char *verity_data,
|
const char *verity_data,
|
||||||
|
const MountOptions *mount_options,
|
||||||
DissectImageFlags flags,
|
DissectImageFlags flags,
|
||||||
DissectedImage **ret) {
|
DissectedImage **ret) {
|
||||||
|
|
||||||
|
@ -400,8 +401,8 @@ int dissect_image(
|
||||||
|
|
||||||
(void) blkid_probe_lookup_value(b, "USAGE", &usage, NULL);
|
(void) blkid_probe_lookup_value(b, "USAGE", &usage, NULL);
|
||||||
if (STRPTR_IN_SET(usage, "filesystem", "crypto")) {
|
if (STRPTR_IN_SET(usage, "filesystem", "crypto")) {
|
||||||
_cleanup_free_ char *t = NULL, *n = NULL;
|
_cleanup_free_ char *t = NULL, *n = NULL, *o = NULL;
|
||||||
const char *fstype = NULL;
|
const char *fstype = NULL, *options = NULL;
|
||||||
|
|
||||||
/* OK, we have found a file system, that's our root partition then. */
|
/* OK, we have found a file system, that's our root partition then. */
|
||||||
(void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
|
(void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
|
||||||
|
@ -420,6 +421,13 @@ int dissect_image(
|
||||||
m->verity = root_hash && verity_data;
|
m->verity = root_hash && verity_data;
|
||||||
m->can_verity = !!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) {
|
m->partitions[PARTITION_ROOT] = (DissectedPartition) {
|
||||||
.found = true,
|
.found = true,
|
||||||
.rw = !m->verity,
|
.rw = !m->verity,
|
||||||
|
@ -427,6 +435,7 @@ int dissect_image(
|
||||||
.architecture = _ARCHITECTURE_INVALID,
|
.architecture = _ARCHITECTURE_INVALID,
|
||||||
.fstype = TAKE_PTR(t),
|
.fstype = TAKE_PTR(t),
|
||||||
.node = TAKE_PTR(n),
|
.node = TAKE_PTR(n),
|
||||||
|
.mount_options = TAKE_PTR(o),
|
||||||
};
|
};
|
||||||
|
|
||||||
m->encrypted = streq_ptr(fstype, "crypto_LUKS");
|
m->encrypted = streq_ptr(fstype, "crypto_LUKS");
|
||||||
|
@ -691,7 +700,8 @@ int dissect_image(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (designator != _PARTITION_DESIGNATOR_INVALID) {
|
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 */
|
/* First one wins */
|
||||||
if (m->partitions[designator].found)
|
if (m->partitions[designator].found)
|
||||||
|
@ -707,6 +717,13 @@ int dissect_image(
|
||||||
if (!n)
|
if (!n)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
options = mount_options_from_part(mount_options, nr);
|
||||||
|
if (options) {
|
||||||
|
o = strdup(options);
|
||||||
|
if (!o)
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
m->partitions[designator] = (DissectedPartition) {
|
m->partitions[designator] = (DissectedPartition) {
|
||||||
.found = true,
|
.found = true,
|
||||||
.partno = nr,
|
.partno = nr,
|
||||||
|
@ -715,6 +732,7 @@ int dissect_image(
|
||||||
.node = TAKE_PTR(n),
|
.node = TAKE_PTR(n),
|
||||||
.fstype = TAKE_PTR(t),
|
.fstype = TAKE_PTR(t),
|
||||||
.uuid = id,
|
.uuid = id,
|
||||||
|
.mount_options = TAKE_PTR(o),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -740,9 +758,9 @@ int dissect_image(
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xEA: { /* Boot Loader Spec extended $BOOT partition */
|
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;
|
sd_id128_t id = SD_ID128_NULL;
|
||||||
const char *sid;
|
const char *sid, *options = NULL;
|
||||||
|
|
||||||
/* First one wins */
|
/* First one wins */
|
||||||
if (m->partitions[PARTITION_XBOOTLDR].found)
|
if (m->partitions[PARTITION_XBOOTLDR].found)
|
||||||
|
@ -756,6 +774,13 @@ int dissect_image(
|
||||||
if (!n)
|
if (!n)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
options = mount_options_from_part(mount_options, nr);
|
||||||
|
if (options) {
|
||||||
|
o = strdup(options);
|
||||||
|
if (!o)
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
m->partitions[PARTITION_XBOOTLDR] = (DissectedPartition) {
|
m->partitions[PARTITION_XBOOTLDR] = (DissectedPartition) {
|
||||||
.found = true,
|
.found = true,
|
||||||
.partno = nr,
|
.partno = nr,
|
||||||
|
@ -763,6 +788,7 @@ int dissect_image(
|
||||||
.architecture = _ARCHITECTURE_INVALID,
|
.architecture = _ARCHITECTURE_INVALID,
|
||||||
.node = TAKE_PTR(n),
|
.node = TAKE_PTR(n),
|
||||||
.uuid = id,
|
.uuid = id,
|
||||||
|
.mount_options = TAKE_PTR(o),
|
||||||
};
|
};
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -785,6 +811,8 @@ int dissect_image(
|
||||||
zero(m->partitions[PARTITION_ROOT_SECONDARY_VERITY]);
|
zero(m->partitions[PARTITION_ROOT_SECONDARY_VERITY]);
|
||||||
|
|
||||||
} else if (flags & DISSECT_IMAGE_REQUIRE_ROOT) {
|
} 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
|
/* If the root has was set, then we won't fallback to a generic node, because the root hash
|
||||||
* decides */
|
* decides */
|
||||||
|
@ -800,6 +828,13 @@ int dissect_image(
|
||||||
if (multiple_generic)
|
if (multiple_generic)
|
||||||
return -ENOTUNIQ;
|
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) {
|
m->partitions[PARTITION_ROOT] = (DissectedPartition) {
|
||||||
.found = true,
|
.found = true,
|
||||||
.rw = generic_rw,
|
.rw = generic_rw,
|
||||||
|
@ -807,6 +842,7 @@ int dissect_image(
|
||||||
.architecture = _ARCHITECTURE_INVALID,
|
.architecture = _ARCHITECTURE_INVALID,
|
||||||
.node = TAKE_PTR(generic_node),
|
.node = TAKE_PTR(generic_node),
|
||||||
.uuid = generic_uuid,
|
.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].node);
|
||||||
free(m->partitions[i].decrypted_fstype);
|
free(m->partitions[i].decrypted_fstype);
|
||||||
free(m->partitions[i].decrypted_node);
|
free(m->partitions[i].decrypted_node);
|
||||||
|
free(m->partitions[i].mount_options);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(m->hostname);
|
free(m->hostname);
|
||||||
|
@ -1008,6 +1045,10 @@ static int mount_partition(
|
||||||
return -ENOMEM;
|
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);
|
r = mount_verbose(LOG_DEBUG, node, p, fstype, MS_NODEV|(rw ? 0 : MS_RDONLY), options);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
@ -1819,6 +1860,7 @@ int dissect_image_and_warn(
|
||||||
const void *root_hash,
|
const void *root_hash,
|
||||||
size_t root_hash_size,
|
size_t root_hash_size,
|
||||||
const char *verity_data,
|
const char *verity_data,
|
||||||
|
const MountOptions *mount_options,
|
||||||
DissectImageFlags flags,
|
DissectImageFlags flags,
|
||||||
DissectedImage **ret) {
|
DissectedImage **ret) {
|
||||||
|
|
||||||
|
@ -1833,7 +1875,7 @@ int dissect_image_and_warn(
|
||||||
name = buffer;
|
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) {
|
switch (r) {
|
||||||
|
|
||||||
|
@ -1880,6 +1922,27 @@ bool dissected_image_has_verity(const DissectedImage *image, unsigned partition_
|
||||||
return k >= 0 && image->partitions[k].found;
|
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[] = {
|
static const char *const partition_designator_table[] = {
|
||||||
[PARTITION_ROOT] = "root",
|
[PARTITION_ROOT] = "root",
|
||||||
[PARTITION_ROOT_SECONDARY] = "root-secondary",
|
[PARTITION_ROOT_SECONDARY] = "root-secondary",
|
||||||
|
|
|
@ -5,11 +5,13 @@
|
||||||
|
|
||||||
#include "sd-id128.h"
|
#include "sd-id128.h"
|
||||||
|
|
||||||
|
#include "list.h"
|
||||||
#include "macro.h"
|
#include "macro.h"
|
||||||
|
|
||||||
typedef struct DissectedImage DissectedImage;
|
typedef struct DissectedImage DissectedImage;
|
||||||
typedef struct DissectedPartition DissectedPartition;
|
typedef struct DissectedPartition DissectedPartition;
|
||||||
typedef struct DecryptedImage DecryptedImage;
|
typedef struct DecryptedImage DecryptedImage;
|
||||||
|
typedef struct MountOptions MountOptions;
|
||||||
|
|
||||||
struct DissectedPartition {
|
struct DissectedPartition {
|
||||||
bool found:1;
|
bool found:1;
|
||||||
|
@ -21,6 +23,7 @@ struct DissectedPartition {
|
||||||
char *node;
|
char *node;
|
||||||
char *decrypted_node;
|
char *decrypted_node;
|
||||||
char *decrypted_fstype;
|
char *decrypted_fstype;
|
||||||
|
char *mount_options;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
@ -81,9 +84,19 @@ struct DissectedImage {
|
||||||
char **os_release;
|
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 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(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, 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);
|
DissectedImage* dissected_image_unref(DissectedImage *m);
|
||||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DissectedImage*, dissected_image_unref);
|
DEFINE_TRIVIAL_CLEANUP_FUNC(DissectedImage*, dissected_image_unref);
|
||||||
|
|
|
@ -1171,7 +1171,7 @@ int image_read_metadata(Image *i) {
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
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)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ int main(int argc, char *argv[]) {
|
||||||
return EXIT_FAILURE;
|
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) {
|
if (r < 0) {
|
||||||
log_error_errno(r, "Failed to dissect image: %m");
|
log_error_errno(r, "Failed to dissect image: %m");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
|
|
|
@ -12,11 +12,14 @@ int main(int argc, const char *argv[]) {
|
||||||
LIST_FIELDS(struct list_item, item);
|
LIST_FIELDS(struct list_item, item);
|
||||||
} list_item;
|
} list_item;
|
||||||
LIST_HEAD(list_item, head);
|
LIST_HEAD(list_item, head);
|
||||||
|
LIST_HEAD(list_item, head2);
|
||||||
list_item items[4];
|
list_item items[4];
|
||||||
list_item *cursor;
|
list_item *cursor;
|
||||||
|
|
||||||
LIST_HEAD_INIT(head);
|
LIST_HEAD_INIT(head);
|
||||||
|
LIST_HEAD_INIT(head2);
|
||||||
assert_se(head == NULL);
|
assert_se(head == NULL);
|
||||||
|
assert_se(head2 == NULL);
|
||||||
|
|
||||||
for (i = 0; i < ELEMENTSOF(items); i++) {
|
for (i = 0; i < ELEMENTSOF(items); i++) {
|
||||||
LIST_INIT(item, &items[i]);
|
LIST_INIT(item, &items[i]);
|
||||||
|
@ -203,5 +206,49 @@ int main(int argc, const char *argv[]) {
|
||||||
|
|
||||||
assert_se(head == NULL);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,6 +150,7 @@ static void test_protect_kernel_logs(void) {
|
||||||
assert_se(fd > 0);
|
assert_se(fd > 0);
|
||||||
|
|
||||||
r = setup_namespace(NULL,
|
r = setup_namespace(NULL,
|
||||||
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
&ns_info,
|
&ns_info,
|
||||||
NULL,
|
NULL,
|
||||||
|
|
|
@ -62,6 +62,7 @@ int main(int argc, char *argv[]) {
|
||||||
log_info("Not chrooted");
|
log_info("Not chrooted");
|
||||||
|
|
||||||
r = setup_namespace(root_directory,
|
r = setup_namespace(root_directory,
|
||||||
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
&ns_info,
|
&ns_info,
|
||||||
(char **) writable,
|
(char **) writable,
|
||||||
|
|
|
@ -34,6 +34,7 @@ test_create_image() {
|
||||||
BASICTOOLS=(
|
BASICTOOLS=(
|
||||||
bash
|
bash
|
||||||
cat
|
cat
|
||||||
|
mount
|
||||||
)
|
)
|
||||||
oldinitdir=$initdir
|
oldinitdir=$initdir
|
||||||
export initdir=$TESTDIR/minimal
|
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}.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
|
echo OK > /testok
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
|
Loading…
Reference in New Issue