daandemeyer / rpms / systemd

Forked from rpms/systemd 2 years ago
Clone
4eda52
From a13bd62f6cb8332864ed3566fdf51eedfe240043 Mon Sep 17 00:00:00 2001
4295f9
From: Yu Watanabe <watanabe.yu+github@gmail.com>
4295f9
Date: Wed, 1 Sep 2021 12:57:40 +0900
4295f9
Subject: [PATCH] udev-node: always update timestamp of stack directory
4295f9
4295f9
Please see the comments in the code.
4295f9
4295f9
(cherry picked from commit 6df797f75fa08bb1a9e657001229bd47903e6174)
4295f9
4eda52
Related: #2005024
4295f9
---
4295f9
 src/udev/udev-node.c | 90 ++++++++++++++++++++++++++++++++++++++++++--
4295f9
 1 file changed, 87 insertions(+), 3 deletions(-)
4295f9
4295f9
diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c
4295f9
index 5d6aae0bd4..0de848da19 100644
4295f9
--- a/src/udev/udev-node.c
4295f9
+++ b/src/udev/udev-node.c
4295f9
@@ -32,6 +32,7 @@
4295f9
 #define CREATE_LINK_MAX_RETRIES        128
4295f9
 #define LINK_UPDATE_MAX_RETRIES        128
4295f9
 #define CREATE_STACK_LINK_MAX_RETRIES  128
4295f9
+#define UPDATE_TIMESTAMP_MAX_RETRIES   128
4295f9
 #define UDEV_NODE_HASH_KEY SD_ID128_MAKE(b9,6a,f1,ce,40,31,44,1a,9e,19,ec,8b,ae,f3,e3,2f)
4295f9
 
4295f9
 static int create_symlink(const char *target, const char *slink) {
4295f9
@@ -285,9 +286,60 @@ toolong:
4295f9
         return size - 1;
4295f9
 }
4295f9
 
4295f9
+static int update_timestamp(sd_device *dev, const char *path, struct stat *prev) {
4295f9
+        assert(path);
4295f9
+        assert(prev);
4295f9
+
4295f9
+        /* Even if a symlink in the stack directory is created/removed, the mtime of the directory may
4295f9
+         * not be changed. Why? Let's consider the following situation. For simplicity, let's assume
4295f9
+         * there exist three udev workers (A, B, and C) and all of them calls link_update() for the
4295f9
+         * same devlink simultaneously.
4295f9
+         *
4295f9
+         * 1. B creates/removes a symlink in the stack directory.
4295f9
+         * 2. A calls the first stat() in the loop of link_update().
4295f9
+         * 3. A calls link_find_prioritized().
4295f9
+         * 4. C creates/removes another symlink in the stack directory, so the result of the step 3 is outdated.
4295f9
+         * 5. B and C finish link_update().
4295f9
+         * 6. A creates/removes devlink according to the outdated result in the step 3.
4295f9
+         * 7. A calls the second stat() in the loop of link_update().
4295f9
+         *
4295f9
+         * If these 7 steps are processed in this order within a short time period that kernel's timer
4295f9
+         * does not increase, then even if the contents in the stack directory is changed, the results
4295f9
+         * of two stat() called by A shows the same timestamp, and A cannot detect the change.
4295f9
+         *
4295f9
+         * By calling this function after creating/removing symlinks in the stack directory, the
4295f9
+         * timestamp of the stack directory is always increased at least in the above step 5, so A can
4295f9
+         * detect the update. */
4295f9
+
4295f9
+        if ((prev->st_mode & S_IFMT) == 0)
4295f9
+                return 0; /* Does not exist, or previous stat() failed. */
4295f9
+
4295f9
+        for (unsigned i = 0; i < UPDATE_TIMESTAMP_MAX_RETRIES; i++) {
4295f9
+                struct stat st;
4295f9
+
4295f9
+                if (stat(path, &st) < 0)
4295f9
+                        return -errno;
4295f9
+
4295f9
+                if (!stat_inode_unmodified(prev, &st))
4295f9
+                        return 0;
4295f9
+
4295f9
+                log_device_debug(dev,
4295f9
+                                 "%s is modified, but its timestamp is not changed, "
4295f9
+                                 "updating timestamp after 10ms.",
4295f9
+                                 path);
4295f9
+
4295f9
+                (void) usleep(10 * USEC_PER_MSEC);
4295f9
+                if (utimensat(AT_FDCWD, path, NULL, 0) < 0)
4295f9
+                        return -errno;
4295f9
+        }
4295f9
+
4295f9
+        return -ELOOP;
4295f9
+}
4295f9
+
4295f9
 static int update_stack_directory(sd_device *dev, const char *dirname, bool add) {
4295f9
         _cleanup_free_ char *filename = NULL, *data = NULL, *buf = NULL;
4295f9
         const char *devname, *id;
4295f9
+        struct stat st = {};
4295f9
         int priority, r;
4295f9
 
4295f9
         assert(dev);
4295f9
@@ -302,10 +354,31 @@ static int update_stack_directory(sd_device *dev, const char *dirname, bool add)
4295f9
                 return log_oom_debug();
4295f9
 
4295f9
         if (!add) {
4295f9
-                if (unlink(filename) < 0 && errno != ENOENT)
4295f9
-                        log_device_debug_errno(dev, errno, "Failed to remove %s, ignoring: %m", filename);
4295f9
+                bool unlink_failed = false;
4295f9
+
4295f9
+                if (stat(dirname, &st) < 0) {
4295f9
+                        if (errno == ENOENT)
4295f9
+                                return 0; /* The stack directory is already removed. That's OK. */
4295f9
+                        log_device_debug_errno(dev, errno, "Failed to stat %s, ignoring: %m", dirname);
4295f9
+                }
4295f9
+
4295f9
+                if (unlink(filename) < 0) {
4295f9
+                        unlink_failed = true;
4295f9
+                        if (errno != ENOENT)
4295f9
+                                log_device_debug_errno(dev, errno, "Failed to remove %s, ignoring: %m", filename);
4295f9
+                }
4295f9
+
4295f9
+                if (rmdir(dirname) >= 0 || errno == ENOENT)
4295f9
+                        return 0;
4295f9
+
4295f9
+                if (unlink_failed)
4295f9
+                        return 0; /* If we failed to remove the symlink, there is almost nothing we can do. */
4295f9
+
4295f9
+                /* The symlink was removed. Check if the timestamp of directory is changed. */
4295f9
+                r = update_timestamp(dev, dirname, &st);
4295f9
+                if (r < 0 && r != -ENOENT)
4295f9
+                        return log_device_debug_errno(dev, r, "Failed to update timestamp of %s: %m", dirname);
4295f9
 
4295f9
-                (void) rmdir(dirname);
4295f9
                 return 0;
4295f9
         }
4295f9
 
4295f9
@@ -335,12 +408,23 @@ static int update_stack_directory(sd_device *dev, const char *dirname, bool add)
4295f9
                 if (r < 0)
4295f9
                         return log_device_debug_errno(dev, r, "Failed to create directory %s: %m", dirname);
4295f9
 
4295f9
+                if (stat(dirname, &st) < 0) {
4295f9
+                        if (errno == ENOENT)
4295f9
+                                continue;
4295f9
+                        return log_device_debug_errno(dev, errno, "Failed to stat %s: %m", dirname);
4295f9
+                }
4295f9
+
4295f9
                 if (symlink(data, filename) < 0) {
4295f9
                         if (errno == ENOENT)
4295f9
                                 continue;
4295f9
                         return log_device_debug_errno(dev, errno, "Failed to create symbolic link %s: %m", filename);
4295f9
                 }
4295f9
 
4295f9
+                /* The symlink was created. Check if the timestamp of directory is changed. */
4295f9
+                r = update_timestamp(dev, dirname, &st);
4295f9
+                if (r < 0)
4295f9
+                        return log_device_debug_errno(dev, r, "Failed to update timestamp of %s: %m", dirname);
4295f9
+
4295f9
                 return 0;
4295f9
         }
4295f9