Merge pull request #14398 from poettering/mount-prep

This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2020-02-04 15:36:17 +01:00
commit dc9fd22d3d
13 changed files with 306 additions and 113 deletions

3
TODO
View File

@ -44,6 +44,9 @@ Features:
* cryptsetup: allow encoding key directly in /etc/crypttab, maybe with a
"base64:" prefix. Useful in particular for pkcs11 mode.
* cryptsetup: reimplement the mkswap/mke2fs in cryptsetup-generator to use
systemd-makefs.service instead.
* socket units: allow creating a udev monitor socket with ListenDevices= or so,
with matches, then actviate app thorugh that passing socket oveer

View File

@ -26,6 +26,7 @@
<filename>cryptsetup-pre.target</filename>,
<filename>cryptsetup.target</filename>,
<filename>ctrl-alt-del.target</filename>,
<filename>blockdev@.target</filename>,
<filename>boot-complete.target</filename>,
<filename>default.target</filename>,
<filename>emergency.target</filename>,
@ -845,6 +846,23 @@
not useful as only unit within a transaction.</para>
<variablelist>
<varlistentry>
<term><filename>blockdev@.target</filename></term>
<listitem><para>This template unit may be used to order mount units and other consumers of block
devices against services that synthesize these block devices. This is intended to be used to order
storage services (such as
<citerefentry><refentrytitle>systemd-cryptsetup@.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>)
that allocate and manage a virtual block device against mount units and other consumers of
it. Specifically, the storage services are supposed to be orderd before an instance of
<filename>blockdev@.target</filename>, and the mount unit (or other consuming unit, such as a swap
unit) after it. The ordering is particular relevant during shutdown, as it ensures that the mount
is deactivated first and the service backing the mount only deactivated after that completed. The
<filename>blockdev@.target</filename> instance should be pulled in via a <option>Wants=</option>
dependency of the storage daemon and thus generally not be part of any transaction unless a storage
daemon is used. The instance name for instances of this template unit is supposed to be the
properly escaped bock device node path, e.g. <filename>blockdev@dev-mapper-foobar.target</filename>
for a storage device <filename>/dev/mapper/foobar</filename>.</para></listitem>
</varlistentry>
<varlistentry>
<term><filename>cryptsetup-pre.target</filename></term>
<listitem>

View File

@ -217,7 +217,7 @@ static void mount_done(Unit *u) {
m->timer_event_source = sd_event_source_unref(m->timer_event_source);
}
_pure_ static MountParameters* get_mount_parameters_fragment(Mount *m) {
static MountParameters* get_mount_parameters_fragment(Mount *m) {
assert(m);
if (m->from_fragment)
@ -226,7 +226,7 @@ _pure_ static MountParameters* get_mount_parameters_fragment(Mount *m) {
return NULL;
}
_pure_ static MountParameters* get_mount_parameters(Mount *m) {
static MountParameters* get_mount_parameters(Mount *m) {
assert(m);
if (m->from_proc_self_mountinfo)
@ -342,20 +342,18 @@ static int mount_add_device_dependencies(Mount *m) {
if (!is_device_path(p->what))
return 0;
/* /dev/root is a really weird thing, it's not a real device,
* but just a path the kernel exports for the root file system
* specified on the kernel command line. Ignore it here. */
if (path_equal(p->what, "/dev/root"))
/* /dev/root is a really weird thing, it's not a real device, but just a path the kernel exports for
* the root file system specified on the kernel command line. Ignore it here. */
if (PATH_IN_SET(p->what, "/dev/root", "/dev/nfs"))
return 0;
if (path_equal(m->where, "/"))
return 0;
/* Mount units from /proc/self/mountinfo are not bound to devices
* by default since they're subject to races when devices are
* unplugged. But the user can still force this dep with an
* appropriate option (or udev property) so the mount units are
* automatically stopped when the device disappears suddenly. */
/* Mount units from /proc/self/mountinfo are not bound to devices by default since they're subject to
* races when devices are unplugged. But the user can still force this dep with an appropriate option
* (or udev property) so the mount units are automatically stopped when the device disappears
* suddenly. */
dep = mount_is_bound_to_device(m) ? UNIT_BINDS_TO : UNIT_REQUIRES;
/* We always use 'what' from /proc/self/mountinfo if mounted */
@ -365,7 +363,7 @@ static int mount_add_device_dependencies(Mount *m) {
if (r < 0)
return r;
return 0;
return unit_add_blockdev_dependency(UNIT(m), p->what, mask);
}
static int mount_add_quota_dependencies(Mount *m) {

View File

@ -184,21 +184,45 @@ static int swap_arm_timer(Swap *s, usec_t usec) {
return 0;
}
static SwapParameters* swap_get_parameters(Swap *s) {
assert(s);
if (s->from_proc_swaps)
return &s->parameters_proc_swaps;
if (s->from_fragment)
return &s->parameters_fragment;
return NULL;
}
static int swap_add_device_dependencies(Swap *s) {
UnitDependencyMask mask;
SwapParameters *p;
int r;
assert(s);
if (!s->what)
return 0;
if (!s->from_fragment)
p = swap_get_parameters(s);
if (!p)
return 0;
if (is_device_path(s->what))
return unit_add_node_dependency(UNIT(s), s->what, UNIT_BINDS_TO, UNIT_DEPENDENCY_FILE);
mask = s->from_proc_swaps ? UNIT_DEPENDENCY_PROC_SWAP : UNIT_DEPENDENCY_FILE;
/* File based swap devices need to be ordered after systemd-remount-fs.service,
* since they might need a writable file system. */
return unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_REMOUNT_FS_SERVICE, true, UNIT_DEPENDENCY_FILE);
if (is_device_path(p->what)) {
r = unit_add_node_dependency(UNIT(s), p->what, UNIT_REQUIRES, mask);
if (r < 0)
return r;
return unit_add_blockdev_dependency(UNIT(s), p->what, mask);
}
/* File based swap devices need to be ordered after systemd-remount-fs.service, since they might need
* a writable file system. */
return unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_REMOUNT_FS_SERVICE, true, mask);
}
static int swap_add_default_dependencies(Swap *s) {

View File

@ -3866,8 +3866,8 @@ int unit_deserialize_skip(FILE *f) {
}
int unit_add_node_dependency(Unit *u, const char *what, UnitDependency dep, UnitDependencyMask mask) {
Unit *device;
_cleanup_free_ char *e = NULL;
Unit *device;
int r;
assert(u);
@ -3879,8 +3879,7 @@ int unit_add_node_dependency(Unit *u, const char *what, UnitDependency dep, Unit
if (!is_device_path(what))
return 0;
/* When device units aren't supported (such as in a
* container), don't create dependencies on them. */
/* When device units aren't supported (such as in a container), don't create dependencies on them. */
if (!unit_type_supported(UNIT_DEVICE))
return 0;
@ -3900,6 +3899,33 @@ int unit_add_node_dependency(Unit *u, const char *what, UnitDependency dep, Unit
device, true, mask);
}
int unit_add_blockdev_dependency(Unit *u, const char *what, UnitDependencyMask mask) {
_cleanup_free_ char *escaped = NULL, *target = NULL;
int r;
assert(u);
if (isempty(what))
return 0;
if (!path_startswith(what, "/dev/"))
return 0;
/* If we don't support devices, then also don't bother with blockdev@.target */
if (!unit_type_supported(UNIT_DEVICE))
return 0;
r = unit_name_path_escape(what, &escaped);
if (r < 0)
return r;
r = unit_name_build("blockdev", escaped, ".target", &target);
if (r < 0)
return r;
return unit_add_dependency_by_name(u, UNIT_AFTER, target, true, mask);
}
int unit_coldplug(Unit *u) {
int r = 0, q;
char **i;

View File

@ -743,6 +743,7 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds);
int unit_deserialize_skip(FILE *f);
int unit_add_node_dependency(Unit *u, const char *what, UnitDependency d, UnitDependencyMask mask);
int unit_add_blockdev_dependency(Unit *u, const char *what, UnitDependencyMask mask);
int unit_coldplug(Unit *u);
void unit_catchup(Unit *u);

View File

@ -99,7 +99,14 @@ static int split_keyspec(const char *keyspec, char **ret_keyfile, char **ret_key
return 0;
}
static int generate_keydev_mount(const char *name, const char *keydev, const char *keydev_timeout, bool canfail, char **unit, char **mount) {
static int generate_keydev_mount(
const char *name,
const char *keydev,
const char *keydev_timeout,
bool canfail,
char **unit,
char **mount) {
_cleanup_free_ char *u = NULL, *where = NULL, *name_escaped = NULL, *device_unit = NULL;
_cleanup_fclose_ FILE *f = NULL;
int r;
@ -223,8 +230,8 @@ static int create_disk(
const char *options) {
_cleanup_free_ char *n = NULL, *d = NULL, *u = NULL, *e = NULL,
*keydev_mount = NULL, *keyfile_timeout_value = NULL, *password_escaped = NULL,
*filtered = NULL, *u_escaped = NULL, *filtered_escaped = NULL, *name_escaped = NULL, *header_path = NULL;
*keydev_mount = NULL, *keyfile_timeout_value = NULL,
*filtered = NULL, *u_escaped = NULL, *name_escaped = NULL, *header_path = NULL, *password_buffer = NULL;
_cleanup_fclose_ FILE *f = NULL;
const char *dmname;
bool noauto, nofail, tmp, swap, netdev, attach_in_initrd;
@ -285,39 +292,29 @@ static int create_disk(
if (r < 0)
return r;
fprintf(f,
"[Unit]\n"
"Description=Cryptography Setup for %%I\n"
"Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n"
"SourcePath=%s\n"
"DefaultDependencies=no\n"
"IgnoreOnIsolate=true\n"
"After=%s\n",
arg_crypttab,
netdev ? "remote-fs-pre.target" : "cryptsetup-pre.target");
r = generator_write_cryptsetup_unit_section(f, arg_crypttab);
if (r < 0)
return r;
if (netdev)
fprintf(f, "After=remote-fs-pre.target\n");
/* If initrd takes care of attaching the disk then it should also detach it during shutdown. */
if (!attach_in_initrd)
fprintf(f, "Conflicts=umount.target\n");
if (password) {
password_escaped = specifier_escape(password);
if (!password_escaped)
return log_oom();
}
if (keydev) {
_cleanup_free_ char *unit = NULL, *p = NULL;
_cleanup_free_ char *unit = NULL;
r = generate_keydev_mount(name, keydev, keyfile_timeout_value, keyfile_can_timeout > 0, &unit, &keydev_mount);
if (r < 0)
return log_error_errno(r, "Failed to generate keydev mount unit: %m");
p = path_join(keydev_mount, password_escaped);
if (!p)
password_buffer = path_join(keydev_mount, password);
if (!password_buffer)
return log_oom();
free_and_replace(password_escaped, p);
password = password_buffer;
fprintf(f, "After=%s\n", unit);
if (keyfile_can_timeout > 0)
@ -344,17 +341,13 @@ static int create_disk(
return r;
}
if (path_startswith(u, "/dev/")) {
if (path_startswith(u, "/dev/"))
fprintf(f,
"BindsTo=%s\n"
"After=%s\n"
"Before=umount.target\n",
d, d);
if (swap)
fputs("Before=dev-mapper-%i.swap\n",
f);
} else
else
/* For loopback devices, add systemd-tmpfiles-setup-dev.service
dependency to ensure that loopback support is available in
the kernel (/dev/loop-control needs to exist) */
@ -368,23 +361,9 @@ static int create_disk(
if (r < 0)
log_warning_errno(r, "Failed to write device timeout drop-in: %m");
if (filtered) {
filtered_escaped = specifier_escape(filtered);
if (!filtered_escaped)
return log_oom();
}
fprintf(f,
"\n[Service]\n"
"Type=oneshot\n"
"RemainAfterExit=yes\n"
"TimeoutSec=0\n" /* the binary handles timeouts anyway */
"KeyringMode=shared\n" /* make sure we can share cached keys among instances */
"OOMScoreAdjust=500\n" /* unlocking can allocate a lot of memory if Argon2 is used */
"ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n"
"ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
name_escaped, u_escaped, strempty(password_escaped), strempty(filtered_escaped),
name_escaped);
r = generator_write_cryptsetup_service_section(f, name, u, password, filtered);
if (r < 0)
return r;
if (tmp)
fprintf(f,

View File

@ -118,11 +118,18 @@ static int add_swap(
fprintf(f,
"[Unit]\n"
"SourcePath=%s\n"
"Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n\n"
"[Swap]\n",
"Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n"
"SourcePath=%s\n",
fstab_path());
r = generator_write_blockdev_dependency(f, what);
if (r < 0)
return r;
fprintf(f,
"\n"
"[Swap]\n");
r = write_what(f, what);
if (r < 0)
return r;
@ -174,8 +181,13 @@ static bool mount_in_initrd(struct mntent *me) {
streq(me->mnt_dir, "/usr");
}
static int write_timeout(FILE *f, const char *where, const char *opts,
const char *filter, const char *variable) {
static int write_timeout(
FILE *f,
const char *where,
const char *opts,
const char *filter,
const char *variable) {
_cleanup_free_ char *timeout = NULL;
char timespan[FORMAT_TIMESPAN_MAX];
usec_t u;
@ -208,8 +220,12 @@ static int write_mount_timeout(FILE *f, const char *where, const char *opts) {
"x-systemd.mount-timeout\0", "TimeoutSec");
}
static int write_dependency(FILE *f, const char *opts,
const char *filter, const char *format) {
static int write_dependency(
FILE *f,
const char *opts,
const char *filter,
const char *format) {
_cleanup_strv_free_ char **names = NULL, **units = NULL;
_cleanup_free_ char *res = NULL;
char **s;
@ -230,6 +246,7 @@ static int write_dependency(FILE *f, const char *opts,
r = unit_name_mangle_with_suffix(*s, "as dependency", 0, ".mount", &x);
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
r = strv_consume(&units, x);
if (r < 0)
return log_oom();
@ -249,7 +266,8 @@ static int write_dependency(FILE *f, const char *opts,
}
static int write_after(FILE *f, const char *opts) {
return write_dependency(f, opts, "x-systemd.after", "After=%1$s\n");
return write_dependency(f, opts,
"x-systemd.after", "After=%1$s\n");
}
static int write_requires_after(FILE *f, const char *opts) {
@ -363,8 +381,8 @@ static int add_mount(
fprintf(f,
"[Unit]\n"
"SourcePath=%s\n"
"Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
"Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n"
"SourcePath=%s\n",
source);
/* All mounts under /sysroot need to happen later, at initrd-fs.target time. IOW, it's not
@ -411,7 +429,14 @@ static int add_mount(
return r;
}
fprintf(f, "\n[Mount]\n");
r = generator_write_blockdev_dependency(f, what);
if (r < 0)
return r;
fprintf(f,
"\n"
"[Mount]\n");
if (original_where)
fprintf(f, "# Canonicalized from %s\n", original_where);

View File

@ -105,9 +105,8 @@ static int open_parent_block_device(dev_t devnum, int *ret_fd) {
}
static int add_cryptsetup(const char *id, const char *what, bool rw, bool require, char **device) {
_cleanup_free_ char *e = NULL, *n = NULL, *d = NULL, *id_escaped = NULL, *what_escaped = NULL;
_cleanup_free_ char *e = NULL, *n = NULL, *d = NULL;
_cleanup_fclose_ FILE *f = NULL;
const char *p;
int r;
assert(id);
@ -125,44 +124,28 @@ static int add_cryptsetup(const char *id, const char *what, bool rw, bool requir
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
id_escaped = specifier_escape(id);
if (!id_escaped)
return log_oom();
r = generator_open_unit_file(arg_dest, NULL, n, &f);
if (r < 0)
return r;
what_escaped = specifier_escape(what);
if (!what_escaped)
return log_oom();
p = prefix_roota(arg_dest, n);
f = fopen(p, "wxe");
if (!f)
return log_error_errno(errno, "Failed to create unit file %s: %m", p);
r = generator_write_cryptsetup_unit_section(f, NULL);
if (r < 0)
return r;
fprintf(f,
"# Automatically generated by systemd-gpt-auto-generator\n\n"
"[Unit]\n"
"Description=Cryptography Setup for %%I\n"
"Documentation=man:systemd-gpt-auto-generator(8) man:systemd-cryptsetup@.service(8)\n"
"DefaultDependencies=no\n"
"Conflicts=umount.target\n"
"BindsTo=dev-mapper-%%i.device %s\n"
"Before=umount.target cryptsetup.target\n"
"After=%s\n"
"IgnoreOnIsolate=true\n"
"[Service]\n"
"Type=oneshot\n"
"RemainAfterExit=yes\n"
"TimeoutSec=0\n" /* the binary handles timeouts anyway */
"KeyringMode=shared\n" /* make sure we can share cached keys among instances */
"ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '' '%s'\n"
"ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
d, d,
id_escaped, what_escaped, rw ? "" : "read-only",
id_escaped);
"Conflicts=umount.target\n"
"BindsTo=%s\n"
"After=%s\n",
d, d);
r = generator_write_cryptsetup_service_section(f, id, what, NULL, rw ? NULL : "read-only");
if (r < 0)
return r;
r = fflush_and_check(f);
if (r < 0)
return log_error_errno(r, "Failed to write file %s: %m", p);
return log_error_errno(r, "Failed to write file %s: %m", n);
r = generator_add_symlink(arg_dest, d, "wants", n);
if (r < 0)
@ -227,7 +210,6 @@ static int add_mount(
log_debug("Adding %s: %s fstype=%s", where, what, fstype ?: "(any)");
if (streq_ptr(fstype, "crypto_LUKS")) {
r = add_cryptsetup(id, what, rw, true, &crypto_what);
if (r < 0)
return r;
@ -262,6 +244,10 @@ static int add_mount(
if (r < 0)
return r;
r = generator_write_blockdev_dependency(f, what);
if (r < 0)
return r;
fprintf(f,
"\n"
"[Mount]\n"
@ -370,7 +356,14 @@ static int add_swap(const char *path) {
"# Automatically generated by systemd-gpt-auto-generator\n\n"
"[Unit]\n"
"Description=Swap Partition\n"
"Documentation=man:systemd-gpt-auto-generator(8)\n\n"
"Documentation=man:systemd-gpt-auto-generator(8)\n");
r = generator_write_blockdev_dependency(f, path);
if (r < 0)
return r;
fprintf(f,
"\n"
"[Swap]\n"
"What=%s\n",
path);

View File

@ -519,6 +519,103 @@ int generator_enable_remount_fs_service(const char *dir) {
SYSTEM_DATA_UNIT_PATH "/" SPECIAL_REMOUNT_FS_SERVICE);
}
int generator_write_blockdev_dependency(
FILE *f,
const char *what) {
_cleanup_free_ char *escaped = NULL;
int r;
assert(f);
assert(what);
if (!path_startswith(what, "/dev/"))
return 0;
r = unit_name_path_escape(what, &escaped);
if (r < 0)
return log_error_errno(r, "Failed to escape device node path %s: %m", what);
fprintf(f,
"After=blockdev@%s.target\n",
escaped);
return 0;
}
int generator_write_cryptsetup_unit_section(
FILE *f,
const char *source) {
assert(f);
fprintf(f,
"[Unit]\n"
"Description=Cryptography Setup for %%I\n"
"Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n");
if (source)
fprintf(f, "SourcePath=%s\n", source);
fprintf(f,
"DefaultDependencies=no\n"
"IgnoreOnIsolate=true\n"
"After=cryptsetup-pre.target\n"
"Before=blockdev@dev-mapper-%%i.target\n"
"Wants=blockdev@dev-mapper-%%i.target\n");
return 0;
}
int generator_write_cryptsetup_service_section(
FILE *f,
const char *name,
const char *what,
const char *password,
const char *options) {
_cleanup_free_ char *name_escaped = NULL, *what_escaped = NULL, *password_escaped = NULL, *options_escaped = NULL;
assert(f);
assert(name);
assert(what);
name_escaped = specifier_escape(name);
if (!name_escaped)
return log_oom();
what_escaped = specifier_escape(what);
if (!what_escaped)
return log_oom();
if (password) {
password_escaped = specifier_escape(password);
if (!password_escaped)
return log_oom();
}
if (options) {
options_escaped = specifier_escape(options);
if (!options_escaped)
return log_oom();
}
fprintf(f,
"\n"
"[Service]\n"
"Type=oneshot\n"
"RemainAfterExit=yes\n"
"TimeoutSec=0\n" /* The binary handles timeouts on its own */
"KeyringMode=shared\n" /* Make sure we can share cached keys among instances */
"OOMScoreAdjust=500\n" /* Unlocking can allocate a lot of memory if Argon2 is used */
"ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n"
"ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
name_escaped, what_escaped, strempty(password_escaped), strempty(options_escaped),
name_escaped);
return 0;
}
void log_setup_generator(void) {
log_set_prohibit_ipc(true);
log_setup_service();

View File

@ -27,6 +27,21 @@ int generator_write_timeouts(
const char *opts,
char **filtered);
int generator_write_blockdev_dependency(
FILE *f,
const char *what);
int generator_write_cryptsetup_unit_section(
FILE *f,
const char *source);
int generator_write_cryptsetup_service_section(
FILE *f,
const char *name,
const char *what,
const char *password,
const char *options);
int generator_write_device_deps(
const char *dir,
const char *what,

13
units/blockdev@.target Normal file
View File

@ -0,0 +1,13 @@
# SPDX-License-Identifier: LGPL-2.1+
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=Block Device Preparation for %f
Documentation=man:systemd.special(7)
StopWhenUnneeded=yes

View File

@ -2,6 +2,7 @@
units = [
['basic.target', ''],
['blockdev@.target', ''],
['bluetooth.target', ''],
['boot-complete.target', ''],
['cryptsetup-pre.target', 'HAVE_LIBCRYPTSETUP'],