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 18namespace base { 19namespace files { 20 21namespace { 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. 43class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate, 44 public MessageLoopForIO::Watcher, 45 public MessageLoop::DestructionObserver { 46 public: 47 FilePathWatcherImpl() : kqueue_(-1) {} 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: 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. 120 static bool IsKeventFileDescriptorOpen(const struct kevent& event) { 121 return event.ident != static_cast<uintptr_t>(-1); 122 } 123 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 138void 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 145int 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 187int FilePathWatcherImpl::FileDescriptorForPath(const FilePath& path) { 188 return HANDLE_EINTR(open(path.value().c_str(), O_EVTONLY)); 189} 190 191void 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 202bool 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 236void 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 260void 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 276void 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 297bool 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 338void 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 411void FilePathWatcherImpl::OnFileCanWriteWithoutBlocking(int fd) { 412 NOTREACHED(); 413} 414 415void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() { 416 CancelOnMessageLoopThread(); 417} 418 419bool 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 458void 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 472void 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 488FilePathWatcher::FilePathWatcher() { 489 impl_ = new FilePathWatcherImpl(); 490} 491 492} // namespace files 493} // namespace base 494