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);
*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. */
oldslash = slash;
else {
/* whole path has been iterated over */
- s->primary_wd = r;
+ s->primary_wd = wd;
break;
}
}