]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: watch paths with symlinks in .path units 19726/head
authorLennart Poettering <lennart@poettering.net>
Tue, 25 May 2021 21:10:36 +0000 (23:10 +0200)
committerLennart Poettering <lennart@poettering.net>
Tue, 25 May 2021 21:14:38 +0000 (23:14 +0200)
When watching paths that contain symlinks in some element we so far
always only watched the inode they are pointing to, not the symlink
inode itself. Let's fix that and always watch both. We do this by simply
installing the inotify watch once with and once without IN_DONT_FOLLOW.
For non-symlink inodes this just overrides the same watch twice (where
the second one replaces the first), which is has no effect effectively.
For symlinks it means we'll watch both source and destination.

Fixes: #17727
src/core/path.c

index 8f2971a4e15a4572019a18067a00ed7e8289cd79..d27e923ace99a411bc25d27a98e709bbcac216bb 100644 (file)
@@ -71,9 +71,9 @@ int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler) {
         assert(!strstr(s->path, "//"));
 
         for (slash = strchr(s->path, '/'); ; slash = strchr(slash+1, '/')) {
-                char *cut = NULL;
-                int flags;
-                char tmp;
+                bool incomplete = false;
+                int flags, wd = -1;
+                char tmp, *cut;
 
                 if (slash) {
                         cut = slash + (slash == s->path);
@@ -81,28 +81,50 @@ int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler) {
                         *cut = '\0';
 
                         flags = IN_MOVE_SELF | IN_DELETE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO;
-                } else
+                } else {
+                        cut = NULL;
                         flags = flags_table[s->type];
+                }
 
-                r = inotify_add_watch(s->inotify_fd, s->path, flags);
-                if (r < 0) {
-                        if (IN_SET(errno, EACCES, ENOENT)) {
-                                if (cut)
-                                        *cut = tmp;
-                                break;
-                        }
+                /* If this is a symlink watch both the symlink inode and where it points to. If the inode is
+                 * not a symlink both calls will install the same watch, which is redundant and doesn't
+                 * hurt. */
+                for (int follow_symlink = 0; follow_symlink < 2; follow_symlink ++) {
+                        uint32_t f = flags;
 
-                        /* This second call to inotify_add_watch() should fail like the previous
-                         * one and is done for logging the error in a comprehensive way. */
-                        r = inotify_add_watch_and_warn(s->inotify_fd, s->path, flags);
-                        if (r < 0) {
-                                if (cut)
-                                        *cut = tmp;
-                                goto fail;
+                        SET_FLAG(f, IN_DONT_FOLLOW, !follow_symlink);
+
+                        wd = inotify_add_watch(s->inotify_fd, s->path, f);
+                        if (wd < 0) {
+                                if (IN_SET(errno, EACCES, ENOENT)) {
+                                        incomplete = true; /* This is an expected error, let's accept this
+                                                            * quietly: we have an incomplete watch for
+                                                            * now. */
+                                        break;
+                                }
+
+                                /* This second call to inotify_add_watch() should fail like the previous one
+                                 * and is done for logging the error in a comprehensive way. */
+                                wd = inotify_add_watch_and_warn(s->inotify_fd, s->path, f);
+                                if (wd < 0) {
+                                        if (cut)
+                                                *cut = tmp;
+
+                                        r = wd;
+                                        goto fail;
+                                }
+
+                                /* Hmm, we succeeded in adding the watch this time... let's continue. */
                         }
+                }
 
-                        /* Hmm, we succeeded in adding the watch this time... let's continue. */
+                if (incomplete) {
+                        if (cut)
+                                *cut = tmp;
+
+                        break;
                 }
+
                 exists = true;
 
                 /* Path exists, we don't need to watch parent too closely. */
@@ -124,7 +146,7 @@ int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler) {
                         oldslash = slash;
                 else {
                         /* whole path has been iterated over */
-                        s->primary_wd = r;
+                        s->primary_wd = wd;
                         break;
                 }
         }