mount: use libmount to monitor mountinfo & utab
authorKarel Zak <kzak@redhat.com>
Mon, 1 Jun 2015 11:48:01 +0000 (13:48 +0200)
committerKarel Zak <kzak@redhat.com>
Mon, 14 Sep 2015 07:12:31 +0000 (09:12 +0200)
The current implementation directly monitor /proc/self/mountinfo and
/run/mount/utab files. It's really not optimal because utab file is
private libmount stuff without any official guaranteed semantic.

The libmount since v2.26 provides API to monitor mount kernel &
userspace changes and since v2.27 the monitor is usable for
non-root users too.

This patch replaces the current implementation with libmount based
solution.

Signed-off-by: Karel Zak <kzak@redhat.com>
Makefile.am
README
configure.ac
src/core/manager.c
src/core/manager.h
src/core/mount.c

index c395840..57dfee7 100644 (file)
@@ -1327,7 +1327,8 @@ systemd_SOURCES = \
 
 systemd_CFLAGS = \
        $(AM_CFLAGS) \
-       $(SECCOMP_CFLAGS)
+       $(SECCOMP_CFLAGS) \
+       $(MOUNT_CFLAGS)
 
 systemd_LDADD = \
        libcore.la
@@ -1532,7 +1533,8 @@ test_engine_SOURCES = \
 
 test_engine_CFLAGS = \
        $(AM_CFLAGS) \
-       $(SECCOMP_CFLAGS)
+       $(SECCOMP_CFLAGS) \
+       $(MOUNT_CFLAGS)
 
 test_engine_LDADD = \
        libcore.la
@@ -1542,7 +1544,8 @@ test_job_type_SOURCES = \
 
 test_job_type_CFLAGS = \
        $(AM_CFLAGS) \
-       $(SECCOMP_CFLAGS)
+       $(SECCOMP_CFLAGS) \
+       $(MOUNT_CFLAGS)
 
 test_job_type_LDADD = \
        libcore.la
@@ -1592,7 +1595,8 @@ test_unit_name_SOURCES = \
 
 test_unit_name_CFLAGS = \
        $(AM_CFLAGS) \
-       $(SECCOMP_CFLAGS)
+       $(SECCOMP_CFLAGS) \
+       $(MOUNT_CFLAGS)
 
 test_unit_name_LDADD = \
        libcore.la
@@ -1602,7 +1606,8 @@ test_unit_file_SOURCES = \
 
 test_unit_file_CFLAGS = \
        $(AM_CFLAGS) \
-       $(SECCOMP_CFLAGS)
+       $(SECCOMP_CFLAGS) \
+       $(MOUNT_CFLAGS)
 
 test_unit_file_LDADD = \
        libcore.la
@@ -1811,7 +1816,8 @@ test_tables_CPPFLAGS = \
 
 test_tables_CFLAGS = \
        $(AM_CFLAGS) \
-       $(SECCOMP_CFLAGS)
+       $(SECCOMP_CFLAGS) \
+       $(MOUNT_CFLAGS)
 
 test_tables_LDADD = \
        libjournal-core.la \
@@ -1937,7 +1943,8 @@ test_cgroup_mask_SOURCES = \
        src/test/test-cgroup-mask.c
 
 test_cgroup_mask_CPPFLAGS = \
-       $(AM_CPPFLAGS)
+       $(AM_CPPFLAGS) \
+       $(MOUNT_CFLAGS)
 
 test_cgroup_mask_CFLAGS = \
        $(AM_CFLAGS) \
@@ -1980,7 +1987,8 @@ test_path_SOURCES = \
        src/test/test-path.c
 
 test_path_CFLAGS = \
-       $(AM_CFLAGS)
+       $(AM_CFLAGS) \
+       $(MOUNT_CFLAGS)
 
 test_path_LDADD = \
        libcore.la
@@ -1989,7 +1997,8 @@ test_execute_SOURCES = \
        src/test/test-execute.c
 
 test_execute_CFLAGS = \
-       $(AM_CFLAGS)
+       $(AM_CFLAGS) \
+       $(MOUNT_CFLAGS)
 
 test_execute_LDADD = \
        libcore.la
@@ -2016,7 +2025,8 @@ test_sched_prio_SOURCES = \
        src/test/test-sched-prio.c
 
 test_sched_prio_CPPFLAGS = \
-       $(AM_CPPFLAGS)
+       $(AM_CPPFLAGS) \
+       $(MOUNT_CFLAGS)
 
 test_sched_prio_CFLAGS = \
        $(AM_CFLAGS) \
@@ -2103,7 +2113,8 @@ systemd_analyze_SOURCES = \
 
 systemd_analyze_CFLAGS = \
        $(AM_CFLAGS) \
-       $(SECCOMP_CFLAGS)
+       $(SECCOMP_CFLAGS) \
+       $(MOUNT_CFLAGS)
 
 systemd_analyze_LDADD = \
        libcore.la
diff --git a/README b/README
index b9a89f5..f6fb966 100644 (file)
--- a/README
+++ b/README
@@ -122,7 +122,7 @@ REQUIREMENTS:
 
         glibc >= 2.16
         libcap
-        libmount >= 2.20 (from util-linux)
+        libmount >= 2.27 (from util-linux)
         libseccomp >= 1.0.0 (optional)
         libblkid >= 2.24 (from util-linux) (optional)
         libkmod >= 15 (optional)
@@ -144,7 +144,7 @@ REQUIREMENTS:
         During runtime, you need the following additional
         dependencies:
 
-        util-linux >= v2.26 required
+        util-linux >= v2.27 required
         dbus >= 1.4.0 (strictly speaking optional, but recommended)
         dracut (optional)
         PolicyKit (optional)
index aad6782..d71cb07 100644 (file)
@@ -427,7 +427,7 @@ AM_CONDITIONAL(HAVE_BLKID, [test "$have_blkid" = "yes"])
 
 # ------------------------------------------------------------------------------
 have_libmount=no
-PKG_CHECK_MODULES(MOUNT, [ mount >= 2.20 ],
+PKG_CHECK_MODULES(MOUNT, [ mount >= 2.27 ],
         [AC_DEFINE(HAVE_LIBMOUNT, 1, [Define if libmount is available]) have_libmount=yes], have_libmount=no)
 if test "x$have_libmount" = xno; then
         AC_MSG_ERROR([*** libmount support required but libraries not found])
index 8e518fb..bb10090 100644 (file)
@@ -571,8 +571,8 @@ int manager_new(ManagerRunningAs running_as, bool test_run, Manager **_m) {
         m->idle_pipe[0] = m->idle_pipe[1] = m->idle_pipe[2] = m->idle_pipe[3] = -1;
 
         m->pin_cgroupfs_fd = m->notify_fd = m->signal_fd = m->time_change_fd =
-                m->dev_autofs_fd = m->private_listen_fd = m->kdbus_fd = m->utab_inotify_fd =
-                m->cgroup_inotify_fd = -1;
+                m->dev_autofs_fd = m->private_listen_fd = m->kdbus_fd = m->cgroup_inotify_fd = -1;
+
         m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */
 
         m->ask_password_inotify_fd = -1;
index 5cf0dbd..7b896f9 100644 (file)
@@ -23,6 +23,7 @@
 
 #include <stdbool.h>
 #include <stdio.h>
+#include <libmount.h>
 
 #include "sd-bus.h"
 #include "sd-event.h"
@@ -176,10 +177,8 @@ struct Manager {
         Hashmap *devices_by_sysfs;
 
         /* Data specific to the mount subsystem */
-        FILE *proc_self_mountinfo;
+        struct libmnt_monitor *mount_monitor;
         sd_event_source *mount_event_source;
-        int utab_inotify_fd;
-        sd_event_source *mount_utab_event_source;
 
         /* Data specific to the swap filesystem */
         FILE *proc_swaps;
index 1f02aa5..e7aae6e 100644 (file)
@@ -23,8 +23,6 @@
 #include <stdio.h>
 #include <sys/epoll.h>
 #include <signal.h>
-#include <libmount.h>
-#include <sys/inotify.h>
 
 #include "manager.h"
 #include "unit.h"
@@ -1535,13 +1533,13 @@ static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) {
 }
 
 static void mount_shutdown(Manager *m) {
+
         assert(m);
 
         m->mount_event_source = sd_event_source_unref(m->mount_event_source);
-        m->mount_utab_event_source = sd_event_source_unref(m->mount_utab_event_source);
 
-        m->proc_self_mountinfo = safe_fclose(m->proc_self_mountinfo);
-        m->utab_inotify_fd = safe_close(m->utab_inotify_fd);
+        mnt_unref_monitor(m->mount_monitor);
+        m->mount_monitor = NULL;
 }
 
 static int mount_get_timeout(Unit *u, uint64_t *timeout) {
@@ -1560,53 +1558,41 @@ static int mount_get_timeout(Unit *u, uint64_t *timeout) {
 
 static int mount_enumerate(Manager *m) {
         int r;
+
         assert(m);
 
         mnt_init_debug(0);
 
-        if (!m->proc_self_mountinfo) {
-                m->proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
-                if (!m->proc_self_mountinfo)
-                        return -errno;
+        if (!m->mount_monitor) {
+                int fd;
 
-                r = sd_event_add_io(m->event, &m->mount_event_source, fileno(m->proc_self_mountinfo), EPOLLPRI, mount_dispatch_io, m);
-                if (r < 0)
+                m->mount_monitor = mnt_new_monitor();
+                if (!m->mount_monitor) {
+                        r = -ENOMEM;
                         goto fail;
+                }
 
-                /* Dispatch this before we dispatch SIGCHLD, so that
-                 * we always get the events from /proc/self/mountinfo
-                 * before the SIGCHLD of /usr/bin/mount. */
-                r = sd_event_source_set_priority(m->mount_event_source, -10);
+                r = mnt_monitor_enable_kernel(m->mount_monitor, 1);
                 if (r < 0)
                         goto fail;
-
-                (void) sd_event_source_set_description(m->mount_event_source, "mount-mountinfo-dispatch");
-        }
-
-        if (m->utab_inotify_fd < 0) {
-                m->utab_inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
-                if (m->utab_inotify_fd < 0) {
-                        r = -errno;
+                r = mnt_monitor_enable_userspace(m->mount_monitor, 1, NULL);
+                if (r < 0)
                         goto fail;
-                }
-
-                (void) mkdir_p_label("/run/mount", 0755);
 
-                r = inotify_add_watch(m->utab_inotify_fd, "/run/mount", IN_MOVED_TO);
-                if (r < 0) {
-                        r = -errno;
+                /* mnt_unref_monitor() will close the fd */
+                fd = r = mnt_monitor_get_fd(m->mount_monitor);
+                if (r < 0)
                         goto fail;
-                }
 
-                r = sd_event_add_io(m->event, &m->mount_utab_event_source, m->utab_inotify_fd, EPOLLIN, mount_dispatch_io, m);
+                r = sd_event_add_io(m->event, &m->mount_event_source, fd, EPOLLIN, mount_dispatch_io, m);
                 if (r < 0)
                         goto fail;
 
-                r = sd_event_source_set_priority(m->mount_utab_event_source, -10);
+                r = sd_event_source_set_priority(m->mount_event_source, -10);
                 if (r < 0)
                         goto fail;
 
-                (void) sd_event_source_set_description(m->mount_utab_event_source, "mount-utab-dispatch");
+                (void) sd_event_source_set_description(m->mount_event_source, "mount-monitor-dispatch");
         }
 
         r = mount_load_proc_self_mountinfo(m, false);
@@ -1629,45 +1615,27 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
         int r;
 
         assert(m);
-        assert(revents & (EPOLLPRI | EPOLLIN));
-
-        /* The manager calls this for every fd event happening on the
-         * /proc/self/mountinfo file, which informs us about mounting
-         * table changes, and for /run/mount events which we watch
-         * for mount options. */
+        assert(revents & EPOLLIN);
 
-        if (fd == m->utab_inotify_fd) {
+        if (fd == mnt_monitor_get_fd(m->mount_monitor)) {
                 bool rescan = false;
 
-                /* FIXME: We *really* need to replace this with
-                 * libmount's own API for this, we should not hardcode
-                 * internal behaviour of libmount here. */
-
-                for (;;) {
-                        union inotify_event_buffer buffer;
-                        struct inotify_event *e;
-                        ssize_t l;
-
-                        l = read(fd, &buffer, sizeof(buffer));
-                        if (l < 0) {
-                                if (errno == EAGAIN || errno == EINTR)
-                                        break;
-
-                                log_error_errno(errno, "Failed to read utab inotify: %m");
-                                break;
-                        }
-
-                        FOREACH_INOTIFY_EVENT(e, buffer, l) {
-                                /* Only care about changes to utab,
-                                 * but we have to monitor the
-                                 * directory to reliably get
-                                 * notifications about when utab is
-                                 * replaced using rename(2) */
-                                if ((e->mask & IN_Q_OVERFLOW) || streq(e->name, "utab"))
-                                        rescan = true;
-                        }
-                }
-
+                /* Drain all events and verify that the event is valid.
+                 *
+                 * Note that libmount also monitors /run/mount mkdir if the
+                 * directory does not exist yet. The mkdir may generate event
+                 * which is irrelevant for us.
+                 *
+                 * error: r < 0; valid: r == 0, false positive: rc == 1 */
+                do {
+                        r = mnt_monitor_next_change(m->mount_monitor, NULL, NULL);
+                        if (r == 0)
+                                rescan = true;
+                        else if (r < 0)
+                                return log_error_errno(r, "Failed to drain libmount events");
+                } while (r == 0);
+
+                log_debug("libmount event [rescan: %s]", yes_no(rescan));
                 if (!rescan)
                         return 0;
         }