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