1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/files/file_path_watcher_kqueue.h"
6
7 #include <fcntl.h>
8 #include <stddef.h>
9 #include <sys/param.h>
10
11 #include "base/bind.h"
12 #include "base/files/file_util.h"
13 #include "base/logging.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/threading/sequenced_task_runner_handle.h"
16
17 // On some platforms these are not defined.
18 #if !defined(EV_RECEIPT)
19 #define EV_RECEIPT 0
20 #endif
21 #if !defined(O_EVTONLY)
22 #define O_EVTONLY O_RDONLY
23 #endif
24
25 namespace base {
26
FilePathWatcherKQueue()27 FilePathWatcherKQueue::FilePathWatcherKQueue() : kqueue_(-1) {}
28
~FilePathWatcherKQueue()29 FilePathWatcherKQueue::~FilePathWatcherKQueue() {
30 DCHECK(!task_runner() || task_runner()->RunsTasksInCurrentSequence());
31 }
32
ReleaseEvent(struct kevent & event)33 void FilePathWatcherKQueue::ReleaseEvent(struct kevent& event) {
34 CloseFileDescriptor(&event.ident);
35 EventData* entry = EventDataForKevent(event);
36 delete entry;
37 event.udata = NULL;
38 }
39
EventsForPath(FilePath path,EventVector * events)40 int FilePathWatcherKQueue::EventsForPath(FilePath path, EventVector* events) {
41 // Make sure that we are working with a clean slate.
42 DCHECK(events->empty());
43
44 std::vector<FilePath::StringType> components;
45 path.GetComponents(&components);
46
47 if (components.size() < 1) {
48 return -1;
49 }
50
51 int last_existing_entry = 0;
52 FilePath built_path;
53 bool path_still_exists = true;
54 for (std::vector<FilePath::StringType>::iterator i = components.begin();
55 i != components.end(); ++i) {
56 if (i == components.begin()) {
57 built_path = FilePath(*i);
58 } else {
59 built_path = built_path.Append(*i);
60 }
61 uintptr_t fd = kNoFileDescriptor;
62 if (path_still_exists) {
63 fd = FileDescriptorForPath(built_path);
64 if (fd == kNoFileDescriptor) {
65 path_still_exists = false;
66 } else {
67 ++last_existing_entry;
68 }
69 }
70 FilePath::StringType subdir = (i != (components.end() - 1)) ? *(i + 1) : "";
71 EventData* data = new EventData(built_path, subdir);
72 struct kevent event;
73 EV_SET(&event, fd, EVFILT_VNODE, (EV_ADD | EV_CLEAR | EV_RECEIPT),
74 (NOTE_DELETE | NOTE_WRITE | NOTE_ATTRIB |
75 NOTE_RENAME | NOTE_REVOKE | NOTE_EXTEND), 0, data);
76 events->push_back(event);
77 }
78 return last_existing_entry;
79 }
80
FileDescriptorForPath(const FilePath & path)81 uintptr_t FilePathWatcherKQueue::FileDescriptorForPath(const FilePath& path) {
82 int fd = HANDLE_EINTR(open(path.value().c_str(), O_EVTONLY));
83 if (fd == -1)
84 return kNoFileDescriptor;
85 return fd;
86 }
87
CloseFileDescriptor(uintptr_t * fd)88 void FilePathWatcherKQueue::CloseFileDescriptor(uintptr_t* fd) {
89 if (*fd == kNoFileDescriptor) {
90 return;
91 }
92
93 if (IGNORE_EINTR(close(*fd)) != 0) {
94 DPLOG(ERROR) << "close";
95 }
96 *fd = kNoFileDescriptor;
97 }
98
AreKeventValuesValid(struct kevent * kevents,int count)99 bool FilePathWatcherKQueue::AreKeventValuesValid(struct kevent* kevents,
100 int count) {
101 if (count < 0) {
102 DPLOG(ERROR) << "kevent";
103 return false;
104 }
105 bool valid = true;
106 for (int i = 0; i < count; ++i) {
107 if (kevents[i].flags & EV_ERROR && kevents[i].data) {
108 // Find the kevent in |events_| that matches the kevent with the error.
109 EventVector::iterator event = events_.begin();
110 for (; event != events_.end(); ++event) {
111 if (event->ident == kevents[i].ident) {
112 break;
113 }
114 }
115 std::string path_name;
116 if (event != events_.end()) {
117 EventData* event_data = EventDataForKevent(*event);
118 if (event_data != NULL) {
119 path_name = event_data->path_.value();
120 }
121 }
122 if (path_name.empty()) {
123 path_name = base::StringPrintf(
124 "fd %ld", reinterpret_cast<long>(&kevents[i].ident));
125 }
126 DLOG(ERROR) << "Error: " << kevents[i].data << " for " << path_name;
127 valid = false;
128 }
129 }
130 return valid;
131 }
132
HandleAttributesChange(const EventVector::iterator & event,bool * target_file_affected,bool * update_watches)133 void FilePathWatcherKQueue::HandleAttributesChange(
134 const EventVector::iterator& event,
135 bool* target_file_affected,
136 bool* update_watches) {
137 EventVector::iterator next_event = event + 1;
138 EventData* next_event_data = EventDataForKevent(*next_event);
139 // Check to see if the next item in path is still accessible.
140 uintptr_t have_access = FileDescriptorForPath(next_event_data->path_);
141 if (have_access == kNoFileDescriptor) {
142 *target_file_affected = true;
143 *update_watches = true;
144 EventVector::iterator local_event(event);
145 for (; local_event != events_.end(); ++local_event) {
146 // Close all nodes from the event down. This has the side effect of
147 // potentially rendering other events in |updates| invalid.
148 // There is no need to remove the events from |kqueue_| because this
149 // happens as a side effect of closing the file descriptor.
150 CloseFileDescriptor(&local_event->ident);
151 }
152 } else {
153 CloseFileDescriptor(&have_access);
154 }
155 }
156
HandleDeleteOrMoveChange(const EventVector::iterator & event,bool * target_file_affected,bool * update_watches)157 void FilePathWatcherKQueue::HandleDeleteOrMoveChange(
158 const EventVector::iterator& event,
159 bool* target_file_affected,
160 bool* update_watches) {
161 *target_file_affected = true;
162 *update_watches = true;
163 EventVector::iterator local_event(event);
164 for (; local_event != events_.end(); ++local_event) {
165 // Close all nodes from the event down. This has the side effect of
166 // potentially rendering other events in |updates| invalid.
167 // There is no need to remove the events from |kqueue_| because this
168 // happens as a side effect of closing the file descriptor.
169 CloseFileDescriptor(&local_event->ident);
170 }
171 }
172
HandleCreateItemChange(const EventVector::iterator & event,bool * target_file_affected,bool * update_watches)173 void FilePathWatcherKQueue::HandleCreateItemChange(
174 const EventVector::iterator& event,
175 bool* target_file_affected,
176 bool* update_watches) {
177 // Get the next item in the path.
178 EventVector::iterator next_event = event + 1;
179 // Check to see if it already has a valid file descriptor.
180 if (!IsKeventFileDescriptorOpen(*next_event)) {
181 EventData* next_event_data = EventDataForKevent(*next_event);
182 // If not, attempt to open a file descriptor for it.
183 next_event->ident = FileDescriptorForPath(next_event_data->path_);
184 if (IsKeventFileDescriptorOpen(*next_event)) {
185 *update_watches = true;
186 if (next_event_data->subdir_.empty()) {
187 *target_file_affected = true;
188 }
189 }
190 }
191 }
192
UpdateWatches(bool * target_file_affected)193 bool FilePathWatcherKQueue::UpdateWatches(bool* target_file_affected) {
194 // Iterate over events adding kevents for items that exist to the kqueue.
195 // Then check to see if new components in the path have been created.
196 // Repeat until no new components in the path are detected.
197 // This is to get around races in directory creation in a watched path.
198 bool update_watches = true;
199 while (update_watches) {
200 size_t valid;
201 for (valid = 0; valid < events_.size(); ++valid) {
202 if (!IsKeventFileDescriptorOpen(events_[valid])) {
203 break;
204 }
205 }
206 if (valid == 0) {
207 // The root of the file path is inaccessible?
208 return false;
209 }
210
211 EventVector updates(valid);
212 int count = HANDLE_EINTR(kevent(kqueue_, &events_[0], valid, &updates[0],
213 valid, NULL));
214 if (!AreKeventValuesValid(&updates[0], count)) {
215 return false;
216 }
217 update_watches = false;
218 for (; valid < events_.size(); ++valid) {
219 EventData* event_data = EventDataForKevent(events_[valid]);
220 events_[valid].ident = FileDescriptorForPath(event_data->path_);
221 if (IsKeventFileDescriptorOpen(events_[valid])) {
222 update_watches = true;
223 if (event_data->subdir_.empty()) {
224 *target_file_affected = true;
225 }
226 } else {
227 break;
228 }
229 }
230 }
231 return true;
232 }
233
Watch(const FilePath & path,bool recursive,const FilePathWatcher::Callback & callback)234 bool FilePathWatcherKQueue::Watch(const FilePath& path,
235 bool recursive,
236 const FilePathWatcher::Callback& callback) {
237 DCHECK(target_.value().empty()); // Can only watch one path.
238 DCHECK(!callback.is_null());
239 DCHECK_EQ(kqueue_, -1);
240 // Recursive watch is not supported using kqueue.
241 DCHECK(!recursive);
242
243 callback_ = callback;
244 target_ = path;
245
246 set_task_runner(SequencedTaskRunnerHandle::Get());
247
248 kqueue_ = kqueue();
249 if (kqueue_ == -1) {
250 DPLOG(ERROR) << "kqueue";
251 return false;
252 }
253
254 int last_entry = EventsForPath(target_, &events_);
255 DCHECK_NE(last_entry, 0);
256
257 EventVector responses(last_entry);
258
259 int count = HANDLE_EINTR(kevent(kqueue_, &events_[0], last_entry,
260 &responses[0], last_entry, NULL));
261 if (!AreKeventValuesValid(&responses[0], count)) {
262 // Calling Cancel() here to close any file descriptors that were opened.
263 // This would happen in the destructor anyways, but FilePathWatchers tend to
264 // be long lived, and if an error has occurred, there is no reason to waste
265 // the file descriptors.
266 Cancel();
267 return false;
268 }
269
270 // It's safe to use Unretained() because the watch is cancelled and the
271 // callback cannot be invoked after |kqueue_watch_controller_| (which is a
272 // member of |this|) has been deleted.
273 kqueue_watch_controller_ = FileDescriptorWatcher::WatchReadable(
274 kqueue_,
275 Bind(&FilePathWatcherKQueue::OnKQueueReadable, Unretained(this)));
276
277 return true;
278 }
279
Cancel()280 void FilePathWatcherKQueue::Cancel() {
281 if (!task_runner()) {
282 set_cancelled();
283 return;
284 }
285
286 DCHECK(task_runner()->RunsTasksInCurrentSequence());
287 if (!is_cancelled()) {
288 set_cancelled();
289 kqueue_watch_controller_.reset();
290 if (IGNORE_EINTR(close(kqueue_)) != 0) {
291 DPLOG(ERROR) << "close kqueue";
292 }
293 kqueue_ = -1;
294 std::for_each(events_.begin(), events_.end(), ReleaseEvent);
295 events_.clear();
296 callback_.Reset();
297 }
298 }
299
OnKQueueReadable()300 void FilePathWatcherKQueue::OnKQueueReadable() {
301 DCHECK(task_runner()->RunsTasksInCurrentSequence());
302 DCHECK(events_.size());
303
304 // Request the file system update notifications that have occurred and return
305 // them in |updates|. |count| will contain the number of updates that have
306 // occurred.
307 EventVector updates(events_.size());
308 struct timespec timeout = {0, 0};
309 int count = HANDLE_EINTR(kevent(kqueue_, NULL, 0, &updates[0], updates.size(),
310 &timeout));
311
312 // Error values are stored within updates, so check to make sure that no
313 // errors occurred.
314 if (!AreKeventValuesValid(&updates[0], count)) {
315 callback_.Run(target_, true /* error */);
316 Cancel();
317 return;
318 }
319
320 bool update_watches = false;
321 bool send_notification = false;
322
323 // Iterate through each of the updates and react to them.
324 for (int i = 0; i < count; ++i) {
325 // Find our kevent record that matches the update notification.
326 EventVector::iterator event = events_.begin();
327 for (; event != events_.end(); ++event) {
328 if (!IsKeventFileDescriptorOpen(*event) ||
329 event->ident == updates[i].ident) {
330 break;
331 }
332 }
333 if (event == events_.end() || !IsKeventFileDescriptorOpen(*event)) {
334 // The event may no longer exist in |events_| because another event
335 // modified |events_| in such a way to make it invalid. For example if
336 // the path is /foo/bar/bam and foo is deleted, NOTE_DELETE events for
337 // foo, bar and bam will be sent. If foo is processed first, then
338 // the file descriptors for bar and bam will already be closed and set
339 // to -1 before they get a chance to be processed.
340 continue;
341 }
342
343 EventData* event_data = EventDataForKevent(*event);
344
345 // If the subdir is empty, this is the last item on the path and is the
346 // target file.
347 bool target_file_affected = event_data->subdir_.empty();
348 if ((updates[i].fflags & NOTE_ATTRIB) && !target_file_affected) {
349 HandleAttributesChange(event, &target_file_affected, &update_watches);
350 }
351 if (updates[i].fflags & (NOTE_DELETE | NOTE_REVOKE | NOTE_RENAME)) {
352 HandleDeleteOrMoveChange(event, &target_file_affected, &update_watches);
353 }
354 if ((updates[i].fflags & NOTE_WRITE) && !target_file_affected) {
355 HandleCreateItemChange(event, &target_file_affected, &update_watches);
356 }
357 send_notification |= target_file_affected;
358 }
359
360 if (update_watches) {
361 if (!UpdateWatches(&send_notification)) {
362 callback_.Run(target_, true /* error */);
363 Cancel();
364 }
365 }
366
367 if (send_notification) {
368 callback_.Run(target_, false);
369 }
370 }
371
372 } // namespace base
373