blob: 84553cccb740aba4a64787c7b895b35bc04ab38e [file] [log] [blame]
[email protected]a9aaa9d12012-04-25 00:42:511// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]5199d742010-08-19 10:35:462// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]6d2d92a2014-06-11 07:15:245#include "base/files/file_path_watcher_kqueue.h"
[email protected]5199d742010-08-19 10:35:466
[email protected]7b3ee8b2011-04-01 18:48:197#include <fcntl.h>
avi543540e2015-12-24 05:15:328#include <stddef.h>
[email protected]7b3ee8b2011-04-01 18:48:199#include <sys/param.h>
[email protected]5199d742010-08-19 10:35:4610
[email protected]f4412a882011-10-05 16:58:2011#include "base/bind.h"
Lei Zhang26f40fd2019-01-22 23:57:2212#include "base/file_descriptor_posix.h"
[email protected]e3177dd52014-08-13 20:22:1413#include "base/files/file_util.h"
[email protected]66a59402012-12-05 00:36:3914#include "base/logging.h"
[email protected]251cd6e52013-06-11 13:36:3715#include "base/strings/stringprintf.h"
Etienne Pierre-doray9fc69fb2018-09-27 18:08:4616#include "base/threading/scoped_blocking_call.h"
fdoraycaa1245b2016-12-23 18:56:4417#include "base/threading/sequenced_task_runner_handle.h"
[email protected]b8ca91f2011-03-18 04:11:4918
[email protected]d0509a582011-10-25 23:35:2719// On some platforms these are not defined.
20#if !defined(EV_RECEIPT)
21#define EV_RECEIPT 0
22#endif
23#if !defined(O_EVTONLY)
24#define O_EVTONLY O_RDONLY
25#endif
26
[email protected]493c8002011-04-14 16:56:0127namespace base {
[email protected]493c8002011-04-14 16:56:0128
[email protected]6d2d92a2014-06-11 07:15:2429FilePathWatcherKQueue::FilePathWatcherKQueue() : kqueue_(-1) {}
[email protected]5199d742010-08-19 10:35:4630
fdoray0ac2f0f82017-01-09 23:38:4231FilePathWatcherKQueue::~FilePathWatcherKQueue() {
Yeola89b2662017-07-25 17:09:1032 DCHECK(!task_runner() || task_runner()->RunsTasksInCurrentSequence());
fdoray0ac2f0f82017-01-09 23:38:4233}
[email protected]5199d742010-08-19 10:35:4634
[email protected]6d2d92a2014-06-11 07:15:2435void FilePathWatcherKQueue::ReleaseEvent(struct kevent& event) {
[email protected]b4e439f2013-04-02 22:17:4936 CloseFileDescriptor(&event.ident);
[email protected]7b3ee8b2011-04-01 18:48:1937 EventData* entry = EventDataForKevent(event);
38 delete entry;
39 event.udata = NULL;
[email protected]5199d742010-08-19 10:35:4640}
41
[email protected]6d2d92a2014-06-11 07:15:2442int FilePathWatcherKQueue::EventsForPath(FilePath path, EventVector* events) {
[email protected]7b3ee8b2011-04-01 18:48:1943 // Make sure that we are working with a clean slate.
44 DCHECK(events->empty());
[email protected]59eee29ea2011-04-01 00:49:3845
[email protected]7b3ee8b2011-04-01 18:48:1946 std::vector<FilePath::StringType> components;
47 path.GetComponents(&components);
48
49 if (components.size() < 1) {
50 return -1;
51 }
52
53 int last_existing_entry = 0;
54 FilePath built_path;
55 bool path_still_exists = true;
[email protected]6d7f55f2013-05-14 10:12:5656 for (std::vector<FilePath::StringType>::iterator i = components.begin();
[email protected]7b3ee8b2011-04-01 18:48:1957 i != components.end(); ++i) {
58 if (i == components.begin()) {
59 built_path = FilePath(*i);
60 } else {
61 built_path = built_path.Append(*i);
62 }
[email protected]b4e439f2013-04-02 22:17:4963 uintptr_t fd = kNoFileDescriptor;
[email protected]7b3ee8b2011-04-01 18:48:1964 if (path_still_exists) {
65 fd = FileDescriptorForPath(built_path);
[email protected]b4e439f2013-04-02 22:17:4966 if (fd == kNoFileDescriptor) {
[email protected]7b3ee8b2011-04-01 18:48:1967 path_still_exists = false;
68 } else {
69 ++last_existing_entry;
70 }
71 }
72 FilePath::StringType subdir = (i != (components.end() - 1)) ? *(i + 1) : "";
73 EventData* data = new EventData(built_path, subdir);
74 struct kevent event;
75 EV_SET(&event, fd, EVFILT_VNODE, (EV_ADD | EV_CLEAR | EV_RECEIPT),
76 (NOTE_DELETE | NOTE_WRITE | NOTE_ATTRIB |
77 NOTE_RENAME | NOTE_REVOKE | NOTE_EXTEND), 0, data);
78 events->push_back(event);
79 }
80 return last_existing_entry;
[email protected]4f962412011-03-31 23:33:3381}
82
[email protected]6d2d92a2014-06-11 07:15:2483uintptr_t FilePathWatcherKQueue::FileDescriptorForPath(const FilePath& path) {
Etienne Bergeron436d42212019-02-26 17:15:1284 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
[email protected]b4e439f2013-04-02 22:17:4985 int fd = HANDLE_EINTR(open(path.value().c_str(), O_EVTONLY));
Lei Zhang26f40fd2019-01-22 23:57:2286 if (fd == kInvalidFd)
[email protected]b4e439f2013-04-02 22:17:4987 return kNoFileDescriptor;
88 return fd;
[email protected]7b3ee8b2011-04-01 18:48:1989}
90
[email protected]6d2d92a2014-06-11 07:15:2491void FilePathWatcherKQueue::CloseFileDescriptor(uintptr_t* fd) {
[email protected]b4e439f2013-04-02 22:17:4992 if (*fd == kNoFileDescriptor) {
[email protected]b8ca91f2011-03-18 04:11:4993 return;
94 }
95
[email protected]d89eec82013-12-03 14:10:5996 if (IGNORE_EINTR(close(*fd)) != 0) {
[email protected]20f06092012-06-04 10:25:3097 DPLOG(ERROR) << "close";
[email protected]7b3ee8b2011-04-01 18:48:1998 }
[email protected]b4e439f2013-04-02 22:17:4999 *fd = kNoFileDescriptor;
[email protected]7b3ee8b2011-04-01 18:48:19100}
[email protected]5199d742010-08-19 10:35:46101
[email protected]6d2d92a2014-06-11 07:15:24102bool FilePathWatcherKQueue::AreKeventValuesValid(struct kevent* kevents,
[email protected]7b3ee8b2011-04-01 18:48:19103 int count) {
104 if (count < 0) {
[email protected]20f06092012-06-04 10:25:30105 DPLOG(ERROR) << "kevent";
[email protected]7b3ee8b2011-04-01 18:48:19106 return false;
107 }
108 bool valid = true;
109 for (int i = 0; i < count; ++i) {
110 if (kevents[i].flags & EV_ERROR && kevents[i].data) {
111 // Find the kevent in |events_| that matches the kevent with the error.
112 EventVector::iterator event = events_.begin();
113 for (; event != events_.end(); ++event) {
114 if (event->ident == kevents[i].ident) {
115 break;
116 }
117 }
118 std::string path_name;
119 if (event != events_.end()) {
120 EventData* event_data = EventDataForKevent(*event);
121 if (event_data != NULL) {
122 path_name = event_data->path_.value();
123 }
124 }
125 if (path_name.empty()) {
126 path_name = base::StringPrintf(
[email protected]b4e439f2013-04-02 22:17:49127 "fd %ld", reinterpret_cast<long>(&kevents[i].ident));
[email protected]7b3ee8b2011-04-01 18:48:19128 }
[email protected]20f06092012-06-04 10:25:30129 DLOG(ERROR) << "Error: " << kevents[i].data << " for " << path_name;
[email protected]7b3ee8b2011-04-01 18:48:19130 valid = false;
[email protected]5199d742010-08-19 10:35:46131 }
[email protected]7b3ee8b2011-04-01 18:48:19132 }
133 return valid;
134}
135
[email protected]6d2d92a2014-06-11 07:15:24136void FilePathWatcherKQueue::HandleAttributesChange(
[email protected]7b3ee8b2011-04-01 18:48:19137 const EventVector::iterator& event,
138 bool* target_file_affected,
139 bool* update_watches) {
140 EventVector::iterator next_event = event + 1;
141 EventData* next_event_data = EventDataForKevent(*next_event);
142 // Check to see if the next item in path is still accessible.
[email protected]b4e439f2013-04-02 22:17:49143 uintptr_t have_access = FileDescriptorForPath(next_event_data->path_);
144 if (have_access == kNoFileDescriptor) {
[email protected]7b3ee8b2011-04-01 18:48:19145 *target_file_affected = true;
146 *update_watches = true;
147 EventVector::iterator local_event(event);
148 for (; local_event != events_.end(); ++local_event) {
149 // Close all nodes from the event down. This has the side effect of
150 // potentially rendering other events in |updates| invalid.
151 // There is no need to remove the events from |kqueue_| because this
152 // happens as a side effect of closing the file descriptor.
[email protected]b4e439f2013-04-02 22:17:49153 CloseFileDescriptor(&local_event->ident);
[email protected]7b3ee8b2011-04-01 18:48:19154 }
155 } else {
156 CloseFileDescriptor(&have_access);
[email protected]5199d742010-08-19 10:35:46157 }
[email protected]d64abe12011-03-22 20:14:06158}
[email protected]5199d742010-08-19 10:35:46159
[email protected]6d2d92a2014-06-11 07:15:24160void FilePathWatcherKQueue::HandleDeleteOrMoveChange(
[email protected]7b3ee8b2011-04-01 18:48:19161 const EventVector::iterator& event,
162 bool* target_file_affected,
163 bool* update_watches) {
164 *target_file_affected = true;
165 *update_watches = true;
166 EventVector::iterator local_event(event);
167 for (; local_event != events_.end(); ++local_event) {
168 // Close all nodes from the event down. This has the side effect of
169 // potentially rendering other events in |updates| invalid.
170 // There is no need to remove the events from |kqueue_| because this
171 // happens as a side effect of closing the file descriptor.
[email protected]b4e439f2013-04-02 22:17:49172 CloseFileDescriptor(&local_event->ident);
[email protected]59eee29ea2011-04-01 00:49:38173 }
[email protected]7b3ee8b2011-04-01 18:48:19174}
[email protected]59eee29ea2011-04-01 00:49:38175
[email protected]6d2d92a2014-06-11 07:15:24176void FilePathWatcherKQueue::HandleCreateItemChange(
[email protected]7b3ee8b2011-04-01 18:48:19177 const EventVector::iterator& event,
178 bool* target_file_affected,
179 bool* update_watches) {
180 // Get the next item in the path.
181 EventVector::iterator next_event = event + 1;
[email protected]7b3ee8b2011-04-01 18:48:19182 // Check to see if it already has a valid file descriptor.
183 if (!IsKeventFileDescriptorOpen(*next_event)) {
[email protected]b4e439f2013-04-02 22:17:49184 EventData* next_event_data = EventDataForKevent(*next_event);
[email protected]7b3ee8b2011-04-01 18:48:19185 // If not, attempt to open a file descriptor for it.
186 next_event->ident = FileDescriptorForPath(next_event_data->path_);
187 if (IsKeventFileDescriptorOpen(*next_event)) {
188 *update_watches = true;
189 if (next_event_data->subdir_.empty()) {
190 *target_file_affected = true;
191 }
192 }
193 }
194}
195
[email protected]6d2d92a2014-06-11 07:15:24196bool FilePathWatcherKQueue::UpdateWatches(bool* target_file_affected) {
[email protected]7b3ee8b2011-04-01 18:48:19197 // Iterate over events adding kevents for items that exist to the kqueue.
198 // Then check to see if new components in the path have been created.
199 // Repeat until no new components in the path are detected.
200 // This is to get around races in directory creation in a watched path.
201 bool update_watches = true;
202 while (update_watches) {
203 size_t valid;
204 for (valid = 0; valid < events_.size(); ++valid) {
205 if (!IsKeventFileDescriptorOpen(events_[valid])) {
206 break;
207 }
208 }
209 if (valid == 0) {
210 // The root of the file path is inaccessible?
211 return false;
212 }
213
214 EventVector updates(valid);
Etienne Bergeron436d42212019-02-26 17:15:12215 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
[email protected]7b3ee8b2011-04-01 18:48:19216 int count = HANDLE_EINTR(kevent(kqueue_, &events_[0], valid, &updates[0],
217 valid, NULL));
218 if (!AreKeventValuesValid(&updates[0], count)) {
219 return false;
220 }
221 update_watches = false;
222 for (; valid < events_.size(); ++valid) {
223 EventData* event_data = EventDataForKevent(events_[valid]);
224 events_[valid].ident = FileDescriptorForPath(event_data->path_);
225 if (IsKeventFileDescriptorOpen(events_[valid])) {
226 update_watches = true;
227 if (event_data->subdir_.empty()) {
228 *target_file_affected = true;
229 }
230 } else {
231 break;
232 }
233 }
234 }
[email protected]59eee29ea2011-04-01 00:49:38235 return true;
236}
237
fdoraya0e4a7e2016-09-30 22:27:32238bool FilePathWatcherKQueue::Watch(const FilePath& path,
239 bool recursive,
240 const FilePathWatcher::Callback& callback) {
241 DCHECK(target_.value().empty()); // Can only watch one path.
242 DCHECK(!callback.is_null());
243 DCHECK_EQ(kqueue_, -1);
244 // Recursive watch is not supported using kqueue.
245 DCHECK(!recursive);
246
247 callback_ = callback;
248 target_ = path;
249
fdoraycaa1245b2016-12-23 18:56:44250 set_task_runner(SequencedTaskRunnerHandle::Get());
fdoraya0e4a7e2016-09-30 22:27:32251
252 kqueue_ = kqueue();
253 if (kqueue_ == -1) {
254 DPLOG(ERROR) << "kqueue";
255 return false;
256 }
257
258 int last_entry = EventsForPath(target_, &events_);
259 DCHECK_NE(last_entry, 0);
260
261 EventVector responses(last_entry);
262
Etienne Bergeron436d42212019-02-26 17:15:12263 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
fdoraya0e4a7e2016-09-30 22:27:32264 int count = HANDLE_EINTR(kevent(kqueue_, &events_[0], last_entry,
265 &responses[0], last_entry, NULL));
266 if (!AreKeventValuesValid(&responses[0], count)) {
267 // Calling Cancel() here to close any file descriptors that were opened.
268 // This would happen in the destructor anyways, but FilePathWatchers tend to
269 // be long lived, and if an error has occurred, there is no reason to waste
270 // the file descriptors.
271 Cancel();
272 return false;
273 }
274
fdoray0ac2f0f82017-01-09 23:38:42275 // It's safe to use Unretained() because the watch is cancelled and the
276 // callback cannot be invoked after |kqueue_watch_controller_| (which is a
277 // member of |this|) has been deleted.
fdoraya0e4a7e2016-09-30 22:27:32278 kqueue_watch_controller_ = FileDescriptorWatcher::WatchReadable(
kylecharb2695fc2019-04-24 14:51:20279 kqueue_, BindRepeating(&FilePathWatcherKQueue::OnKQueueReadable,
280 Unretained(this)));
fdoray0ac2f0f82017-01-09 23:38:42281
fdoraya0e4a7e2016-09-30 22:27:32282 return true;
283}
284
285void FilePathWatcherKQueue::Cancel() {
286 if (!task_runner()) {
287 set_cancelled();
288 return;
289 }
fdoray52e11d482016-11-29 22:19:58290
Yeola89b2662017-07-25 17:09:10291 DCHECK(task_runner()->RunsTasksInCurrentSequence());
fdoray52e11d482016-11-29 22:19:58292 if (!is_cancelled()) {
293 set_cancelled();
294 kqueue_watch_controller_.reset();
295 if (IGNORE_EINTR(close(kqueue_)) != 0) {
296 DPLOG(ERROR) << "close kqueue";
297 }
298 kqueue_ = -1;
299 std::for_each(events_.begin(), events_.end(), ReleaseEvent);
300 events_.clear();
301 callback_.Reset();
fdoraya0e4a7e2016-09-30 22:27:32302 }
fdoraya0e4a7e2016-09-30 22:27:32303}
304
305void FilePathWatcherKQueue::OnKQueueReadable() {
Yeola89b2662017-07-25 17:09:10306 DCHECK(task_runner()->RunsTasksInCurrentSequence());
[email protected]20f06092012-06-04 10:25:30307 DCHECK(events_.size());
[email protected]59eee29ea2011-04-01 00:49:38308
[email protected]7b3ee8b2011-04-01 18:48:19309 // Request the file system update notifications that have occurred and return
310 // them in |updates|. |count| will contain the number of updates that have
311 // occurred.
312 EventVector updates(events_.size());
313 struct timespec timeout = {0, 0};
314 int count = HANDLE_EINTR(kevent(kqueue_, NULL, 0, &updates[0], updates.size(),
315 &timeout));
316
317 // Error values are stored within updates, so check to make sure that no
318 // errors occurred.
319 if (!AreKeventValuesValid(&updates[0], count)) {
[email protected]6b5d0022013-01-15 00:37:47320 callback_.Run(target_, true /* error */);
[email protected]7b3ee8b2011-04-01 18:48:19321 Cancel();
[email protected]59eee29ea2011-04-01 00:49:38322 return;
323 }
324
[email protected]7b3ee8b2011-04-01 18:48:19325 bool update_watches = false;
326 bool send_notification = false;
327
328 // Iterate through each of the updates and react to them.
329 for (int i = 0; i < count; ++i) {
330 // Find our kevent record that matches the update notification.
331 EventVector::iterator event = events_.begin();
332 for (; event != events_.end(); ++event) {
333 if (!IsKeventFileDescriptorOpen(*event) ||
334 event->ident == updates[i].ident) {
335 break;
336 }
337 }
[email protected]b4e439f2013-04-02 22:17:49338 if (event == events_.end() || !IsKeventFileDescriptorOpen(*event)) {
[email protected]7b3ee8b2011-04-01 18:48:19339 // The event may no longer exist in |events_| because another event
340 // modified |events_| in such a way to make it invalid. For example if
341 // the path is /foo/bar/bam and foo is deleted, NOTE_DELETE events for
342 // foo, bar and bam will be sent. If foo is processed first, then
343 // the file descriptors for bar and bam will already be closed and set
344 // to -1 before they get a chance to be processed.
345 continue;
346 }
347
348 EventData* event_data = EventDataForKevent(*event);
349
350 // If the subdir is empty, this is the last item on the path and is the
351 // target file.
352 bool target_file_affected = event_data->subdir_.empty();
353 if ((updates[i].fflags & NOTE_ATTRIB) && !target_file_affected) {
354 HandleAttributesChange(event, &target_file_affected, &update_watches);
355 }
356 if (updates[i].fflags & (NOTE_DELETE | NOTE_REVOKE | NOTE_RENAME)) {
357 HandleDeleteOrMoveChange(event, &target_file_affected, &update_watches);
358 }
359 if ((updates[i].fflags & NOTE_WRITE) && !target_file_affected) {
360 HandleCreateItemChange(event, &target_file_affected, &update_watches);
361 }
362 send_notification |= target_file_affected;
363 }
364
365 if (update_watches) {
366 if (!UpdateWatches(&send_notification)) {
[email protected]6b5d0022013-01-15 00:37:47367 callback_.Run(target_, true /* error */);
[email protected]7b3ee8b2011-04-01 18:48:19368 Cancel();
369 }
370 }
371
372 if (send_notification) {
[email protected]6b5d0022013-01-15 00:37:47373 callback_.Run(target_, false);
[email protected]59eee29ea2011-04-01 00:49:38374 }
375}
376
[email protected]493c8002011-04-14 16:56:01377} // namespace base