1 // Copyright (c) 2011 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.h"
6
7 #include <fcntl.h>
8 #include <sys/event.h>
9 #include <sys/param.h>
10
11 #include <vector>
12
13 #include "base/file_util.h"
14 #include "base/message_loop.h"
15 #include "base/message_loop_proxy.h"
16 #include "base/stringprintf.h"
17
18 namespace base {
19 namespace files {
20
21 namespace {
22
23 // Mac-specific file watcher implementation based on kqueue.
24 // Originally it was based on FSEvents so that the semantics were equivalent
25 // on Linux, OSX and Windows where it was able to detect:
26 // - file creation/deletion/modification in a watched directory
27 // - file creation/deletion/modification for a watched file
28 // - modifications to the paths to a watched object that would affect the
29 // object such as renaming/attibute changes etc.
30 // The FSEvents version did all of the above except handling attribute changes
31 // to path components. Unfortunately FSEvents appears to have an issue where the
32 // current implementation (Mac OS X 10.6.7) sometimes drops events and doesn't
33 // send notifications. See
34 // http://code.google.com/p/chromium/issues/detail?id=54822#c31 for source that
35 // will reproduce the problem. FSEvents also required having a CFRunLoop
36 // backing the thread that it was running on, that caused added complexity
37 // in the interfaces.
38 // The kqueue implementation will handle all of the items in the list above
39 // except for detecting modifications to files in a watched directory. It will
40 // detect the creation and deletion of files, just not the modification of
41 // files. It does however detect the attribute changes that the FSEvents impl
42 // would miss.
43 class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate,
44 public MessageLoopForIO::Watcher,
45 public MessageLoop::DestructionObserver {
46 public:
FilePathWatcherImpl()47 FilePathWatcherImpl() : kqueue_(-1) {}
~FilePathWatcherImpl()48 virtual ~FilePathWatcherImpl() {}
49
50 // MessageLoopForIO::Watcher overrides.
51 virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE;
52 virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE;
53
54 // MessageLoop::DestructionObserver overrides.
55 virtual void WillDestroyCurrentMessageLoop() OVERRIDE;
56
57 // FilePathWatcher::PlatformDelegate overrides.
58 virtual bool Watch(const FilePath& path,
59 FilePathWatcher::Delegate* delegate) OVERRIDE;
60 virtual void Cancel() OVERRIDE;
61
62 private:
63 class EventData {
64 public:
EventData(const FilePath & path,const FilePath::StringType & subdir)65 EventData(const FilePath& path, const FilePath::StringType& subdir)
66 : path_(path), subdir_(subdir) { }
67 FilePath path_; // Full path to this item.
68 FilePath::StringType subdir_; // Path to any sub item.
69 };
70 typedef std::vector<struct kevent> EventVector;
71
72 // Can only be called on |io_message_loop_|'s thread.
73 virtual void CancelOnMessageLoopThread() OVERRIDE;
74
75 // Returns true if the kevent values are error free.
76 bool AreKeventValuesValid(struct kevent* kevents, int count);
77
78 // Respond to a change of attributes of the path component represented by
79 // |event|. Sets |target_file_affected| to true if |target_| is affected.
80 // Sets |update_watches| to true if |events_| need to be updated.
81 void HandleAttributesChange(const EventVector::iterator& event,
82 bool* target_file_affected,
83 bool* update_watches);
84
85 // Respond to a move of deletion of the path component represented by
86 // |event|. Sets |target_file_affected| to true if |target_| is affected.
87 // Sets |update_watches| to true if |events_| need to be updated.
88 void HandleDeleteOrMoveChange(const EventVector::iterator& event,
89 bool* target_file_affected,
90 bool* update_watches);
91
92 // Respond to a creation of an item in the path component represented by
93 // |event|. Sets |target_file_affected| to true if |target_| is affected.
94 // Sets |update_watches| to true if |events_| need to be updated.
95 void HandleCreateItemChange(const EventVector::iterator& event,
96 bool* target_file_affected,
97 bool* update_watches);
98
99 // Update |events_| with the current status of the system.
100 // Sets |target_file_affected| to true if |target_| is affected.
101 // Returns false if an error occurs.
102 bool UpdateWatches(bool* target_file_affected);
103
104 // Fills |events| with one kevent per component in |path|.
105 // Returns the number of valid events created where a valid event is
106 // defined as one that has a ident (file descriptor) field != -1.
107 static int EventsForPath(FilePath path, EventVector *events);
108
109 // Release a kevent generated by EventsForPath.
110 static void ReleaseEvent(struct kevent& event);
111
112 // Returns a file descriptor that will not block the system from deleting
113 // the file it references.
114 static int FileDescriptorForPath(const FilePath& path);
115
116 // Closes |*fd| and sets |*fd| to -1.
117 static void CloseFileDescriptor(int* fd);
118
119 // Returns true if kevent has open file descriptor.
IsKeventFileDescriptorOpen(const struct kevent & event)120 static bool IsKeventFileDescriptorOpen(const struct kevent& event) {
121 return event.ident != static_cast<uintptr_t>(-1);
122 }
123
EventDataForKevent(const struct kevent & event)124 static EventData* EventDataForKevent(const struct kevent& event) {
125 return reinterpret_cast<EventData*>(event.udata);
126 }
127
128 EventVector events_;
129 scoped_refptr<base::MessageLoopProxy> io_message_loop_;
130 MessageLoopForIO::FileDescriptorWatcher kqueue_watcher_;
131 scoped_refptr<FilePathWatcher::Delegate> delegate_;
132 FilePath target_;
133 int kqueue_;
134
135 DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl);
136 };
137
ReleaseEvent(struct kevent & event)138 void FilePathWatcherImpl::ReleaseEvent(struct kevent& event) {
139 CloseFileDescriptor(reinterpret_cast<int*>(&event.ident));
140 EventData* entry = EventDataForKevent(event);
141 delete entry;
142 event.udata = NULL;
143 }
144
EventsForPath(FilePath path,EventVector * events)145 int FilePathWatcherImpl::EventsForPath(FilePath path, EventVector* events) {
146 DCHECK(MessageLoopForIO::current());
147 // Make sure that we are working with a clean slate.
148 DCHECK(events->empty());
149
150 std::vector<FilePath::StringType> components;
151 path.GetComponents(&components);
152
153 if (components.size() < 1) {
154 return -1;
155 }
156
157 int last_existing_entry = 0;
158 FilePath built_path;
159 bool path_still_exists = true;
160 for(std::vector<FilePath::StringType>::iterator i = components.begin();
161 i != components.end(); ++i) {
162 if (i == components.begin()) {
163 built_path = FilePath(*i);
164 } else {
165 built_path = built_path.Append(*i);
166 }
167 int fd = -1;
168 if (path_still_exists) {
169 fd = FileDescriptorForPath(built_path);
170 if (fd == -1) {
171 path_still_exists = false;
172 } else {
173 ++last_existing_entry;
174 }
175 }
176 FilePath::StringType subdir = (i != (components.end() - 1)) ? *(i + 1) : "";
177 EventData* data = new EventData(built_path, subdir);
178 struct kevent event;
179 EV_SET(&event, fd, EVFILT_VNODE, (EV_ADD | EV_CLEAR | EV_RECEIPT),
180 (NOTE_DELETE | NOTE_WRITE | NOTE_ATTRIB |
181 NOTE_RENAME | NOTE_REVOKE | NOTE_EXTEND), 0, data);
182 events->push_back(event);
183 }
184 return last_existing_entry;
185 }
186
FileDescriptorForPath(const FilePath & path)187 int FilePathWatcherImpl::FileDescriptorForPath(const FilePath& path) {
188 return HANDLE_EINTR(open(path.value().c_str(), O_EVTONLY));
189 }
190
CloseFileDescriptor(int * fd)191 void FilePathWatcherImpl::CloseFileDescriptor(int *fd) {
192 if (*fd == -1) {
193 return;
194 }
195
196 if (HANDLE_EINTR(close(*fd)) != 0) {
197 PLOG(ERROR) << "close";
198 }
199 *fd = -1;
200 }
201
AreKeventValuesValid(struct kevent * kevents,int count)202 bool FilePathWatcherImpl::AreKeventValuesValid(struct kevent* kevents,
203 int count) {
204 if (count < 0) {
205 PLOG(ERROR) << "kevent";
206 return false;
207 }
208 bool valid = true;
209 for (int i = 0; i < count; ++i) {
210 if (kevents[i].flags & EV_ERROR && kevents[i].data) {
211 // Find the kevent in |events_| that matches the kevent with the error.
212 EventVector::iterator event = events_.begin();
213 for (; event != events_.end(); ++event) {
214 if (event->ident == kevents[i].ident) {
215 break;
216 }
217 }
218 std::string path_name;
219 if (event != events_.end()) {
220 EventData* event_data = EventDataForKevent(*event);
221 if (event_data != NULL) {
222 path_name = event_data->path_.value();
223 }
224 }
225 if (path_name.empty()) {
226 path_name = base::StringPrintf(
227 "fd %d", *reinterpret_cast<int*>(&kevents[i].ident));
228 }
229 LOG(ERROR) << "Error: " << kevents[i].data << " for " << path_name;
230 valid = false;
231 }
232 }
233 return valid;
234 }
235
HandleAttributesChange(const EventVector::iterator & event,bool * target_file_affected,bool * update_watches)236 void FilePathWatcherImpl::HandleAttributesChange(
237 const EventVector::iterator& event,
238 bool* target_file_affected,
239 bool* update_watches) {
240 EventVector::iterator next_event = event + 1;
241 EventData* next_event_data = EventDataForKevent(*next_event);
242 // Check to see if the next item in path is still accessible.
243 int have_access = FileDescriptorForPath(next_event_data->path_);
244 if (have_access == -1) {
245 *target_file_affected = true;
246 *update_watches = true;
247 EventVector::iterator local_event(event);
248 for (; local_event != events_.end(); ++local_event) {
249 // Close all nodes from the event down. This has the side effect of
250 // potentially rendering other events in |updates| invalid.
251 // There is no need to remove the events from |kqueue_| because this
252 // happens as a side effect of closing the file descriptor.
253 CloseFileDescriptor(reinterpret_cast<int*>(&local_event->ident));
254 }
255 } else {
256 CloseFileDescriptor(&have_access);
257 }
258 }
259
HandleDeleteOrMoveChange(const EventVector::iterator & event,bool * target_file_affected,bool * update_watches)260 void FilePathWatcherImpl::HandleDeleteOrMoveChange(
261 const EventVector::iterator& event,
262 bool* target_file_affected,
263 bool* update_watches) {
264 *target_file_affected = true;
265 *update_watches = true;
266 EventVector::iterator local_event(event);
267 for (; local_event != events_.end(); ++local_event) {
268 // Close all nodes from the event down. This has the side effect of
269 // potentially rendering other events in |updates| invalid.
270 // There is no need to remove the events from |kqueue_| because this
271 // happens as a side effect of closing the file descriptor.
272 CloseFileDescriptor(reinterpret_cast<int*>(&local_event->ident));
273 }
274 }
275
HandleCreateItemChange(const EventVector::iterator & event,bool * target_file_affected,bool * update_watches)276 void FilePathWatcherImpl::HandleCreateItemChange(
277 const EventVector::iterator& event,
278 bool* target_file_affected,
279 bool* update_watches) {
280 // Get the next item in the path.
281 EventVector::iterator next_event = event + 1;
282 EventData* next_event_data = EventDataForKevent(*next_event);
283
284 // Check to see if it already has a valid file descriptor.
285 if (!IsKeventFileDescriptorOpen(*next_event)) {
286 // If not, attempt to open a file descriptor for it.
287 next_event->ident = FileDescriptorForPath(next_event_data->path_);
288 if (IsKeventFileDescriptorOpen(*next_event)) {
289 *update_watches = true;
290 if (next_event_data->subdir_.empty()) {
291 *target_file_affected = true;
292 }
293 }
294 }
295 }
296
UpdateWatches(bool * target_file_affected)297 bool FilePathWatcherImpl::UpdateWatches(bool* target_file_affected) {
298 // Iterate over events adding kevents for items that exist to the kqueue.
299 // Then check to see if new components in the path have been created.
300 // Repeat until no new components in the path are detected.
301 // This is to get around races in directory creation in a watched path.
302 bool update_watches = true;
303 while (update_watches) {
304 size_t valid;
305 for (valid = 0; valid < events_.size(); ++valid) {
306 if (!IsKeventFileDescriptorOpen(events_[valid])) {
307 break;
308 }
309 }
310 if (valid == 0) {
311 // The root of the file path is inaccessible?
312 return false;
313 }
314
315 EventVector updates(valid);
316 int count = HANDLE_EINTR(kevent(kqueue_, &events_[0], valid, &updates[0],
317 valid, NULL));
318 if (!AreKeventValuesValid(&updates[0], count)) {
319 return false;
320 }
321 update_watches = false;
322 for (; valid < events_.size(); ++valid) {
323 EventData* event_data = EventDataForKevent(events_[valid]);
324 events_[valid].ident = FileDescriptorForPath(event_data->path_);
325 if (IsKeventFileDescriptorOpen(events_[valid])) {
326 update_watches = true;
327 if (event_data->subdir_.empty()) {
328 *target_file_affected = true;
329 }
330 } else {
331 break;
332 }
333 }
334 }
335 return true;
336 }
337
OnFileCanReadWithoutBlocking(int fd)338 void FilePathWatcherImpl::OnFileCanReadWithoutBlocking(int fd) {
339 DCHECK(MessageLoopForIO::current());
340 CHECK_EQ(fd, kqueue_);
341 CHECK(events_.size());
342
343 // Request the file system update notifications that have occurred and return
344 // them in |updates|. |count| will contain the number of updates that have
345 // occurred.
346 EventVector updates(events_.size());
347 struct timespec timeout = {0, 0};
348 int count = HANDLE_EINTR(kevent(kqueue_, NULL, 0, &updates[0], updates.size(),
349 &timeout));
350
351 // Error values are stored within updates, so check to make sure that no
352 // errors occurred.
353 if (!AreKeventValuesValid(&updates[0], count)) {
354 delegate_->OnFilePathError(target_);
355 Cancel();
356 return;
357 }
358
359 bool update_watches = false;
360 bool send_notification = false;
361
362 // Iterate through each of the updates and react to them.
363 for (int i = 0; i < count; ++i) {
364 // Find our kevent record that matches the update notification.
365 EventVector::iterator event = events_.begin();
366 for (; event != events_.end(); ++event) {
367 if (!IsKeventFileDescriptorOpen(*event) ||
368 event->ident == updates[i].ident) {
369 break;
370 }
371 }
372 if (!IsKeventFileDescriptorOpen(*event) || event == events_.end()) {
373 // The event may no longer exist in |events_| because another event
374 // modified |events_| in such a way to make it invalid. For example if
375 // the path is /foo/bar/bam and foo is deleted, NOTE_DELETE events for
376 // foo, bar and bam will be sent. If foo is processed first, then
377 // the file descriptors for bar and bam will already be closed and set
378 // to -1 before they get a chance to be processed.
379 continue;
380 }
381
382 EventData* event_data = EventDataForKevent(*event);
383
384 // If the subdir is empty, this is the last item on the path and is the
385 // target file.
386 bool target_file_affected = event_data->subdir_.empty();
387 if ((updates[i].fflags & NOTE_ATTRIB) && !target_file_affected) {
388 HandleAttributesChange(event, &target_file_affected, &update_watches);
389 }
390 if (updates[i].fflags & (NOTE_DELETE | NOTE_REVOKE | NOTE_RENAME)) {
391 HandleDeleteOrMoveChange(event, &target_file_affected, &update_watches);
392 }
393 if ((updates[i].fflags & NOTE_WRITE) && !target_file_affected) {
394 HandleCreateItemChange(event, &target_file_affected, &update_watches);
395 }
396 send_notification |= target_file_affected;
397 }
398
399 if (update_watches) {
400 if (!UpdateWatches(&send_notification)) {
401 delegate_->OnFilePathError(target_);
402 Cancel();
403 }
404 }
405
406 if (send_notification) {
407 delegate_->OnFilePathChanged(target_);
408 }
409 }
410
OnFileCanWriteWithoutBlocking(int fd)411 void FilePathWatcherImpl::OnFileCanWriteWithoutBlocking(int fd) {
412 NOTREACHED();
413 }
414
WillDestroyCurrentMessageLoop()415 void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() {
416 CancelOnMessageLoopThread();
417 }
418
Watch(const FilePath & path,FilePathWatcher::Delegate * delegate)419 bool FilePathWatcherImpl::Watch(const FilePath& path,
420 FilePathWatcher::Delegate* delegate) {
421 DCHECK(MessageLoopForIO::current());
422 DCHECK(target_.value().empty()); // Can only watch one path.
423 DCHECK(delegate);
424 DCHECK_EQ(kqueue_, -1);
425
426 delegate_ = delegate;
427 target_ = path;
428
429 MessageLoop::current()->AddDestructionObserver(this);
430 io_message_loop_ = base::MessageLoopProxy::CreateForCurrentThread();
431
432 kqueue_ = kqueue();
433 if (kqueue_ == -1) {
434 PLOG(ERROR) << "kqueue";
435 return false;
436 }
437
438 int last_entry = EventsForPath(target_, &events_);
439 CHECK_NE(last_entry, 0);
440
441 EventVector responses(last_entry);
442
443 int count = HANDLE_EINTR(kevent(kqueue_, &events_[0], last_entry,
444 &responses[0], last_entry, NULL));
445 if (!AreKeventValuesValid(&responses[0], count)) {
446 // Calling Cancel() here to close any file descriptors that were opened.
447 // This would happen in the destructor anyways, but FilePathWatchers tend to
448 // be long lived, and if an error has occurred, there is no reason to waste
449 // the file descriptors.
450 Cancel();
451 return false;
452 }
453
454 return MessageLoopForIO::current()->WatchFileDescriptor(
455 kqueue_, true, MessageLoopForIO::WATCH_READ, &kqueue_watcher_, this);
456 }
457
Cancel()458 void FilePathWatcherImpl::Cancel() {
459 base::MessageLoopProxy* proxy = io_message_loop_.get();
460 if (!proxy) {
461 set_cancelled();
462 return;
463 }
464 if (!proxy->BelongsToCurrentThread()) {
465 proxy->PostTask(FROM_HERE,
466 NewRunnableMethod(this, &FilePathWatcherImpl::Cancel));
467 return;
468 }
469 CancelOnMessageLoopThread();
470 }
471
CancelOnMessageLoopThread()472 void FilePathWatcherImpl::CancelOnMessageLoopThread() {
473 DCHECK(MessageLoopForIO::current());
474 if (!is_cancelled()) {
475 set_cancelled();
476 kqueue_watcher_.StopWatchingFileDescriptor();
477 CloseFileDescriptor(&kqueue_);
478 std::for_each(events_.begin(), events_.end(), ReleaseEvent);
479 events_.clear();
480 io_message_loop_ = NULL;
481 MessageLoop::current()->RemoveDestructionObserver(this);
482 delegate_ = NULL;
483 }
484 }
485
486 } // namespace
487
FilePathWatcher()488 FilePathWatcher::FilePathWatcher() {
489 impl_ = new FilePathWatcherImpl();
490 }
491
492 } // namespace files
493 } // namespace base
494