84b277
From 3c567125dd6e3f0f3a460d79701554ea4198c07b Mon Sep 17 00:00:00 2001
84b277
From: Lukas Nykryn <lnykryn@redhat.com>
84b277
Date: Thu, 18 Dec 2014 14:13:26 +0100
84b277
Subject: [PATCH] mount: monitor for utab changes with inotify
84b277
84b277
Parsing the mount table with libmount races against the mount command,
84b277
which will handle the actual mounting before updating utab.  This means
84b277
the poll event on /proc/self/mountinfo can kick of a reparse in systemd
84b277
before the utab information is available.
84b277
84b277
This change adds in an additional event source using inotify to watch
84b277
for changes to utab.  It only watches for IN_MOVED_TO events, matching
84b277
libmount behavior of always overwriting this file using rename(2).
84b277
84b277
This does add a second pass through the mount table parsing when utab is
84b277
updated.
84b277
84b277
(based-on befb6d54948480f836d53d633bef27e3505818c1)
84b277
84b277
Related: #1161417
84b277
---
84b277
 src/core/manager.c |  3 ++-
84b277
 src/core/manager.h |  2 ++
84b277
 src/core/mount.c   | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++---
84b277
 src/core/mount.h   |  2 +-
84b277
 src/shared/util.h  |  7 +++++
84b277
 5 files changed, 88 insertions(+), 5 deletions(-)
84b277
84b277
diff --git a/src/core/manager.c b/src/core/manager.c
84b277
index e48ea36..b0772ba 100644
84b277
--- a/src/core/manager.c
84b277
+++ b/src/core/manager.c
84b277
@@ -1732,8 +1732,9 @@ static int process_event(Manager *m, struct epoll_event *ev) {
84b277
         }
84b277
 
84b277
         case WATCH_MOUNT:
84b277
+        case WATCH_MOUNT_UTAB:
84b277
                 /* Some mount table change, intended for the mount subsystem */
84b277
-                mount_fd_event(m, ev->events);
84b277
+                mount_fd_event(m, w, ev->events);
84b277
                 break;
84b277
 
84b277
         case WATCH_SWAP:
84b277
diff --git a/src/core/manager.h b/src/core/manager.h
84b277
index 0133ea5..af66598 100644
84b277
--- a/src/core/manager.h
84b277
+++ b/src/core/manager.h
84b277
@@ -58,6 +58,7 @@ enum WatchType {
84b277
         WATCH_UNIT_TIMER,
84b277
         WATCH_JOB_TIMER,
84b277
         WATCH_MOUNT,
84b277
+        WATCH_MOUNT_UTAB,
84b277
         WATCH_SWAP,
84b277
         WATCH_UDEV,
84b277
         WATCH_DBUS_WATCH,
84b277
@@ -178,6 +179,7 @@ struct Manager {
84b277
         /* Data specific to the mount subsystem */
84b277
         FILE *proc_self_mountinfo;
84b277
         Watch mount_watch;
84b277
+        Watch mount_watch_utab;
84b277
 
84b277
         /* Data specific to the swap filesystem */
84b277
         FILE *proc_swaps;
84b277
diff --git a/src/core/mount.c b/src/core/mount.c
84b277
index d78269c..efa46da 100644
84b277
--- a/src/core/mount.c
84b277
+++ b/src/core/mount.c
84b277
@@ -25,6 +25,7 @@
84b277
 #include <sys/epoll.h>
84b277
 #include <signal.h>
84b277
 #include <libmount.h>
84b277
+#include <sys/inotify.h>
84b277
 
84b277
 #include "manager.h"
84b277
 #include "unit.h"
84b277
@@ -1619,6 +1620,11 @@ static void mount_shutdown(Manager *m) {
84b277
                 fclose(m->proc_self_mountinfo);
84b277
                 m->proc_self_mountinfo = NULL;
84b277
         }
84b277
+
84b277
+        if (m->mount_watch_utab.fd) {
84b277
+                close_nointr(m->mount_watch_utab.fd);
84b277
+                m->mount_watch_utab.fd=0;
84b277
+        }
84b277
 }
84b277
 
84b277
 static int mount_enumerate(Manager *m) {
84b277
@@ -1644,6 +1650,35 @@ static int mount_enumerate(Manager *m) {
84b277
                         return -errno;
84b277
         }
84b277
 
84b277
+        if (!m->mount_watch_utab.fd) {
84b277
+
84b277
+                struct epoll_event ev = {
84b277
+                        .events = EPOLLIN,
84b277
+                        .data.ptr = &m->mount_watch_utab,
84b277
+                };
84b277
+
84b277
+                m->mount_watch_utab.fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
84b277
+                if (m->mount_watch_utab.fd < 0) {
84b277
+                        r = -errno;
84b277
+                        goto fail;
84b277
+                }
84b277
+
84b277
+                (void) mkdir_p_label("/run/mount", 0755);
84b277
+
84b277
+                r = inotify_add_watch(m->mount_watch_utab.fd, "/run/mount", IN_MOVED_TO);
84b277
+                if (r < 0) {
84b277
+                        r = -errno;
84b277
+                        goto fail;
84b277
+                }
84b277
+
84b277
+                m->mount_watch_utab.type = WATCH_MOUNT_UTAB;
84b277
+
84b277
+                if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->mount_watch_utab.fd, &ev) < 0) {
84b277
+                        r = -errno;
84b277
+                        goto fail;
84b277
+                }
84b277
+        }
84b277
+
84b277
         r = mount_load_proc_self_mountinfo(m, false);
84b277
         if (r < 0)
84b277
                 goto fail;
84b277
@@ -1655,16 +1690,54 @@ fail:
84b277
         return r;
84b277
 }
84b277
 
84b277
-void mount_fd_event(Manager *m, int events) {
84b277
+void mount_fd_event(Manager *m, Watch *w, int events) {
84b277
         Unit *u;
84b277
         int r;
84b277
 
84b277
         assert(m);
84b277
-        assert(events & EPOLLPRI);
84b277
+        assert(w);
84b277
+        assert(events & (EPOLLPRI|EPOLLIN));
84b277
 
84b277
         /* The manager calls this for every fd event happening on the
84b277
          * /proc/self/mountinfo file, which informs us about mounting
84b277
-         * table changes */
84b277
+         * table changes
84b277
+         * This may also be called for /run/mount events */
84b277
+
84b277
+        if (w->type == WATCH_MOUNT_UTAB) {
84b277
+                bool rescan = false;
84b277
+
84b277
+                /* FIXME: We *really* need to replace this with
84b277
+                 * libmount's own API for this, we should not hardcode
84b277
+                 * internal behaviour of libmount here. */
84b277
+
84b277
+                for (;;) {
84b277
+                        uint8_t buffer[INOTIFY_EVENT_MAX] _alignas_(struct inotify_event);
84b277
+                        struct inotify_event *e;
84b277
+                        ssize_t l;
84b277
+
84b277
+                        l = read(w->fd, buffer, sizeof(buffer));
84b277
+                        if (l < 0) {
84b277
+                                if (errno == EAGAIN || errno == EINTR)
84b277
+                                        break;
84b277
+
84b277
+                                log_error("Failed to read utab inotify: %s", strerror(errno));
84b277
+                                break;
84b277
+                        }
84b277
+
84b277
+                        FOREACH_INOTIFY_EVENT(e, buffer, l) {
84b277
+                                /* Only care about changes to utab,
84b277
+                                 * but we have to monitor the
84b277
+                                 * directory to reliably get
84b277
+                                 * notifications about when utab is
84b277
+                                 * replaced using rename(2) */
84b277
+                                if ((e->mask & IN_Q_OVERFLOW) || streq(e->name, "utab"))
84b277
+                                        rescan = true;
84b277
+                        }
84b277
+                }
84b277
+
84b277
+                if (!rescan)
84b277
+                        return;
84b277
+        }
84b277
 
84b277
         r = mount_load_proc_self_mountinfo(m, true);
84b277
         if (r < 0) {
84b277
diff --git a/src/core/mount.h b/src/core/mount.h
84b277
index 7cd4320..df0e541 100644
84b277
--- a/src/core/mount.h
84b277
+++ b/src/core/mount.h
84b277
@@ -113,7 +113,7 @@ struct Mount {
84b277
 
84b277
 extern const UnitVTable mount_vtable;
84b277
 
84b277
-void mount_fd_event(Manager *m, int events);
84b277
+void mount_fd_event(Manager *m, Watch *w, int events);
84b277
 
84b277
 const char* mount_state_to_string(MountState i) _const_;
84b277
 MountState mount_state_from_string(const char *s) _pure_;
84b277
diff --git a/src/shared/util.h b/src/shared/util.h
84b277
index d68f385..c5ef8b6 100644
84b277
--- a/src/shared/util.h
84b277
+++ b/src/shared/util.h
84b277
@@ -791,3 +791,10 @@ static inline void qsort_safe(void *base, size_t nmemb, size_t size,
84b277
                 qsort(base, nmemb, size, compar);
84b277
         }
84b277
 }
84b277
+
84b277
+#define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX + 1)
84b277
+
84b277
+#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \
84b277
+        for ((e) = (struct inotify_event*) (buffer);    \
84b277
+             (uint8_t*) (e) < (uint8_t*) (buffer) + (sz); \
84b277
+             (e) = (struct inotify_event*) ((uint8_t*) (e) + sizeof(struct inotify_event) + (e)->len))