GCC Code Coverage Report


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