summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--man/org.freedesktop.systemd1.xml10
-rw-r--r--man/systemd.mount.xml16
-rw-r--r--src/core/dbus-mount.c25
-rw-r--r--src/core/load-fragment-gperf.gperf.in1
-rw-r--r--src/core/load-fragment.c45
-rw-r--r--src/core/load-fragment.h1
-rw-r--r--src/core/mount.c44
-rw-r--r--src/core/mount.h2
-rw-r--r--src/shared/bus-unit-util.c19
-rwxr-xr-xtest/units/TEST-74-AUX-UTILS.mount.sh7
10 files changed, 161 insertions, 9 deletions
diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml
index 1fc925fe4a..0664d026f1 100644
--- a/man/org.freedesktop.systemd1.xml
+++ b/man/org.freedesktop.systemd1.xml
@@ -7050,6 +7050,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
readonly s Result = '...';
readonly u UID = ...;
readonly u GID = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly as GracefulOptions = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates")
readonly a(sasbttttuii) ExecMount = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates")
@@ -7654,6 +7656,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
<!--property GID is not documented!-->
+ <!--property GracefulOptions is not documented!-->
+
<!--property ExecUnmount is not documented!-->
<!--property ExecRemount is not documented!-->
@@ -8200,6 +8204,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
<variablelist class="dbus-property" generated="True" extra-ref="GID"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="GracefulOptions"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="ExecMount"/>
<variablelist class="dbus-property" generated="True" extra-ref="ExecUnmount"/>
@@ -12455,7 +12461,9 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
<varname>ManagedOOMMemoryPressureDurationUSec</varname>,
<varname>ProtectControlGroupsEx</varname>, and
<varname>PrivatePIDs</varname> were added in version 257.</para>
- <para><varname>ProtectHostnameEx</varname> and <function>RemoveSubgroup()</function> was added in version 258.</para>
+ <para><varname>ProtectHostnameEx</varname>,
+ <function>RemoveSubgroup()</function>, and
+ <varname>GracefulOptions</varname> were added in version 258.</para>
</refsect2>
<refsect2>
<title>Swap Unit Objects</title>
diff --git a/man/systemd.mount.xml b/man/systemd.mount.xml
index 3043e90499..991c1f8506 100644
--- a/man/systemd.mount.xml
+++ b/man/systemd.mount.xml
@@ -650,6 +650,22 @@
<citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para></listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><varname>GracefulOptions=</varname></term>
+
+ <listitem><para>Additional mount options that shall be appended to <varname>Options=</varname> if
+ supported by the kernel. This may be used to configure mount options that are optional and only
+ enabled on kernels that support them. Note that this is supported only for native kernel mount
+ options (i.e. explicitly not for mount options implemented in userspace, such as those processed by
+ <command>/usr/bin/mount</command> itself, by FUSE or by mount helpers such as
+ <command>mount.nfs</command>).</para>
+
+ <para>May be specified multiple times. If specified multiple times, all listed, supported mount
+ options are combined. If an empty string is assigned, the list is reset.</para>
+
+ <xi:include href="version-info.xml" xpointer="v258"/></listitem>
+ </varlistentry>
</variablelist>
<xi:include href="systemd.service.xml" xpointer="shared-unit-options" />
diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c
index d59aa06a11..855300d025 100644
--- a/src/core/dbus-mount.c
+++ b/src/core/dbus-mount.c
@@ -75,6 +75,7 @@ const sd_bus_vtable bus_mount_vtable[] = {
SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Mount, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("UID", "u", bus_property_get_uid, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("GID", "u", bus_property_get_gid, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_PROPERTY("GracefulOptions", "as", NULL, offsetof(Mount, graceful_options), SD_BUS_VTABLE_PROPERTY_CONST),
BUS_EXEC_COMMAND_VTABLE("ExecMount", offsetof(Mount, exec_command[MOUNT_EXEC_MOUNT]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_VTABLE("ExecUnmount", offsetof(Mount, exec_command[MOUNT_EXEC_UNMOUNT]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_VTABLE("ExecRemount", offsetof(Mount, exec_command[MOUNT_EXEC_REMOUNT]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
@@ -150,6 +151,30 @@ static int bus_mount_set_transient_property(
if (streq(name, "ReadWriteOnly"))
return bus_set_transient_bool(u, name, &m->read_write_only, message, flags, error);
+ if (streq(name, "GracefulOptions")) {
+ _cleanup_strv_free_ char **add = NULL;
+ r = sd_bus_message_read_strv(message, &add);
+ if (r < 0)
+ return r;
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+
+ if (strv_isempty(add)) {
+ m->graceful_options = strv_free(m->graceful_options);
+ unit_write_settingf(u, flags, name, "GracefulOptions=");
+ } else {
+ r = strv_extend_strv(&m->graceful_options, add, /* filter_duplicates= */ false);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(a, add)
+ unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "GracefulOptions=%s", *a);
+ }
+ }
+
+ return 1;
+ }
+
return 0;
}
diff --git a/src/core/load-fragment-gperf.gperf.in b/src/core/load-fragment-gperf.gperf.in
index 04a560110e..1033d08103 100644
--- a/src/core/load-fragment-gperf.gperf.in
+++ b/src/core/load-fragment-gperf.gperf.in
@@ -548,6 +548,7 @@ Mount.SloppyOptions, config_parse_bool,
Mount.LazyUnmount, config_parse_bool, 0, offsetof(Mount, lazy_unmount)
Mount.ForceUnmount, config_parse_bool, 0, offsetof(Mount, force_unmount)
Mount.ReadWriteOnly, config_parse_bool, 0, offsetof(Mount, read_write_only)
+Mount.GracefulOptions, config_parse_mount_graceful_options, 0, offsetof(Mount, graceful_options)
{{ EXEC_CONTEXT_CONFIG_ITEMS('Mount') }}
{{ CGROUP_CONTEXT_CONFIG_ITEMS('Mount') }}
{{ KILL_CONTEXT_CONFIG_ITEMS('Mount') }}
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index c8fff60108..fe60f27259 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -6123,6 +6123,51 @@ int config_parse_mount_node(
return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, path, data, userdata);
}
+int config_parse_mount_graceful_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) {
+
+ const Unit *u = ASSERT_PTR(userdata);
+ char ***sv = ASSERT_PTR(data);
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ if (isempty(rvalue)) {
+ *sv = strv_free(*sv);
+ return 1;
+ }
+
+ _cleanup_free_ char *resolved = NULL;
+ r = unit_full_printf(u, rvalue, &resolved);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
+ return 0;
+ }
+
+ _cleanup_strv_free_ char **strv = NULL;
+
+ r = strv_split_full(&strv, resolved, ",", EXTRACT_RETAIN_ESCAPE|EXTRACT_UNESCAPE_SEPARATORS);
+ if (r < 0)
+ return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
+
+ r = strv_extend_strv_consume(sv, TAKE_PTR(strv), /* filter_duplicates = */ false);
+ if (r < 0)
+ return log_oom();
+
+ return 1;
+}
+
static int merge_by_names(Unit *u, Set *names, const char *id) {
char *k;
int r;
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index 881ce152d5..28275af8d8 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -165,6 +165,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_open_file);
CONFIG_PARSER_PROTOTYPE(config_parse_memory_pressure_watch);
CONFIG_PARSER_PROTOTYPE(config_parse_cgroup_nft_set);
CONFIG_PARSER_PROTOTYPE(config_parse_mount_node);
+CONFIG_PARSER_PROTOTYPE(config_parse_mount_graceful_options);
/* gperf prototypes */
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
diff --git a/src/core/mount.c b/src/core/mount.c
index 4b47092c1d..073e9c7193 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -231,6 +231,8 @@ static void mount_done(Unit *u) {
mount_unwatch_control_pid(m);
m->timer_event_source = sd_event_source_disable_unref(m->timer_event_source);
+
+ m->graceful_options = strv_free(m->graceful_options);
}
static int update_parameters_proc_self_mountinfo(
@@ -1079,6 +1081,44 @@ fail:
mount_enter_dead_or_mounted(m, MOUNT_FAILURE_RESOURCES, /* flush_result = */ false);
}
+static int mount_append_graceful_options(Mount *m, const MountParameters *p, char **opts) {
+ int r;
+
+ assert(m);
+ assert(p);
+ assert(opts);
+
+ if (strv_isempty(m->graceful_options))
+ return 0;
+
+ if (!p->fstype) {
+ log_unit_warning(UNIT(m), "GracefulOptions= used but file system type not known, suppressing all graceful options.");
+ return 0;
+ }
+
+ STRV_FOREACH(o, m->graceful_options) {
+ _cleanup_free_ char *k = NULL, *v = NULL;
+
+ r = split_pair(*o, "=", &k, &v);
+ if (r < 0 && r != -EINVAL) /* EINVAL → not a key/value pair */
+ return r;
+
+ r = mount_option_supported(p->fstype, k ?: *o, v);
+ if (r < 0)
+ log_unit_warning_errno(UNIT(m), r, "GracefulOptions=%s specified, but cannot determine availability, suppressing.", *o);
+ else if (r == 0)
+ log_unit_info(UNIT(m), "GracefulOptions=%s specified, but option is not available, suppressing.", *o);
+ else {
+ log_unit_debug(UNIT(m), "GracefulOptions=%s specified and supported, appending to mount option string.", *o);
+
+ if (!strextend_with_separator(opts, ",", *o))
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
static int mount_set_mount_command(Mount *m, ExecCommand *c, const MountParameters *p) {
int r;
@@ -1113,6 +1153,10 @@ static int mount_set_mount_command(Mount *m, ExecCommand *c, const MountParamete
if (r < 0)
return r;
+ r = mount_append_graceful_options(m, p, &opts);
+ if (r < 0)
+ return r;
+
if (!isempty(opts)) {
r = exec_command_append(c, "-o", opts, NULL);
if (r < 0)
diff --git a/src/core/mount.h b/src/core/mount.h
index 9a8b6bb4c0..28cc7785d8 100644
--- a/src/core/mount.h
+++ b/src/core/mount.h
@@ -88,6 +88,8 @@ struct Mount {
sd_event_source *timer_event_source;
unsigned n_retry_umount;
+
+ char **graceful_options;
};
extern const UnitVTable mount_vtable;
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
index 1b6c6f0566..ed73950355 100644
--- a/src/shared/bus-unit-util.c
+++ b/src/shared/bus-unit-util.c
@@ -147,7 +147,7 @@ static int bus_append_string(sd_bus_message *m, const char *field, const char *e
return 1;
}
-static int bus_append_strv(sd_bus_message *m, const char *field, const char *eq, ExtractFlags flags) {
+static int bus_append_strv(sd_bus_message *m, const char *field, const char *eq, const char *separator, ExtractFlags flags) {
const char *p;
int r;
@@ -170,7 +170,7 @@ static int bus_append_strv(sd_bus_message *m, const char *field, const char *eq,
for (p = eq;;) {
_cleanup_free_ char *word = NULL;
- r = extract_first_word(&p, &word, NULL, flags);
+ r = extract_first_word(&p, &word, separator, flags);
if (r == 0)
break;
if (r == -ENOMEM)
@@ -613,12 +613,12 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
return bus_append_cg_blkio_weight_parse(m, field, eq);
if (streq(field, "DisableControllers"))
- return bus_append_strv(m, "DisableControllers", eq, EXTRACT_UNQUOTE);
+ return bus_append_strv(m, "DisableControllers", eq, /* separator= */ NULL, EXTRACT_UNQUOTE);
if (streq(field, "Delegate")) {
r = parse_boolean(eq);
if (r < 0)
- return bus_append_strv(m, "DelegateControllers", eq, EXTRACT_UNQUOTE);
+ return bus_append_strv(m, "DelegateControllers", eq, /* separator= */ NULL, EXTRACT_UNQUOTE);
r = sd_bus_message_append(m, "(sv)", "Delegate", "b", r);
if (r < 0)
@@ -1116,7 +1116,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
"ConfigurationDirectory",
"SupplementaryGroups",
"SystemCallArchitectures"))
- return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE);
+ return bus_append_strv(m, field, eq, /* separator= */ NULL, EXTRACT_UNQUOTE);
if (STR_IN_SET(field, "SyslogLevel",
"LogLevelMax"))
@@ -1175,7 +1175,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
if (STR_IN_SET(field, "Environment",
"UnsetEnvironment",
"PassEnvironment"))
- return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE);
+ return bus_append_strv(m, field, eq, /* separator= */ NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE);
if (streq(field, "EnvironmentFile")) {
if (isempty(eq))
@@ -2333,6 +2333,9 @@ static int bus_append_mount_property(sd_bus_message *m, const char *field, const
"ReadwriteOnly"))
return bus_append_parse_boolean(m, field, eq);
+ if (streq(field, "GracefulOptions"))
+ return bus_append_strv(m, field, eq, /* separator= */ ",", 0);
+
return 0;
}
@@ -2615,7 +2618,7 @@ static int bus_append_socket_property(sd_bus_message *m, const char *field, cons
return bus_append_string(m, field, eq);
if (streq(field, "Symlinks"))
- return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE);
+ return bus_append_strv(m, field, eq, /* separator= */ NULL, EXTRACT_UNQUOTE);
if (streq(field, "SocketProtocol"))
return bus_append_parse_ip_protocol(m, field, eq);
@@ -2749,7 +2752,7 @@ static int bus_append_unit_property(sd_bus_message *m, const char *field, const
"RequiresMountsFor",
"WantsMountsFor",
"Markers"))
- return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE);
+ return bus_append_strv(m, field, eq, /* separator= */ NULL, EXTRACT_UNQUOTE);
t = condition_type_from_string(field);
if (t >= 0)
diff --git a/test/units/TEST-74-AUX-UTILS.mount.sh b/test/units/TEST-74-AUX-UTILS.mount.sh
index 89a391d48e..0700b43063 100755
--- a/test/units/TEST-74-AUX-UTILS.mount.sh
+++ b/test/units/TEST-74-AUX-UTILS.mount.sh
@@ -185,3 +185,10 @@ systemctl status "$WORK_DIR/mnt"
touch "$WORK_DIR/mnt/hello"
[[ "$(stat -c "%U:%G" "$WORK_DIR/mnt/hello")" == "testuser:testuser" ]]
systemd-umount LABEL=owner-vfat
+
+# Mkae sure that graceful mount options work
+GRACEFULTEST="/tmp/graceful/$RANDOM"
+systemd-mount --tmpfs -p GracefulOptions=idefinitelydontexist,nr_inodes=4711,idonexisteither "$GRACEFULTEST"
+findmnt -n -o options "$GRACEFULTEST"
+findmnt -n -o options "$GRACEFULTEST" | grep -q nr_inodes=4711
+umount "$GRACEFULTEST"