mirror of
https://github.com/systemd/systemd
synced 2025-11-09 20:04:46 +01:00
Compare commits
13 Commits
825cba0d43
...
18dbc899ff
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
18dbc899ff | ||
|
|
7e62257219 | ||
|
|
09364a8043 | ||
|
|
653ca0d913 | ||
|
|
1cdbff1c84 | ||
|
|
6f646e0175 | ||
|
|
e4ec8b1f51 | ||
|
|
2d2d0a57e7 | ||
|
|
a015fbe7ef | ||
|
|
18d7370587 | ||
|
|
5511d8c1b9 | ||
|
|
b89f5f7b03 | ||
|
|
78490efca3 |
@ -265,6 +265,15 @@
|
||||
and graphic illustrating the changes applied.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--json=</option><replaceable>MODE</replaceable></term>
|
||||
|
||||
<listitem><para>Shows output formatted as JSON. Expects one of <literal>short</literal> (for the
|
||||
shortest possible output without any redundant whitespace or line breaks), <literal>pretty</literal>
|
||||
(for a pretty version of the same, with indentation and line breaks) or <literal>off</literal> (to turn
|
||||
off json output).</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--definitions=</option></term>
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
|
||||
@ -2668,6 +2668,7 @@ if conf.get('ENABLE_REPART') == 1
|
||||
install_rpath : rootlibexecdir,
|
||||
install : true,
|
||||
install_dir : rootbindir)
|
||||
public_programs += exe
|
||||
|
||||
if want_tests != 'false'
|
||||
test('test-repart',
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -18,8 +18,9 @@ fi
|
||||
|
||||
MACHINE_ID=$KERNEL_INSTALL_MACHINE_ID
|
||||
|
||||
ENTRY_DIR="/$MACHINE_ID/$KERNEL_VERSION"
|
||||
BOOT_ROOT=${ENTRY_DIR_ABS%$ENTRY_DIR}
|
||||
BOOT_ROOT=${ENTRY_DIR_ABS%/$MACHINE_ID/$KERNEL_VERSION}
|
||||
BOOT_MNT=$(stat -c %m $BOOT_ROOT)
|
||||
ENTRY_DIR=/${ENTRY_DIR_ABS#$BOOT_MNT}
|
||||
|
||||
if [[ $COMMAND == remove ]]; then
|
||||
rm -f "$BOOT_ROOT/loader/entries/$MACHINE_ID-$KERNEL_VERSION.conf"
|
||||
|
||||
@ -585,7 +585,7 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
|
||||
r = sd_bus_message_read(message, "ss", &user, &path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
user = empty_to_null(user);
|
||||
user = isempty(user) ? "root" : user;
|
||||
r = sd_bus_message_read_strv(message, &args_wire);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -604,7 +604,7 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
|
||||
r = asprintf(&args[2],
|
||||
"shell=$(getent passwd %s 2>/dev/null | { IFS=: read _ _ _ _ _ _ x; echo \"$x\"; })\n"\
|
||||
"exec \"${shell:-/bin/sh}\" -l", /* -l is means --login */
|
||||
isempty(user) ? "root" : user);
|
||||
user);
|
||||
if (r < 0) {
|
||||
args[2] = NULL;
|
||||
return -ENOMEM;
|
||||
@ -628,11 +628,18 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
|
||||
if (!strv_env_is_valid(env))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment assignments");
|
||||
|
||||
const char *details[] = {
|
||||
"machine", m->name,
|
||||
"user", user,
|
||||
"program", path,
|
||||
NULL
|
||||
};
|
||||
|
||||
r = bus_verify_polkit_async(
|
||||
message,
|
||||
CAP_SYS_ADMIN,
|
||||
m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-shell" : "org.freedesktop.machine1.shell",
|
||||
NULL,
|
||||
details,
|
||||
false,
|
||||
UID_INVALID,
|
||||
&m->manager->polkit_registry,
|
||||
@ -677,7 +684,7 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
description = strjoina("Shell for User ", isempty(user) ? "root" : user);
|
||||
description = strjoina("Shell for User ", user);
|
||||
r = sd_bus_message_append(tm,
|
||||
"(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)",
|
||||
"Description", "s", description,
|
||||
@ -695,7 +702,7 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_append(tm, "(sv)", "User", "s", isempty(user) ? "root" : user);
|
||||
r = sd_bus_message_append(tm, "(sv)", "User", "s", user);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
@ -3259,7 +3259,7 @@ static int inner_child(
|
||||
* binary. */
|
||||
dollar_path = strv_env_get(env_use, "PATH");
|
||||
if (dollar_path) {
|
||||
if (putenv((char*) dollar_path) != 0)
|
||||
if (setenv("PATH", dollar_path, 1) < 0)
|
||||
return log_error_errno(errno, "Failed to update $PATH: %m");
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -33,6 +33,7 @@
|
||||
#include "fs-util.h"
|
||||
#include "gpt.h"
|
||||
#include "id128-util.h"
|
||||
#include "json.h"
|
||||
#include "list.h"
|
||||
#include "locale-util.h"
|
||||
#include "main-func.h"
|
||||
@ -79,6 +80,8 @@ static sd_id128_t arg_seed = SD_ID128_NULL;
|
||||
static bool arg_randomize = false;
|
||||
static int arg_pretty = -1;
|
||||
static uint64_t arg_size = UINT64_MAX;
|
||||
static bool arg_json = false;
|
||||
static JsonFormatFlags arg_json_format_flags = 0;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_definitions, freep);
|
||||
@ -1620,12 +1623,23 @@ static int context_dump_partitions(Context *context, const char *node) {
|
||||
Partition *p;
|
||||
int r;
|
||||
|
||||
t = table_new("type", "label", "uuid", "file", "node", "offset", "raw size", "size", "raw padding", "padding");
|
||||
if (!arg_json && context->n_partitions == 0) {
|
||||
log_info("Empty partition table.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
t = table_new("type", "label", "uuid", "file", "node", "offset", "old size", "raw size", "size", "old padding", "raw padding", "padding", "activity");
|
||||
if (!t)
|
||||
return log_oom();
|
||||
|
||||
if (!DEBUG_LOGGING)
|
||||
(void) table_set_display(t, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4, (size_t) 7, (size_t) 9, (size_t) -1);
|
||||
if (!DEBUG_LOGGING) {
|
||||
if (arg_json)
|
||||
(void) table_set_display(t, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4,
|
||||
(size_t) 5, (size_t) 6, (size_t) 7, (size_t) 9, (size_t) 10, (size_t) 12, (size_t) -1);
|
||||
else
|
||||
(void) table_set_display(t, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4,
|
||||
(size_t) 8, (size_t) 11, (size_t) -1);
|
||||
}
|
||||
|
||||
(void) table_set_align_percent(t, table_get_cell(t, 0, 4), 100);
|
||||
(void) table_set_align_percent(t, table_get_cell(t, 0, 5), 100);
|
||||
@ -1633,11 +1647,16 @@ static int context_dump_partitions(Context *context, const char *node) {
|
||||
LIST_FOREACH(partitions, p, context->partitions) {
|
||||
_cleanup_free_ char *size_change = NULL, *padding_change = NULL, *partname = NULL;
|
||||
char uuid_buffer[ID128_UUID_STRING_MAX];
|
||||
const char *label;
|
||||
const char *label, *activity = NULL;
|
||||
|
||||
if (p->dropped)
|
||||
continue;
|
||||
|
||||
if (p->current_size == UINT64_MAX)
|
||||
activity = "create";
|
||||
else if (p->current_size != p->new_size)
|
||||
activity = "resize";
|
||||
|
||||
label = partition_label(p);
|
||||
partname = p->partno != UINT64_MAX ? fdisk_partname(node, p->partno+1) : NULL;
|
||||
|
||||
@ -1660,17 +1679,20 @@ static int context_dump_partitions(Context *context, const char *node) {
|
||||
TABLE_STRING, label ?: "-", TABLE_SET_COLOR, label ? NULL : ansi_grey(),
|
||||
TABLE_UUID, sd_id128_is_null(p->new_uuid) ? p->current_uuid : p->new_uuid,
|
||||
TABLE_STRING, p->definition_path ? basename(p->definition_path) : "-", TABLE_SET_COLOR, p->definition_path ? NULL : ansi_grey(),
|
||||
TABLE_STRING, partname ?: "no", TABLE_SET_COLOR, partname ? NULL : ansi_highlight(),
|
||||
TABLE_STRING, partname ?: "-", TABLE_SET_COLOR, partname ? NULL : ansi_highlight(),
|
||||
TABLE_UINT64, p->offset,
|
||||
TABLE_UINT64, p->current_size == UINT64_MAX ? 0 : p->current_size,
|
||||
TABLE_UINT64, p->new_size,
|
||||
TABLE_STRING, size_change, TABLE_SET_COLOR, !p->partitions_next && sum_size > 0 ? ansi_underline() : NULL,
|
||||
TABLE_UINT64, p->current_padding == UINT64_MAX ? 0 : p->current_padding,
|
||||
TABLE_UINT64, p->new_padding,
|
||||
TABLE_STRING, padding_change, TABLE_SET_COLOR, !p->partitions_next && sum_padding > 0 ? ansi_underline() : NULL);
|
||||
TABLE_STRING, padding_change, TABLE_SET_COLOR, !p->partitions_next && sum_padding > 0 ? ansi_underline() : NULL,
|
||||
TABLE_STRING, activity ?: "unknown");
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
}
|
||||
|
||||
if (sum_padding > 0 || sum_size > 0) {
|
||||
if (!arg_json && (sum_padding > 0 || sum_size > 0)) {
|
||||
char s[FORMAT_BYTES_MAX];
|
||||
const char *a, *b;
|
||||
|
||||
@ -1686,14 +1708,20 @@ static int context_dump_partitions(Context *context, const char *node) {
|
||||
TABLE_EMPTY,
|
||||
TABLE_EMPTY,
|
||||
TABLE_EMPTY,
|
||||
TABLE_EMPTY,
|
||||
TABLE_STRING, a,
|
||||
TABLE_EMPTY,
|
||||
TABLE_STRING, b);
|
||||
TABLE_EMPTY,
|
||||
TABLE_STRING, b,
|
||||
TABLE_EMPTY);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
}
|
||||
|
||||
r = table_print(t, stdout);
|
||||
if (arg_json)
|
||||
r = table_print_json(t, stdout, arg_json_format_flags);
|
||||
else
|
||||
r = table_print(t, stdout);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to dump table: %m");
|
||||
|
||||
@ -2399,16 +2427,14 @@ static int context_write_partition_table(
|
||||
assert(context);
|
||||
|
||||
if (arg_pretty > 0 ||
|
||||
(arg_pretty < 0 && isatty(STDOUT_FILENO) > 0)) {
|
||||
(arg_pretty < 0 && isatty(STDOUT_FILENO) > 0) || arg_json) {
|
||||
|
||||
if (context->n_partitions == 0)
|
||||
puts("Empty partition table.");
|
||||
else
|
||||
(void) context_dump_partitions(context, node);
|
||||
(void) context_dump_partitions(context, node);
|
||||
|
||||
putc('\n', stdout);
|
||||
|
||||
(void) context_dump_partition_bar(context, node);
|
||||
if (!arg_json)
|
||||
(void) context_dump_partition_bar(context, node);
|
||||
putc('\n', stdout);
|
||||
fflush(stdout);
|
||||
}
|
||||
@ -2791,7 +2817,7 @@ static int help(void) {
|
||||
" --empty=MODE One of refuse, allow, require, force, create; controls\n"
|
||||
" how to handle empty disks lacking partition tables\n"
|
||||
" --discard=BOOL Whether to discard backing blocks for new partitions\n"
|
||||
" --pretty=BOOL Whether to show pretty summary before executing operation\n"
|
||||
" --pretty=BOOL Whether to show pretty summary before doing changes\n"
|
||||
" --factory-reset=BOOL Whether to remove data partitions before recreating\n"
|
||||
" them\n"
|
||||
" --can-factory-reset Test whether factory reset is defined\n"
|
||||
@ -2799,6 +2825,8 @@ static int help(void) {
|
||||
" --definitions=DIR Find partitions in specified directory\n"
|
||||
" --seed=UUID 128bit seed UUID to derive all UUIDs from\n"
|
||||
" --size=BYTES Grow loopback file to specified size\n"
|
||||
" --json=pretty|short|off\n"
|
||||
" Generate json output\n"
|
||||
"\nSee the %s for details.\n"
|
||||
, program_invocation_short_name
|
||||
, ansi_highlight(), ansi_normal()
|
||||
@ -2822,6 +2850,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_PRETTY,
|
||||
ARG_DEFINITIONS,
|
||||
ARG_SIZE,
|
||||
ARG_JSON,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@ -2837,6 +2866,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "pretty", required_argument, NULL, ARG_PRETTY },
|
||||
{ "definitions", required_argument, NULL, ARG_DEFINITIONS },
|
||||
{ "size", required_argument, NULL, ARG_SIZE },
|
||||
{ "json", required_argument, NULL, ARG_JSON },
|
||||
{}
|
||||
};
|
||||
|
||||
@ -2960,6 +2990,26 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_size = rounded;
|
||||
break;
|
||||
}
|
||||
case ARG_JSON:
|
||||
if (streq(optarg, "pretty")) {
|
||||
arg_json = true;
|
||||
arg_json_format_flags = JSON_FORMAT_PRETTY|JSON_FORMAT_COLOR_AUTO;
|
||||
} else if (streq(optarg, "short")) {
|
||||
arg_json = true;
|
||||
arg_json_format_flags = JSON_FORMAT_NEWLINE;
|
||||
} else if (streq(optarg, "off")) {
|
||||
arg_json = false;
|
||||
arg_json_format_flags = 0;
|
||||
} else if (streq(optarg, "help")) {
|
||||
puts("pretty\n"
|
||||
"short\n"
|
||||
"off");
|
||||
return 0;
|
||||
} else
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown argument to --json=: %s", optarg);
|
||||
|
||||
break;
|
||||
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@ -10,6 +10,8 @@ mkdir -p $D/definitions
|
||||
|
||||
SEED=e2a40bf9-73f1-4278-9160-49c031e7aef8
|
||||
|
||||
echo "### Testing systemd-repart --empty=create ###"
|
||||
|
||||
$repart $D/zzz --empty=create --size=1G --seed=$SEED
|
||||
|
||||
sfdisk -d $D/zzz | grep -v -e 'sector-size' -e '^$' >$D/empty
|
||||
@ -23,6 +25,8 @@ first-lba: 2048
|
||||
last-lba: 2097118
|
||||
EOF
|
||||
|
||||
echo "### Testing with root, root2, home, & swap ###"
|
||||
|
||||
cat >$D/definitions/root.conf <<EOF
|
||||
[Partition]
|
||||
Type=root-x86-64
|
||||
@ -61,6 +65,8 @@ $D/zzz3 : start= 1185760, size= 591864, type=4F68BCE3-E8CD-4DB1-96E7-FB
|
||||
$D/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=2AA78CDB-59C7-4173-AF11-C7453737A5D1, name="swap"
|
||||
EOF
|
||||
|
||||
echo "### Testing with root, root2, home, swap, & another partition ###"
|
||||
|
||||
cat >$D/definitions/swap.conf <<EOF
|
||||
[Partition]
|
||||
Type=swap
|
||||
@ -95,6 +101,8 @@ $D/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-09
|
||||
$D/zzz5 : start= 1908696, size= 188416, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name="custom_label"
|
||||
EOF
|
||||
|
||||
echo "### Resizing to 2G ###"
|
||||
|
||||
$repart $D/zzz --size=2G --dry-run=no --seed=$SEED --definitions=$D/definitions
|
||||
|
||||
sfdisk -d $D/zzz | grep -v -e 'sector-size' -e '^$' >$D/populated3
|
||||
@ -115,6 +123,8 @@ EOF
|
||||
|
||||
dd if=/dev/urandom of=$D/block-copy bs=4096 count=10240
|
||||
|
||||
echo "### Testing with root, root2, home, swap, another partition, & partition copy ###"
|
||||
|
||||
cat >$D/definitions/extra2.conf <<EOF
|
||||
[Partition]
|
||||
Type=linux-generic
|
||||
@ -143,3 +153,8 @@ $D/zzz6 : start= 4194264, size= 2097152, type=0FC63DAF-8483-4772-8E79-3D
|
||||
EOF
|
||||
|
||||
cmp --bytes=41943040 --ignore-initial=0:$((512*4194264)) $D/block-copy $D/zzz
|
||||
|
||||
echo "### Testing json output ###"
|
||||
$repart $D/zzz --size=3G --dry-run=no --seed=$SEED --definitions=$D/definitions --json=help
|
||||
$repart $D/zzz --size=3G --dry-run=no --seed=$SEED --definitions=$D/definitions --json=pretty
|
||||
$repart $D/zzz --size=3G --dry-run=no --seed=$SEED --definitions=$D/definitions --json=short
|
||||
|
||||
@ -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
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
[Unit]
|
||||
Description=Cleaning Up and Shutting Down Daemons
|
||||
DefaultDependencies=no
|
||||
ConditionPathExists=/etc/initrd-release
|
||||
AssertPathExists=/etc/initrd-release
|
||||
OnFailure=emergency.target
|
||||
OnFailureJobMode=replace-irreversibly
|
||||
After=initrd-root-fs.target initrd-fs.target initrd.target
|
||||
|
||||
@ -12,7 +12,7 @@ Description=Initrd File Systems
|
||||
Documentation=man:systemd.special(7)
|
||||
OnFailure=emergency.target
|
||||
OnFailureJobMode=replace-irreversibly
|
||||
ConditionPathExists=/etc/initrd-release
|
||||
AssertPathExists=/etc/initrd-release
|
||||
After=initrd-parse-etc.service
|
||||
DefaultDependencies=no
|
||||
Conflicts=shutdown.target
|
||||
|
||||
@ -14,7 +14,7 @@ Requires=initrd-root-fs.target
|
||||
After=initrd-root-fs.target
|
||||
OnFailure=emergency.target
|
||||
OnFailureJobMode=replace-irreversibly
|
||||
ConditionPathExists=/etc/initrd-release
|
||||
AssertPathExists=/etc/initrd-release
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
[Unit]
|
||||
Description=Initrd Root Device
|
||||
Documentation=man:systemd.special(7)
|
||||
ConditionPathExists=/etc/initrd-release
|
||||
AssertPathExists=/etc/initrd-release
|
||||
OnFailure=emergency.target
|
||||
OnFailureJobMode=replace-irreversibly
|
||||
DefaultDependencies=no
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
[Unit]
|
||||
Description=Initrd Root File System
|
||||
Documentation=man:systemd.special(7)
|
||||
ConditionPathExists=/etc/initrd-release
|
||||
AssertPathExists=/etc/initrd-release
|
||||
OnFailure=emergency.target
|
||||
OnFailureJobMode=replace-irreversibly
|
||||
DefaultDependencies=no
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
[Unit]
|
||||
Description=Switch Root
|
||||
DefaultDependencies=no
|
||||
ConditionPathExists=/etc/initrd-release
|
||||
AssertPathExists=/etc/initrd-release
|
||||
OnFailure=emergency.target
|
||||
OnFailureJobMode=replace-irreversibly
|
||||
AllowIsolate=yes
|
||||
|
||||
@ -9,9 +9,9 @@
|
||||
|
||||
[Unit]
|
||||
Description=Switch Root
|
||||
ConditionPathExists=/etc/initrd-release
|
||||
AssertPathExists=/etc/initrd-release
|
||||
DefaultDependencies=no
|
||||
Requires=initrd-switch-root.service
|
||||
Wants=initrd-switch-root.service
|
||||
Before=initrd-switch-root.service
|
||||
AllowIsolate=yes
|
||||
Wants=initrd-udevadm-cleanup-db.service initrd-root-fs.target initrd-fs.target systemd-journald.service initrd-cleanup.service
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
[Unit]
|
||||
Description=Cleanup udev Database
|
||||
DefaultDependencies=no
|
||||
ConditionPathExists=/etc/initrd-release
|
||||
AssertPathExists=/etc/initrd-release
|
||||
Conflicts=systemd-udevd.service systemd-udevd-control.socket systemd-udevd-kernel.socket systemd-udev-trigger.service systemd-udev-settle.service
|
||||
After=systemd-udevd.service systemd-udevd-control.socket systemd-udevd-kernel.socket systemd-udev-trigger.service systemd-udev-settle.service
|
||||
Before=initrd-switch-root.target
|
||||
|
||||
@ -12,7 +12,7 @@ Description=Initrd Default Target
|
||||
Documentation=man:systemd.special(7)
|
||||
OnFailure=emergency.target
|
||||
OnFailureJobMode=replace-irreversibly
|
||||
ConditionPathExists=/etc/initrd-release
|
||||
AssertPathExists=/etc/initrd-release
|
||||
Requires=basic.target
|
||||
Wants=initrd-root-fs.target initrd-root-device.target initrd-fs.target initrd-parse-etc.service
|
||||
After=initrd-root-fs.target initrd-root-device.target initrd-fs.target basic.target rescue.service rescue.target
|
||||
|
||||
@ -15,7 +15,7 @@ BindsTo=%i.device
|
||||
Wants=local-fs-pre.target
|
||||
After=%i.device
|
||||
Before=local-fs-pre.target
|
||||
ConditionPathExists=/etc/initrd-release
|
||||
AssertPathExists=/etc/initrd-release
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user