1
0
mirror of https://github.com/systemd/systemd synced 2025-11-09 20:04:46 +01:00

Compare commits

..

13 Commits

Author SHA1 Message Date
Zbigniew Jędrzejewski-Szmek
18dbc899ff
Merge pull request #16258 from hunger/master
systemd-repart: Add an option to generate a JSON report
2020-08-03 10:05:32 +02:00
Zbigniew Jędrzejewski-Szmek
7e62257219
Merge pull request #16308 from bluca/root_image_options
service: add new RootImageOptions feature
2020-08-03 10:04:36 +02:00
Marti Raudsepp
09364a8043 machine: Pass machine, user, program values to polkit on OpenMachineShell
This allows more granular access control in PolicyKit rules, similar to
/etc/sudoers, for polkit actions:
* org.freedesktop.machine1.host-shell
* org.freedesktop.machine1.shell

Example configuration, place in /etc/polkit-1/rules.d/

polkit.addRule(function(action, subject) {
    if (action.id == "org.freedesktop.machine1.host-shell"
        && subject.user == "my-user"
        && action.lookup("user") == "target-user") {
            return polkit.Result.YES;
    }
});
2020-08-03 10:03:13 +02:00
Zbigniew Jędrzejewski-Szmek
653ca0d913
Merge pull request #16609 from poettering/initrd-unit-fixes
units: some initrd unit fixes
2020-08-03 10:01:30 +02:00
Kir Kolyshkin
1cdbff1c84 kernel-install/90-loaderentry: fix when /boot is not mountpoint
I happen to have a machine where /boot is not a separate mountpoint,
but rather just a directory under /. After upgrade to recent Fedora,
I found out that grub2 can't find any new kernels.

This happens because loadentry script generates kernel and initrd file
paths relative to /boot, while grub2 expects path to be relative to the
root of filesystem on which they are residing.

This commit fixes this issue by using stat's %m to find the mount point
of a partition holding the images, and using it as a prefix to be
removed from ENTRY_DIR_ABS.

Note that %m for stat requires coreutils 8.6, released in Oct 2010.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2020-08-03 10:01:03 +02:00
Daan De Meyer
6f646e0175 nspawn: Fix incorrect usage of putenv
strv_env_get only returns the environment variable value. putenv expects
KEY=VALUE format strings. Use setenv instead to fix the use.
2020-08-03 09:58:05 +02:00
Zbigniew Jędrzejewski-Szmek
e4ec8b1f51 test-repart: also print json output
This doesn't really "test" anything, but a human can look at it.
2020-07-30 16:03:53 +02:00
Zbigniew Jędrzejewski-Szmek
2d2d0a57e7 repart: adjust --help and test output width 2020-07-30 16:02:45 +02:00
Tobias Hunger
a015fbe7ef Repart: Add support for machine readably report
Add --json command line argument that prints all relevant information
in a machine-readable format.
2020-07-30 12:26:15 +02:00
Luca Boccassi
18d7370587 service: add new RootImageOptions feature
Allows to specify mount options for RootImage.
In case of multi-partition images, the partition number can be prefixed
followed by colon. Eg:

RootImageOptions=1:ro,dev 2:nosuid nodev

In absence of a partition number, 0 is assumed.
2020-07-29 17:17:32 +01:00
Luca Boccassi
5511d8c1b9 basic/list: add LIST_JOIN helper
Joins together two lists, tail to head.

a -> b
c -> d

a -> b -> c -> d
2020-07-29 17:12:45 +01:00
Lennart Poettering
b89f5f7b03 units: downgrade Requires= to Wants=
Combining Requires= with Before= doesn't really make sense, since this
means we are requiring something that runs after us, which logically
cannot be fulfilled.

Let's hence downgrade Requires= to Wants= so that the ordering is kept
but no failure propagation implied.
2020-07-29 16:32:45 +02:00
Lennart Poettering
78490efca3 units: upgrade initrd check Conditions to Asserts
It's a bug if an initrd unit is run on the host. Hence let's upgrade the
conditions to asserts.
2020-07-29 16:32:03 +02:00
41 changed files with 583 additions and 50 deletions

View File

@ -265,6 +265,15 @@
and graphic illustrating the changes applied.</para></listitem> and graphic illustrating the changes applied.</para></listitem>
</varlistentry> </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> <varlistentry>
<term><option>--definitions=</option></term> <term><option>--definitions=</option></term>

View File

@ -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>

View File

@ -2668,6 +2668,7 @@ if conf.get('ENABLE_REPART') == 1
install_rpath : rootlibexecdir, install_rpath : rootlibexecdir,
install : true, install : true,
install_dir : rootbindir) install_dir : rootbindir)
public_programs += exe
if want_tests != 'false' if want_tests != 'false'
test('test-repart', test('test-repart',

View File

@ -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)

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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)

View File

@ -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,

View File

@ -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);

View File

@ -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)

View File

@ -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,

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -18,8 +18,9 @@ fi
MACHINE_ID=$KERNEL_INSTALL_MACHINE_ID MACHINE_ID=$KERNEL_INSTALL_MACHINE_ID
ENTRY_DIR="/$MACHINE_ID/$KERNEL_VERSION" BOOT_ROOT=${ENTRY_DIR_ABS%/$MACHINE_ID/$KERNEL_VERSION}
BOOT_ROOT=${ENTRY_DIR_ABS%$ENTRY_DIR} BOOT_MNT=$(stat -c %m $BOOT_ROOT)
ENTRY_DIR=/${ENTRY_DIR_ABS#$BOOT_MNT}
if [[ $COMMAND == remove ]]; then if [[ $COMMAND == remove ]]; then
rm -f "$BOOT_ROOT/loader/entries/$MACHINE_ID-$KERNEL_VERSION.conf" rm -f "$BOOT_ROOT/loader/entries/$MACHINE_ID-$KERNEL_VERSION.conf"

View File

@ -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); r = sd_bus_message_read(message, "ss", &user, &path);
if (r < 0) if (r < 0)
return r; return r;
user = empty_to_null(user); user = isempty(user) ? "root" : user;
r = sd_bus_message_read_strv(message, &args_wire); r = sd_bus_message_read_strv(message, &args_wire);
if (r < 0) if (r < 0)
return r; return r;
@ -604,7 +604,7 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
r = asprintf(&args[2], r = asprintf(&args[2],
"shell=$(getent passwd %s 2>/dev/null | { IFS=: read _ _ _ _ _ _ x; echo \"$x\"; })\n"\ "shell=$(getent passwd %s 2>/dev/null | { IFS=: read _ _ _ _ _ _ x; echo \"$x\"; })\n"\
"exec \"${shell:-/bin/sh}\" -l", /* -l is means --login */ "exec \"${shell:-/bin/sh}\" -l", /* -l is means --login */
isempty(user) ? "root" : user); user);
if (r < 0) { if (r < 0) {
args[2] = NULL; args[2] = NULL;
return -ENOMEM; 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)) if (!strv_env_is_valid(env))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment assignments"); 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( r = bus_verify_polkit_async(
message, message,
CAP_SYS_ADMIN, CAP_SYS_ADMIN,
m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-shell" : "org.freedesktop.machine1.shell", m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-shell" : "org.freedesktop.machine1.shell",
NULL, details,
false, false,
UID_INVALID, UID_INVALID,
&m->manager->polkit_registry, &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) if (r < 0)
return r; return r;
description = strjoina("Shell for User ", isempty(user) ? "root" : user); description = strjoina("Shell for User ", user);
r = sd_bus_message_append(tm, r = sd_bus_message_append(tm,
"(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)", "(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)",
"Description", "s", description, "Description", "s", description,
@ -695,7 +702,7 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
if (r < 0) if (r < 0)
return r; 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) if (r < 0)
return r; return r;

View File

@ -3259,7 +3259,7 @@ static int inner_child(
* binary. */ * binary. */
dollar_path = strv_env_get(env_use, "PATH"); dollar_path = strv_env_get(env_use, "PATH");
if (dollar_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"); return log_error_errno(errno, "Failed to update $PATH: %m");
} }
@ -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) {

View File

@ -33,6 +33,7 @@
#include "fs-util.h" #include "fs-util.h"
#include "gpt.h" #include "gpt.h"
#include "id128-util.h" #include "id128-util.h"
#include "json.h"
#include "list.h" #include "list.h"
#include "locale-util.h" #include "locale-util.h"
#include "main-func.h" #include "main-func.h"
@ -79,6 +80,8 @@ static sd_id128_t arg_seed = SD_ID128_NULL;
static bool arg_randomize = false; static bool arg_randomize = false;
static int arg_pretty = -1; static int arg_pretty = -1;
static uint64_t arg_size = UINT64_MAX; 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_root, freep);
STATIC_DESTRUCTOR_REGISTER(arg_definitions, freep); STATIC_DESTRUCTOR_REGISTER(arg_definitions, freep);
@ -1620,12 +1623,23 @@ static int context_dump_partitions(Context *context, const char *node) {
Partition *p; Partition *p;
int r; 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) if (!t)
return log_oom(); return log_oom();
if (!DEBUG_LOGGING) 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 (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, 4), 100);
(void) table_set_align_percent(t, table_get_cell(t, 0, 5), 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) { LIST_FOREACH(partitions, p, context->partitions) {
_cleanup_free_ char *size_change = NULL, *padding_change = NULL, *partname = NULL; _cleanup_free_ char *size_change = NULL, *padding_change = NULL, *partname = NULL;
char uuid_buffer[ID128_UUID_STRING_MAX]; char uuid_buffer[ID128_UUID_STRING_MAX];
const char *label; const char *label, *activity = NULL;
if (p->dropped) if (p->dropped)
continue; continue;
if (p->current_size == UINT64_MAX)
activity = "create";
else if (p->current_size != p->new_size)
activity = "resize";
label = partition_label(p); label = partition_label(p);
partname = p->partno != UINT64_MAX ? fdisk_partname(node, p->partno+1) : NULL; 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_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_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, 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->offset,
TABLE_UINT64, p->current_size == UINT64_MAX ? 0 : p->current_size,
TABLE_UINT64, p->new_size, TABLE_UINT64, p->new_size,
TABLE_STRING, size_change, TABLE_SET_COLOR, !p->partitions_next && sum_size > 0 ? ansi_underline() : NULL, 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_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) if (r < 0)
return table_log_add_error(r); 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]; char s[FORMAT_BYTES_MAX];
const char *a, *b; 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_EMPTY, TABLE_EMPTY,
TABLE_EMPTY,
TABLE_STRING, a, TABLE_STRING, a,
TABLE_EMPTY, TABLE_EMPTY,
TABLE_STRING, b); TABLE_EMPTY,
TABLE_STRING, b,
TABLE_EMPTY);
if (r < 0) if (r < 0)
return table_log_add_error(r); 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) if (r < 0)
return log_error_errno(r, "Failed to dump table: %m"); return log_error_errno(r, "Failed to dump table: %m");
@ -2399,16 +2427,14 @@ static int context_write_partition_table(
assert(context); assert(context);
if (arg_pretty > 0 || 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) (void) context_dump_partitions(context, node);
puts("Empty partition table.");
else
(void) context_dump_partitions(context, node);
putc('\n', stdout); putc('\n', stdout);
(void) context_dump_partition_bar(context, node); if (!arg_json)
(void) context_dump_partition_bar(context, node);
putc('\n', stdout); putc('\n', stdout);
fflush(stdout); fflush(stdout);
} }
@ -2791,7 +2817,7 @@ static int help(void) {
" --empty=MODE One of refuse, allow, require, force, create; controls\n" " --empty=MODE One of refuse, allow, require, force, create; controls\n"
" how to handle empty disks lacking partition tables\n" " how to handle empty disks lacking partition tables\n"
" --discard=BOOL Whether to discard backing blocks for new partitions\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" " --factory-reset=BOOL Whether to remove data partitions before recreating\n"
" them\n" " them\n"
" --can-factory-reset Test whether factory reset is defined\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" " --definitions=DIR Find partitions in specified directory\n"
" --seed=UUID 128bit seed UUID to derive all UUIDs from\n" " --seed=UUID 128bit seed UUID to derive all UUIDs from\n"
" --size=BYTES Grow loopback file to specified size\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" "\nSee the %s for details.\n"
, program_invocation_short_name , program_invocation_short_name
, ansi_highlight(), ansi_normal() , ansi_highlight(), ansi_normal()
@ -2822,6 +2850,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_PRETTY, ARG_PRETTY,
ARG_DEFINITIONS, ARG_DEFINITIONS,
ARG_SIZE, ARG_SIZE,
ARG_JSON,
}; };
static const struct option options[] = { static const struct option options[] = {
@ -2837,6 +2866,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "pretty", required_argument, NULL, ARG_PRETTY }, { "pretty", required_argument, NULL, ARG_PRETTY },
{ "definitions", required_argument, NULL, ARG_DEFINITIONS }, { "definitions", required_argument, NULL, ARG_DEFINITIONS },
{ "size", required_argument, NULL, ARG_SIZE }, { "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; arg_size = rounded;
break; 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 '?': case '?':
return -EINVAL; return -EINVAL;

View File

@ -10,6 +10,8 @@ mkdir -p $D/definitions
SEED=e2a40bf9-73f1-4278-9160-49c031e7aef8 SEED=e2a40bf9-73f1-4278-9160-49c031e7aef8
echo "### Testing systemd-repart --empty=create ###"
$repart $D/zzz --empty=create --size=1G --seed=$SEED $repart $D/zzz --empty=create --size=1G --seed=$SEED
sfdisk -d $D/zzz | grep -v -e 'sector-size' -e '^$' >$D/empty sfdisk -d $D/zzz | grep -v -e 'sector-size' -e '^$' >$D/empty
@ -23,6 +25,8 @@ first-lba: 2048
last-lba: 2097118 last-lba: 2097118
EOF EOF
echo "### Testing with root, root2, home, & swap ###"
cat >$D/definitions/root.conf <<EOF cat >$D/definitions/root.conf <<EOF
[Partition] [Partition]
Type=root-x86-64 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" $D/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=2AA78CDB-59C7-4173-AF11-C7453737A5D1, name="swap"
EOF EOF
echo "### Testing with root, root2, home, swap, & another partition ###"
cat >$D/definitions/swap.conf <<EOF cat >$D/definitions/swap.conf <<EOF
[Partition] [Partition]
Type=swap 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" $D/zzz5 : start= 1908696, size= 188416, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name="custom_label"
EOF EOF
echo "### Resizing to 2G ###"
$repart $D/zzz --size=2G --dry-run=no --seed=$SEED --definitions=$D/definitions $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 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 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 cat >$D/definitions/extra2.conf <<EOF
[Partition] [Partition]
Type=linux-generic Type=linux-generic
@ -143,3 +153,8 @@ $D/zzz6 : start= 4194264, size= 2097152, type=0FC63DAF-8483-4772-8E79-3D
EOF EOF
cmp --bytes=41943040 --ignore-initial=0:$((512*4194264)) $D/block-copy $D/zzz 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

View File

@ -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)

View File

@ -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;
} }

View File

@ -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",

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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;
} }

View File

@ -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,

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -10,7 +10,7 @@
[Unit] [Unit]
Description=Cleaning Up and Shutting Down Daemons Description=Cleaning Up and Shutting Down Daemons
DefaultDependencies=no DefaultDependencies=no
ConditionPathExists=/etc/initrd-release AssertPathExists=/etc/initrd-release
OnFailure=emergency.target OnFailure=emergency.target
OnFailureJobMode=replace-irreversibly OnFailureJobMode=replace-irreversibly
After=initrd-root-fs.target initrd-fs.target initrd.target After=initrd-root-fs.target initrd-fs.target initrd.target

View File

@ -12,7 +12,7 @@ Description=Initrd File Systems
Documentation=man:systemd.special(7) Documentation=man:systemd.special(7)
OnFailure=emergency.target OnFailure=emergency.target
OnFailureJobMode=replace-irreversibly OnFailureJobMode=replace-irreversibly
ConditionPathExists=/etc/initrd-release AssertPathExists=/etc/initrd-release
After=initrd-parse-etc.service After=initrd-parse-etc.service
DefaultDependencies=no DefaultDependencies=no
Conflicts=shutdown.target Conflicts=shutdown.target

View File

@ -14,7 +14,7 @@ Requires=initrd-root-fs.target
After=initrd-root-fs.target After=initrd-root-fs.target
OnFailure=emergency.target OnFailure=emergency.target
OnFailureJobMode=replace-irreversibly OnFailureJobMode=replace-irreversibly
ConditionPathExists=/etc/initrd-release AssertPathExists=/etc/initrd-release
[Service] [Service]
Type=oneshot Type=oneshot

View File

@ -10,7 +10,7 @@
[Unit] [Unit]
Description=Initrd Root Device Description=Initrd Root Device
Documentation=man:systemd.special(7) Documentation=man:systemd.special(7)
ConditionPathExists=/etc/initrd-release AssertPathExists=/etc/initrd-release
OnFailure=emergency.target OnFailure=emergency.target
OnFailureJobMode=replace-irreversibly OnFailureJobMode=replace-irreversibly
DefaultDependencies=no DefaultDependencies=no

View File

@ -10,7 +10,7 @@
[Unit] [Unit]
Description=Initrd Root File System Description=Initrd Root File System
Documentation=man:systemd.special(7) Documentation=man:systemd.special(7)
ConditionPathExists=/etc/initrd-release AssertPathExists=/etc/initrd-release
OnFailure=emergency.target OnFailure=emergency.target
OnFailureJobMode=replace-irreversibly OnFailureJobMode=replace-irreversibly
DefaultDependencies=no DefaultDependencies=no

View File

@ -10,7 +10,7 @@
[Unit] [Unit]
Description=Switch Root Description=Switch Root
DefaultDependencies=no DefaultDependencies=no
ConditionPathExists=/etc/initrd-release AssertPathExists=/etc/initrd-release
OnFailure=emergency.target OnFailure=emergency.target
OnFailureJobMode=replace-irreversibly OnFailureJobMode=replace-irreversibly
AllowIsolate=yes AllowIsolate=yes

View File

@ -9,9 +9,9 @@
[Unit] [Unit]
Description=Switch Root Description=Switch Root
ConditionPathExists=/etc/initrd-release AssertPathExists=/etc/initrd-release
DefaultDependencies=no DefaultDependencies=no
Requires=initrd-switch-root.service Wants=initrd-switch-root.service
Before=initrd-switch-root.service Before=initrd-switch-root.service
AllowIsolate=yes AllowIsolate=yes
Wants=initrd-udevadm-cleanup-db.service initrd-root-fs.target initrd-fs.target systemd-journald.service initrd-cleanup.service Wants=initrd-udevadm-cleanup-db.service initrd-root-fs.target initrd-fs.target systemd-journald.service initrd-cleanup.service

View File

@ -10,7 +10,7 @@
[Unit] [Unit]
Description=Cleanup udev Database Description=Cleanup udev Database
DefaultDependencies=no 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 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 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 Before=initrd-switch-root.target

View File

@ -12,7 +12,7 @@ Description=Initrd Default Target
Documentation=man:systemd.special(7) Documentation=man:systemd.special(7)
OnFailure=emergency.target OnFailure=emergency.target
OnFailureJobMode=replace-irreversibly OnFailureJobMode=replace-irreversibly
ConditionPathExists=/etc/initrd-release AssertPathExists=/etc/initrd-release
Requires=basic.target Requires=basic.target
Wants=initrd-root-fs.target initrd-root-device.target initrd-fs.target initrd-parse-etc.service 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 After=initrd-root-fs.target initrd-root-device.target initrd-fs.target basic.target rescue.service rescue.target

View File

@ -15,7 +15,7 @@ BindsTo=%i.device
Wants=local-fs-pre.target Wants=local-fs-pre.target
After=%i.device After=%i.device
Before=local-fs-pre.target Before=local-fs-pre.target
ConditionPathExists=/etc/initrd-release AssertPathExists=/etc/initrd-release
[Service] [Service]
Type=oneshot Type=oneshot