GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/file_watcher_inotify.cc
Date: 2025-06-29 02:35:41
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 64 FileWatcherInotify::FileWatcherInotify() { }
26
27 256 FileWatcherInotify::~FileWatcherInotify() { }
28
29 64 bool FileWatcherInotify::RunEventLoop(const FileWatcher::HandlerMap &handlers,
30 int read_pipe, int write_pipe) {
31 64 inotify_fd_ = inotify_init1(IN_NONBLOCK);
32
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
64 assert(inotify_fd_ >= 0);
33
34 64 for (FileWatcher::HandlerMap::const_iterator it = handlers.begin();
35
2/2
✓ Branch 2 taken 64 times.
✓ Branch 3 taken 64 times.
128 it != handlers.end();
36 64 ++it) {
37
1/2
✓ Branch 3 taken 64 times.
✗ Branch 4 not taken.
64 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 64 times.
✗ Branch 2 not taken.
64 WritePipe(write_pipe, "s", 1);
43
44 struct pollfd poll_set[2];
45
46 64 poll_set[0].fd = read_pipe;
47 64 poll_set[0].events = POLLHUP | POLLIN;
48 64 poll_set[0].revents = 0;
49 64 poll_set[1].fd = inotify_fd_;
50 64 poll_set[1].events = POLLIN;
51 64 poll_set[1].revents = 0;
52
53 64 bool stop = false;
54
2/2
✓ Branch 0 taken 128 times.
✓ Branch 1 taken 64 times.
192 while (!stop) {
55
1/2
✓ Branch 1 taken 128 times.
✗ Branch 2 not taken.
128 const int ready = poll(poll_set, 2, -1);
56
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 128 times.
128 if (ready == -1) {
57 if (errno == EINTR) {
58 64 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 128 times.
128 if (ready == 0) {
65 continue;
66 }
67
68
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 128 times.
128 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 64 times.
✓ Branch 1 taken 64 times.
128 if (poll_set[0].revents & POLLIN) {
74 char buffer[1];
75
1/2
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
64 ReadPipe(read_pipe, &buffer, 1);
76
1/2
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
64 LogCvmfs(kLogCvmfs, kLogDebug, "FileWatcherInotify - Stopping.\n");
77 64 stop = true;
78 64 continue;
79 64 }
80
81 64 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 64 const size_t buffer_size = event_size + PATH_MAX + 1;
84 char buffer[buffer_size];
85
1/2
✓ Branch 0 taken 64 times.
✗ Branch 1 not taken.
64 if (poll_set[1].revents & POLLIN) {
86
1/2
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
64 const int len = read(inotify_fd_, buffer, buffer_size);
87
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
64 assert(len > 0);
88 64 int i = 0;
89
2/2
✓ Branch 0 taken 128 times.
✓ Branch 1 taken 64 times.
192 while (i < len) {
90 struct inotify_event
91 128 *inotify_event = reinterpret_cast<struct inotify_event *>(
92 &buffer[i]);
93 const std::map<int, WatchRecord>::const_iterator
94
1/2
✓ Branch 1 taken 128 times.
✗ Branch 2 not taken.
128 it = watch_records_.find(inotify_event->wd);
95
2/2
✓ Branch 3 taken 96 times.
✓ Branch 4 taken 32 times.
128 if (it != watch_records_.end()) {
96
1/2
✓ Branch 2 taken 96 times.
✗ Branch 3 not taken.
96 WatchRecord current_record = it->second;
97 96 file_watcher::Event event = file_watcher::kInvalid;
98
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 64 times.
96 if (inotify_event->mask & IN_DELETE_SELF) {
99 32 event = file_watcher::kDeleted;
100
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 32 times.
64 } else if (inotify_event->mask & IN_CLOSE_WRITE) {
101 // Modified
102 32 event = file_watcher::kModified;
103
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
32 } else if (inotify_event->mask & IN_MOVE_SELF) {
104 // Renamed
105 event = file_watcher::kRenamed;
106
1/2
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
32 } else if (inotify_event->mask & IN_ATTRIB) {
107 // Attributes
108 32 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 96 bool clear_handler = true;
115
1/2
✓ Branch 0 taken 96 times.
✗ Branch 1 not taken.
96 if (event != file_watcher::kInvalid
116
1/2
✓ Branch 0 taken 96 times.
✗ Branch 1 not taken.
96 && event != file_watcher::kIgnored) {
117
1/2
✓ Branch 1 taken 96 times.
✗ Branch 2 not taken.
96 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 32 times.
✓ Branch 1 taken 64 times.
96 if (event == file_watcher::kDeleted) {
127
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
32 watch_records_.erase(inotify_event->wd);
128
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
32 if (!clear_handler) {
129 RegisterFilter(current_record.file_path_,
130 current_record.handler_);
131 }
132 }
133 96 } else {
134
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
32 LogCvmfs(kLogCvmfs, kLogDebug,
135 "FileWatcherInotify - Unknown event ident: %d",
136 inotify_event->wd);
137 }
138
139 128 i += event_size + inotify_event->len;
140 }
141 }
142 }
143
144 64 watch_records_.clear();
145
146
1/2
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
64 close(inotify_fd_);
147
148 64 return true;
149 }
150
151 64 int FileWatcherInotify::TryRegisterFilter(const std::string &file_path) {
152 64 return inotify_add_watch(
153 inotify_fd_, file_path.c_str(),
154 64 IN_ATTRIB | IN_CLOSE_WRITE | IN_DELETE_SELF | IN_MOVE_SELF);
155 }
156
157 } // namespace file_watcher
158