diff --git a/docs/TRANSIENT-SETTINGS.md b/docs/TRANSIENT-SETTINGS.md
index d5a8046676..d9b1c20c77 100644
--- a/docs/TRANSIENT-SETTINGS.md
+++ b/docs/TRANSIENT-SETTINGS.md
@@ -332,6 +332,7 @@ All mount unit settings are available to transient units:
✓ SloppyOptions=
✓ LazyUnmount=
✓ ForceUnmount=
+✓ ReadWriteOnly=
```
## Automount Unit Settings
diff --git a/man/systemd.mount.xml b/man/systemd.mount.xml
index 4200a9ed7e..07feccb54a 100644
--- a/man/systemd.mount.xml
+++ b/man/systemd.mount.xml
@@ -359,6 +359,17 @@
Options= setting in a unit file.
+
+
+
+ If a mount operation fails to mount the file system
+ read-write, it normally tries mounting the file system read-only instead.
+ This option disables that behaviour, and causes the mount to fail
+ immediately instead. This option is translated into the
+ ReadWriteOnly= setting in a unit file.
+
+
+
@@ -497,6 +508,19 @@
off.
+
+ ReadWriteOnly=
+
+ Takes a boolean argument. If false, a mount
+ point that shall be mounted read-write but cannot be mounted
+ so is retried to be mounted read-only. If true the operation
+ will fail immediately after the read-write mount attempt did
+ not succeed. This corresponds with
+ mount8's
+ -w switch. Defaults to
+ off.
+
+
ForceUnmount=
diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c
index b6d61627eb..3ab5ecc425 100644
--- a/src/core/dbus-mount.c
+++ b/src/core/dbus-mount.c
@@ -51,6 +51,7 @@ const sd_bus_vtable bus_mount_vtable[] = {
SD_BUS_PROPERTY("SloppyOptions", "b", bus_property_get_bool, offsetof(Mount, sloppy_options), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("LazyUnmount", "b", bus_property_get_bool, offsetof(Mount, lazy_unmount), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ForceUnmount", "b", bus_property_get_bool, offsetof(Mount, force_unmount), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("ReadWriteOnly", "b", bus_property_get_bool, offsetof(Mount, read_write_only), SD_BUS_VTABLE_PROPERTY_CONST),
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),
@@ -102,6 +103,9 @@ static int bus_mount_set_transient_property(
if (streq(name, "ForceUnmount"))
return bus_set_transient_bool(u, name, &m->force_unmount, message, flags, error);
+ if (streq(name, "ReadWriteOnly"))
+ return bus_set_transient_bool(u, name, &m->read_write_only, message, flags, error);
+
return 0;
}
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 165b9ca9c1..5fd58b379b 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -429,6 +429,7 @@ Mount.DirectoryMode, config_parse_mode, 0,
Mount.SloppyOptions, config_parse_bool, 0, offsetof(Mount, sloppy_options)
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)
EXEC_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl
CGROUP_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl
KILL_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl
diff --git a/src/core/mount.c b/src/core/mount.c
index 5463196f69..48500b4932 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -780,6 +780,7 @@ static void mount_dump(Unit *u, FILE *f, const char *prefix) {
"%sSloppyOptions: %s\n"
"%sLazyUnmount: %s\n"
"%sForceUnmount: %s\n"
+ "%sReadWriteOnly: %s\n"
"%sTimeoutSec: %s\n",
prefix, mount_state_to_string(m->state),
prefix, mount_result_to_string(m->result),
@@ -795,6 +796,7 @@ static void mount_dump(Unit *u, FILE *f, const char *prefix) {
prefix, yes_no(m->sloppy_options),
prefix, yes_no(m->lazy_unmount),
prefix, yes_no(m->force_unmount),
+ prefix, yes_no(m->read_write_only),
prefix, format_timespan(buf, sizeof(buf), m->timeout_usec, USEC_PER_SEC));
if (m->control_pid > 0)
@@ -1026,6 +1028,8 @@ static void mount_enter_mounting(Mount *m) {
r = exec_command_set(m->control_command, MOUNT_PATH, p->what, m->where, NULL);
if (r >= 0 && m->sloppy_options)
r = exec_command_append(m->control_command, "-s", NULL);
+ if (r >= 0 && m->read_write_only)
+ r = exec_command_append(m->control_command, "-w", NULL);
if (r >= 0 && p->fstype)
r = exec_command_append(m->control_command, "-t", p->fstype, NULL);
if (r >= 0 && !isempty(opts))
@@ -1086,6 +1090,8 @@ static void mount_enter_remounting(Mount *m) {
"-o", o, NULL);
if (r >= 0 && m->sloppy_options)
r = exec_command_append(m->control_command, "-s", NULL);
+ if (r >= 0 && m->read_write_only)
+ r = exec_command_append(m->control_command, "-w", NULL);
if (r >= 0 && p->fstype)
r = exec_command_append(m->control_command, "-t", p->fstype, NULL);
} else
diff --git a/src/core/mount.h b/src/core/mount.h
index 07fa05f3ca..a1bc2d71a6 100644
--- a/src/core/mount.h
+++ b/src/core/mount.h
@@ -59,6 +59,8 @@ struct Mount {
bool lazy_unmount;
bool force_unmount;
+ bool read_write_only;
+
MountResult result;
MountResult reload_result;
MountResult clean_result;
diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c
index 2b3e9ec7cb..36a8ff23d4 100644
--- a/src/fstab-generator/fstab-generator.c
+++ b/src/fstab-generator/fstab-generator.c
@@ -35,6 +35,7 @@ typedef enum MountpointFlags {
AUTOMOUNT = 1 << 2,
MAKEFS = 1 << 3,
GROWFS = 1 << 4,
+ RWONLY = 1 << 5,
} MountpointFlags;
static const char *arg_dest = NULL;
@@ -472,6 +473,9 @@ static int add_mount(
if (r < 0)
return r;
+ if (flags & RWONLY)
+ fprintf(f, "ReadWriteOnly=yes\n");
+
r = fflush_and_check(f);
if (r < 0)
return log_error_errno(r, "Failed to write unit file %s: %m", name);
@@ -583,7 +587,7 @@ static int parse_fstab(bool initrd) {
while ((me = getmntent(f))) {
_cleanup_free_ char *where = NULL, *what = NULL, *canonical_where = NULL;
- bool makefs, growfs, noauto, nofail;
+ bool makefs, growfs, noauto, nofail, rwonly;
int k;
if (initrd && !mount_in_initrd(me))
@@ -623,6 +627,7 @@ static int parse_fstab(bool initrd) {
makefs = fstab_test_option(me->mnt_opts, "x-systemd.makefs\0");
growfs = fstab_test_option(me->mnt_opts, "x-systemd.growfs\0");
+ rwonly = fstab_test_option(me->mnt_opts, "x-systemd.rw-only\0");
noauto = fstab_test_yes_no_option(me->mnt_opts, "noauto\0" "auto\0");
nofail = fstab_test_yes_no_option(me->mnt_opts, "nofail\0" "fail\0");
@@ -655,7 +660,7 @@ static int parse_fstab(bool initrd) {
me->mnt_type,
me->mnt_opts,
me->mnt_passno,
- makefs*MAKEFS | growfs*GROWFS | noauto*NOAUTO | nofail*NOFAIL | automount*AUTOMOUNT,
+ makefs*MAKEFS | growfs*GROWFS | noauto*NOAUTO | nofail*NOFAIL | automount*AUTOMOUNT | rwonly*RWONLY,
post,
fstab);
}
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
index 463a0ddb71..3be75e6b4d 100644
--- a/src/shared/bus-unit-util.c
+++ b/src/shared/bus-unit-util.c
@@ -1436,7 +1436,8 @@ static int bus_append_mount_property(sd_bus_message *m, const char *field, const
if (STR_IN_SET(field, "SloppyOptions",
"LazyUnmount",
- "ForceUnmount"))
+ "ForceUnmount",
+ "ReadwriteOnly"))
return bus_append_parse_boolean(m, field, eq);
return 0;
diff --git a/test/fuzz/fuzz-unit-file/directives.service b/test/fuzz/fuzz-unit-file/directives.service
index 98cddad349..6fa96e1d58 100644
--- a/test/fuzz/fuzz-unit-file/directives.service
+++ b/test/fuzz/fuzz-unit-file/directives.service
@@ -856,6 +856,7 @@ RateLimitIntervalSec=
ReadKMsg=
ReadOnly=
ReadOnlyPaths=
+ReadWriteOnly=
ReadWritePaths=
RemoveIPC=
ReserveVT=