From 41cdcb54980f95326d7ed0361f16786b76c2787e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 25 May 2021 23:10:36 +0200 Subject: [PATCH] core: watch paths with symlinks in .path units 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 | 62 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 20 deletions(-) diff --git a/src/core/path.c b/src/core/path.c index 8f2971a4e15..d27e923ace9 100644 --- a/src/core/path.c +++ b/src/core/path.c @@ -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; } } -- 2.47.3