]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: make mount units from /proc/self/mountinfo possibly bind to a device (#4515)
authorFranck Bui <fbui@suse.com>
Fri, 16 Dec 2016 16:13:58 +0000 (17:13 +0100)
committerLennart Poettering <lennart@poettering.net>
Fri, 16 Dec 2016 16:13:58 +0000 (17:13 +0100)
Since commit 9d06297, mount units from mountinfo are not bound to their devices
anymore (they use the "Requires" dependency instead).

This has the following drawback: if a media is mounted and the eject button is
pressed then the media is unconditionally ejected leaving some inconsistent
states.

Since udev is the component that is reacting (no matter if the device is used
or not) to the eject button, users expect that udev at least try to unmount the
media properly.

This patch introduces a new property "SYSTEMD_MOUNT_DEVICE_BOUND". When set on
a block device, all units that requires this device will see their "Requires"
dependency upgraded to a "BindTo" one. This is currently only used by cdrom
devices.

This patch also gives the possibility to the user to restore the previous
behavior that is bind a mount unit to a device. This is achieved by passing the
"x-systemd.device-bound" option to mount(8). Please note that currently this is
not working because libmount treats the x-* options has comments therefore
they're not available in utab for later application retrievals.

man/systemd.mount.xml
rules/60-cdrom_id.rules
src/core/device.c
src/core/device.h
src/core/mount.c
src/core/unit.c

index 68ff6f8f1c9abeb916293bb56aa0d787735ed934..2117433bf043f3569dfc50e3a8b7d47be81ded4e 100644 (file)
         for details.</para></listitem>
        </varlistentry>
 
+      <varlistentry>
+        <term><option>x-systemd.device-bound</option></term>
+
+        <listitem><para>The block device backed file system will be upgraded
+        to <varname>BindsTo=</varname> dependency. This option is only useful
+        when mounting file systems manually with
+        <citerefentry><refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+        as the default dependency in this case is <varname>Requires=</varname>.
+        This option is already implied by entries in <filename>/etc/fstab</filename>
+        or by mount units.
+        </para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>x-systemd.automount</option></term>
 
index 5c3b52ebb9eebfd1e1e174bf5d9bb912c4592dcd..f91d8cb456f323b869264efe471fbd57850fbeef 100644 (file)
@@ -8,6 +8,10 @@ ENV{DEVTYPE}!="disk", GOTO="cdrom_end"
 # unconditionally tag device as CDROM
 KERNEL=="sr[0-9]*", ENV{ID_CDROM}="1"
 
+# stop automatically any mount units bound to the device if the media eject
+# button is pressed.
+ENV{ID_CDROM}=="1", ENV{SYSTEMD_MOUNT_DEVICE_BOUND}="1"
+
 # media eject button pressed
 ENV{DISK_EJECT_REQUEST}=="?*", RUN+="cdrom_id --eject-media $devnode", GOTO="cdrom_end"
 
index e345552f24935d67aa2a94a4e1b25050591036f3..bd481c8050db9f3485a4b2392248052db4b11308 100644 (file)
@@ -285,6 +285,37 @@ static int device_add_udev_wants(Unit *u, struct udev_device *dev) {
         }
 }
 
+static bool device_is_bound_by_mounts(Unit *d, struct udev_device *dev) {
+        const char *bound_by;
+        int r = false;
+
+        assert(d);
+        assert(dev);
+
+        bound_by = udev_device_get_property_value(dev, "SYSTEMD_MOUNT_DEVICE_BOUND");
+        if (bound_by)
+                r = parse_boolean(bound_by) > 0;
+
+        DEVICE(d)->bind_mounts = r;
+        return r;
+}
+
+static int device_upgrade_mount_deps(Unit *u) {
+        Unit *other;
+        Iterator i;
+        int r;
+
+        SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY], i) {
+                if (other->type != UNIT_MOUNT)
+                        continue;
+
+                r = unit_add_dependency(other, UNIT_BINDS_TO, u, true);
+                if (r < 0)
+                        return r;
+        }
+        return 0;
+}
+
 static int device_setup_unit(Manager *m, struct udev_device *dev, const char *path, bool main) {
         _cleanup_free_ char *e = NULL;
         const char *sysfs = NULL;
@@ -349,6 +380,13 @@ static int device_setup_unit(Manager *m, struct udev_device *dev, const char *pa
                         (void) device_add_udev_wants(u, dev);
         }
 
+        /* So the user wants the mount units to be bound to the device but a
+         * mount unit might has been seen by systemd before the device appears
+         * on its radar. In this case the device unit is partially initialized
+         * and includes the deps on the mount unit but at that time the "bind
+         * mounts" flag wasn't not present. Fix this up now. */
+        if (device_is_bound_by_mounts(u, dev))
+                device_upgrade_mount_deps(u);
 
         /* Note that this won't dispatch the load queue, the caller
          * has to do that if needed and appropriate */
@@ -824,6 +862,14 @@ int device_found_node(Manager *m, const char *node, bool add, DeviceFound found,
         return device_update_found_by_name(m, node, add, found, now);
 }
 
+bool device_shall_be_bound_by(Unit *device, Unit *u) {
+
+        if (u->type != UNIT_MOUNT)
+                return false;
+
+        return DEVICE(device)->bind_mounts;
+}
+
 const UnitVTable device_vtable = {
         .object_size = sizeof(Device),
         .sections =
index 184a1a349b269904872e3d87a2d350df0e12c98d..dd372fb695586285a5bbf5642b05b11dddaa4359 100644 (file)
@@ -40,8 +40,11 @@ struct Device {
         LIST_FIELDS(struct Device, same_sysfs);
 
         DeviceState state, deserialized_state;
+
+        bool bind_mounts;
 };
 
 extern const UnitVTable device_vtable;
 
 int device_found_node(Manager *m, const char *node, bool add, DeviceFound found, bool now);
+bool device_shall_be_bound_by(Unit *device, Unit *u);
index 997dbe383757c929fe629c96cc0160ddcd396b57..daf7f5697bc639501a5c0fa13bb3577a9bbdc284 100644 (file)
@@ -135,6 +135,16 @@ static bool mount_state_active(MountState state) {
                       MOUNT_REMOUNTING_SIGKILL);
 }
 
+static bool mount_is_bound_to_device(const Mount *m) {
+        const MountParameters *p;
+
+        if (m->from_fragment)
+                return true;
+
+        p = &m->parameters_proc_self_mountinfo;
+        return fstab_test_option(p->options, "x-systemd.device-bound\0");
+}
+
 static bool needs_quota(const MountParameters *p) {
         assert(p);
 
@@ -324,6 +334,7 @@ static int mount_add_mount_links(Mount *m) {
 static int mount_add_device_links(Mount *m) {
         MountParameters *p;
         bool device_wants_mount = false;
+        UnitDependency dep;
         int r;
 
         assert(m);
@@ -353,7 +364,14 @@ static int mount_add_device_links(Mount *m) {
         if (mount_is_auto(p) && !mount_is_automount(p) && MANAGER_IS_SYSTEM(UNIT(m)->manager))
                 device_wants_mount = true;
 
-        r = unit_add_node_link(UNIT(m), p->what, device_wants_mount, m->from_fragment ? UNIT_BINDS_TO : UNIT_REQUIRES);
+        /* 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;
+
+        r = unit_add_node_link(UNIT(m), p->what, device_wants_mount, dep);
         if (r < 0)
                 return r;
 
index ab4013573672792301e8150ce39ee663f4fe3058..5d0b17425b100b2df9033f0e8f8785cfad288f54 100644 (file)
@@ -3048,6 +3048,9 @@ int unit_add_node_link(Unit *u, const char *what, bool wants, UnitDependency dep
         if (r < 0)
                 return r;
 
+        if (dep == UNIT_REQUIRES && device_shall_be_bound_by(device, u))
+                dep = UNIT_BINDS_TO;
+
         r = unit_add_two_dependencies(u, UNIT_AFTER,
                                       MANAGER_IS_SYSTEM(u->manager) ? dep : UNIT_WANTS,
                                       device, true);