diff options
author | Yu Watanabe <watanabe.yu+github@gmail.com> | 2021-05-26 10:51:00 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-05-26 10:51:00 +0900 |
commit | 89c629fc4b67631d133ee2f432dcb85e7a182c56 (patch) | |
tree | dd7cf89448d0871a53d33147e64458925c0ff36f /src | |
parent | b69855e645a046d0860f2ae99464019e0f6adf81 (diff) | |
parent | 41cdcb54980f95326d7ed0361f16786b76c2787e (diff) | |
download | systemd-89c629fc4b67631d133ee2f432dcb85e7a182c56.tar.gz |
Merge pull request #19726 from poettering/path-event-symlink
teach .path units to notice events on paths with components that are symlinks
Diffstat (limited to 'src')
-rw-r--r-- | src/core/path.c | 90 |
1 files changed, 57 insertions, 33 deletions
diff --git a/src/core/path.c b/src/core/path.c index 04084bf63e..d27e923ace 100644 --- a/src/core/path.c +++ b/src/core/path.c @@ -36,10 +36,10 @@ static int path_dispatch_io(sd_event_source *source, int fd, uint32_t revents, v int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler) { static const int flags_table[_PATH_TYPE_MAX] = { - [PATH_EXISTS] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB, - [PATH_EXISTS_GLOB] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB, - [PATH_CHANGED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO, - [PATH_MODIFIED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO|IN_MODIFY, + [PATH_EXISTS] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB, + [PATH_EXISTS_GLOB] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB, + [PATH_CHANGED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO, + [PATH_MODIFIED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO|IN_MODIFY, [PATH_DIRECTORY_NOT_EMPTY] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CREATE|IN_MOVED_TO, }; @@ -55,13 +55,15 @@ int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler) { s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); if (s->inotify_fd < 0) { - r = -errno; + r = log_error_errno(errno, "Failed to allocate inotify fd: %m"); goto fail; } r = sd_event_add_io(s->unit->manager->event, &s->event_source, s->inotify_fd, EPOLLIN, handler, s); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to add inotify fd to event loop: %m"); goto fail; + } (void) sd_event_source_set_description(s->event_source, "path"); @@ -69,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); @@ -79,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. */ @@ -122,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; } } @@ -151,7 +175,8 @@ int path_spec_fd_event(PathSpec *s, uint32_t revents) { union inotify_event_buffer buffer; struct inotify_event *e; ssize_t l; - int r = 0; + + assert(s); if (revents != EPOLLIN) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), @@ -165,13 +190,12 @@ int path_spec_fd_event(PathSpec *s, uint32_t revents) { return log_error_errno(errno, "Failed to read inotify event: %m"); } - FOREACH_INOTIFY_EVENT(e, buffer, l) { - if (IN_SET(s->type, PATH_CHANGED, PATH_MODIFIED) && - s->primary_wd == e->wd) - r = 1; - } + if (IN_SET(s->type, PATH_CHANGED, PATH_MODIFIED)) + FOREACH_INOTIFY_EVENT(e, buffer, l) + if (s->primary_wd == e->wd) + return 1; - return r; + return 0; } static bool path_spec_check_good(PathSpec *s, bool initial, bool from_trigger_notify) { |