GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/file_watcher_inotify.cc
Date: 2026-04-26 02:35:59
Exec Total Coverage
Lines: 64 77 83.1%
Branches: 39 76 51.3%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4
5 #include "file_watcher_inotify.h"
6
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <linux/limits.h>
10 #include <poll.h>
11 #include <sys/inotify.h>
12 #include <sys/time.h>
13 #include <sys/types.h>
14 #include <unistd.h>
15
16 #include <cassert>
17 #include <string>
18
19 #include "util/logging.h"
20 #include "util/posix.h"
21
22 namespace file_watcher {
23
24 152 FileWatcherInotify::FileWatcherInotify() { }
25
26 608 FileWatcherInotify::~FileWatcherInotify() { }
27
28 152 bool FileWatcherInotify::RunEventLoop(const FileWatcher::HandlerMap &handlers,
29 int read_pipe, int write_pipe) {
30 152 inotify_fd_ = inotify_init1(IN_NONBLOCK);
31
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 152 times.
152 assert(inotify_fd_ >= 0);
32
33 152 for (FileWatcher::HandlerMap::const_iterator it = handlers.begin();
34
2/2
✓ Branch 2 taken 152 times.
✓ Branch 3 taken 152 times.
304 it != handlers.end();
35 152 ++it) {
36
1/2
✓ Branch 3 taken 152 times.
✗ Branch 4 not taken.
152 RegisterFilter(it->first, it->second);
37 }
38
39 // Use the control pipe to signal readiness to the main thread,
40 // before continuing with the event loop.
41
1/2
✓ Branch 1 taken 152 times.
✗ Branch 2 not taken.
152 WritePipe(write_pipe, "s", 1);
42
43 struct pollfd poll_set[2];
44
45 152 poll_set[0].fd = read_pipe;
46 152 poll_set[0].events = POLLHUP | POLLIN;
47 152 poll_set[0].revents = 0;
48 152 poll_set[1].fd = inotify_fd_;
49 152 poll_set[1].events = POLLIN;
50 152 poll_set[1].revents = 0;
51
52 152 bool stop = false;
53
2/2
✓ Branch 0 taken 304 times.
✓ Branch 1 taken 152 times.
456 while (!stop) {
54
1/2
✓ Branch 1 taken 304 times.
✗ Branch 2 not taken.
304 const int ready = poll(poll_set, 2, -1);
55
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 304 times.
304 if (ready == -1) {
56 if (errno == EINTR) {
57 152 continue;
58 }
59 LogCvmfs(kLogCvmfs, kLogSyslogErr,
60 "FileWatcherInotify - Could not poll events. Errno: %d", errno);
61 return false;
62 }
63
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 304 times.
304 if (ready == 0) {
64 continue;
65 }
66
67
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 304 times.
304 if (poll_set[0].revents & POLLHUP) {
68 LogCvmfs(kLogCvmfs, kLogDebug, "FileWatcherInotify - Stopping.\n");
69 stop = true;
70 continue;
71 }
72
2/2
✓ Branch 0 taken 152 times.
✓ Branch 1 taken 152 times.
304 if (poll_set[0].revents & POLLIN) {
73 char buffer[1];
74
1/2
✓ Branch 1 taken 152 times.
✗ Branch 2 not taken.
152 ReadPipe(read_pipe, &buffer, 1);
75
1/2
✓ Branch 1 taken 152 times.
✗ Branch 2 not taken.
152 LogCvmfs(kLogCvmfs, kLogDebug, "FileWatcherInotify - Stopping.\n");
76 152 stop = true;
77 152 continue;
78 152 }
79
80 152 const size_t event_size = sizeof(struct inotify_event);
81 // We need a large enough buffer for an event with the largest path name
82 152 const size_t buffer_size = event_size + PATH_MAX + 1;
83 char buffer[buffer_size];
84
1/2
✓ Branch 0 taken 152 times.
✗ Branch 1 not taken.
152 if (poll_set[1].revents & POLLIN) {
85
1/2
✓ Branch 1 taken 152 times.
✗ Branch 2 not taken.
152 const int len = read(inotify_fd_, buffer, buffer_size);
86
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 152 times.
152 assert(len > 0);
87 152 int i = 0;
88
2/2
✓ Branch 0 taken 304 times.
✓ Branch 1 taken 152 times.
456 while (i < len) {
89 struct inotify_event
90 304 *inotify_event = reinterpret_cast<struct inotify_event *>(
91 &buffer[i]);
92 const std::map<int, WatchRecord>::const_iterator
93
1/2
✓ Branch 1 taken 304 times.
✗ Branch 2 not taken.
304 it = watch_records_.find(inotify_event->wd);
94
2/2
✓ Branch 3 taken 228 times.
✓ Branch 4 taken 76 times.
304 if (it != watch_records_.end()) {
95
1/2
✓ Branch 2 taken 228 times.
✗ Branch 3 not taken.
228 WatchRecord current_record = it->second;
96 228 file_watcher::Event event = file_watcher::kInvalid;
97
2/2
✓ Branch 0 taken 76 times.
✓ Branch 1 taken 152 times.
228 if (inotify_event->mask & IN_DELETE_SELF) {
98 76 event = file_watcher::kDeleted;
99
2/2
✓ Branch 0 taken 76 times.
✓ Branch 1 taken 76 times.
152 } else if (inotify_event->mask & IN_CLOSE_WRITE) {
100 // Modified
101 76 event = file_watcher::kModified;
102
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 76 times.
76 } else if (inotify_event->mask & IN_MOVE_SELF) {
103 // Renamed
104 event = file_watcher::kRenamed;
105
1/2
✓ Branch 0 taken 76 times.
✗ Branch 1 not taken.
76 } else if (inotify_event->mask & IN_ATTRIB) {
106 // Attributes
107 76 event = file_watcher::kAttributes;
108 } else if (inotify_event->mask & IN_IGNORED) {
109 // An IN_IGNORED event is generated after a file is deleted and the
110 // watch is removed
111 event = file_watcher::kIgnored;
112 }
113 228 bool clear_handler = true;
114
1/2
✓ Branch 0 taken 228 times.
✗ Branch 1 not taken.
228 if (event != file_watcher::kInvalid
115
1/2
✓ Branch 0 taken 228 times.
✗ Branch 1 not taken.
228 && event != file_watcher::kIgnored) {
116
1/2
✓ Branch 1 taken 228 times.
✗ Branch 2 not taken.
228 current_record.handler_->Handle(current_record.file_path_, event,
117 &clear_handler);
118 } else {
119 LogCvmfs(kLogCvmfs, kLogDebug,
120 "FileWatcherInotify - Unknown event 0x%x\n",
121 inotify_event->mask);
122 }
123
124 // Perform post-handling actions (i.e. remove, reset filter)
125
2/2
✓ Branch 0 taken 76 times.
✓ Branch 1 taken 152 times.
228 if (event == file_watcher::kDeleted) {
126
1/2
✓ Branch 1 taken 76 times.
✗ Branch 2 not taken.
76 watch_records_.erase(inotify_event->wd);
127
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 76 times.
76 if (!clear_handler) {
128 RegisterFilter(current_record.file_path_,
129 current_record.handler_);
130 }
131 }
132 228 } else {
133
1/2
✓ Branch 1 taken 76 times.
✗ Branch 2 not taken.
76 LogCvmfs(kLogCvmfs, kLogDebug,
134 "FileWatcherInotify - Unknown event ident: %d",
135 inotify_event->wd);
136 }
137
138 304 i += event_size + inotify_event->len;
139 }
140 }
141 }
142
143 152 watch_records_.clear();
144
145
1/2
✓ Branch 1 taken 152 times.
✗ Branch 2 not taken.
152 close(inotify_fd_);
146
147 152 return true;
148 }
149
150 152 int FileWatcherInotify::TryRegisterFilter(const std::string &file_path) {
151 152 return inotify_add_watch(
152 inotify_fd_, file_path.c_str(),
153 152 IN_ATTRIB | IN_CLOSE_WRITE | IN_DELETE_SELF | IN_MOVE_SELF);
154 }
155
156 } // namespace file_watcher
157