diff --git a/src/core/path.c b/src/core/path.c index 8f2971a4e1..d27e923ace 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; - } - - /* 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; - } - - /* Hmm, we succeeded in adding the watch this time... let's continue. */ } + + /* 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; + + 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. */ + } + } + + 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; } }