fstab-generator: add x-systemd.requires and x-systemd.requires-mounts-for

Currently we have no way how to specify dependencies between fstab
entries (or another units) in the /etc/fstab. It means that users are
forced to bypass fstab and write .mount units manually.

The patch introduces new systemd fstab options:

x-systemd.requires=<PATH>

 - to specify dependence an another mount (PATH is translated to unit name)

x-systemd.requires=<UNIT>

 - to specify dependence on arbitrary UNIT

x-systemd.requires-mounts-for=<PATH ...>

 - to specify dependence on another paths, implemented by
   RequiresMountsFor=. The option may be specified more than once.

For example two bind mounts where B depends on A:

 /mnt/test/A    /mnt/test/A     none    bind,defaults
 /mnt/test/A    /mnt/test/B     none    bind,x-systemd.requires=/mnt/test/A

More complex example with overlay FS where one mount point depends on
"low" and "upper" directories:

 /dev/sdc1   /mnt/low    ext4     defaults
 /dev/sdc2   /mnt/high   ext4     defaults
 overlay     /mnt/merged overlay  lowerdir=/mnt/low,upperdir=/mnt/high/data,workdir=/mnt/high/work,x-systemd.requires-mounts-for=/mnt/low,x-systemd.requires-mounts-for=mnt/high

https://bugzilla.redhat.com/show_bug.cgi?id=812826
https://bugzilla.redhat.com/show_bug.cgi?id=1164334
This commit is contained in:
Karel Zak 2015-05-18 12:30:37 +02:00 committed by Lennart Poettering
parent 06ee4910e4
commit 3519d230c8
4 changed files with 139 additions and 0 deletions

View File

@ -138,6 +138,36 @@
<variablelist class='fstab-options'>
<varlistentry>
<term><option>x-systemd.requires=</option></term>
<listitem><para>Configures a <varname>Requires=</varname> and
an <varname>After=</varname> dependency between the created
mount unit and another systemd unit, such as a device or mount
unit. The argument should be a unit name, or an absolute path
to a device node or mount point. This option may be specified
more than once. This option is particularly useful for mount
point declarations that need an additional device to be around
(such as an external journal device for journal file systems)
or an additional mount to be in place (such as an overlay file
system that merges multiple mount points). See
<varname>After=</varname> and <varname>Requires=</varname> in
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for details.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>x-systemd.requires-mounts-for=</option></term>
<listitem><para>Configures a
<varname>RequiresMountsFor=</varname> dependency between the
created mount unit and other mount units. The argument must be
an absolute path. This option may be specified more than once.
See <varname>RequiresMountsFor=</varname> in
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for details.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>x-systemd.automount</option></term>

View File

@ -177,6 +177,65 @@ static int write_idle_timeout(FILE *f, const char *where, const char *opts) {
return 0;
}
static int write_requires_after(FILE *f, const char *opts) {
_cleanup_strv_free_ char **names = NULL, **units = NULL;
_cleanup_free_ char *res = NULL;
char **s;
int r;
assert(f);
assert(opts);
r = fstab_extract_values(opts, "x-systemd.requires", &names);
if (r < 0)
return log_warning_errno(r, "Failed to parse options: %m");
if (r == 0)
return 0;
STRV_FOREACH(s, names) {
char *x;
r = unit_name_mangle_with_suffix(*s, UNIT_NAME_NOGLOB, ".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();
}
if (units) {
res = strv_join(units, " ");
if (!res)
return log_oom();
fprintf(f, "After=%1$s\nRequires=%1$s\n", res);
}
return 0;
}
static int write_requires_mounts_for(FILE *f, const char *opts) {
_cleanup_strv_free_ char **paths = NULL;
_cleanup_free_ char *res = NULL;
int r;
assert(f);
assert(opts);
r = fstab_extract_values(opts, "x-systemd.requires-mounts-for", &paths);
if (r < 0)
return log_warning_errno(r, "Failed to parse options: %m");
if (r == 0)
return 0;
res = strv_join(paths, " ");
if (!res)
return log_oom();
fprintf(f, "RequiresMountsFor=%s\n", res);
return 0;
}
static int add_mount(
const char *what,
const char *where,
@ -251,6 +310,15 @@ static int add_mount(
if (post && !noauto && !nofail && !automount)
fprintf(f, "Before=%s\n", post);
if (!automount && opts) {
r = write_requires_after(f, opts);
if (r < 0)
return r;
r = write_requires_mounts_for(f, opts);
if (r < 0)
return r;
}
if (passno != 0) {
r = generator_write_fsck_deps(f, arg_dest, what, where, fstype);
if (r < 0)
@ -315,6 +383,15 @@ static int add_mount(
"Before=%s\n",
post);
if (opts) {
r = write_requires_after(f, opts);
if (r < 0)
return r;
r = write_requires_mounts_for(f, opts);
if (r < 0)
return r;
}
fprintf(f,
"[Automount]\n"
"Where=%s\n",

View File

@ -125,6 +125,36 @@ answer:
return !!n;
}
int fstab_extract_values(const char *opts, const char *name, char ***values) {
_cleanup_strv_free_ char **optsv = NULL, **res = NULL;
char **s;
assert(opts);
assert(name);
assert(values);
optsv = strv_split(opts, ",");
if (!optsv)
return -ENOMEM;
STRV_FOREACH(s, optsv) {
char *arg;
int r;
arg = startswith(*s, name);
if (!arg || *arg != '=')
continue;
r = strv_extend(&res, arg + 1);
if (r < 0)
return r;
}
*values = res;
res = NULL;
return !!*values;
}
int fstab_find_pri(const char *options, int *ret) {
_cleanup_free_ char *opt = NULL;
int r;

View File

@ -28,6 +28,8 @@
int fstab_filter_options(const char *opts, const char *names,
const char **namefound, char **value, char **filtered);
int fstab_extract_values(const char *opts, const char *name, char ***values);
static inline bool fstab_test_option(const char *opts, const char *names) {
return !!fstab_filter_options(opts, names, NULL, NULL, NULL);
}