| OLD | NEW |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/directory_watcher.h" | 5 #include "base/directory_watcher.h" |
| 6 | 6 |
| 7 #include <errno.h> | 7 #include <errno.h> |
| 8 #include <string.h> | 8 #include <string.h> |
| 9 #include <sys/inotify.h> | 9 #include <sys/inotify.h> |
| 10 #include <sys/ioctl.h> | 10 #include <sys/ioctl.h> |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 81 DISALLOW_COPY_AND_ASSIGN(InotifyReader); | 81 DISALLOW_COPY_AND_ASSIGN(InotifyReader); |
| 82 }; | 82 }; |
| 83 | 83 |
| 84 class DirectoryWatcherImpl : public DirectoryWatcher::PlatformDelegate { | 84 class DirectoryWatcherImpl : public DirectoryWatcher::PlatformDelegate { |
| 85 public: | 85 public: |
| 86 typedef std::set<FilePath> FilePathSet; | 86 typedef std::set<FilePath> FilePathSet; |
| 87 | 87 |
| 88 DirectoryWatcherImpl(); | 88 DirectoryWatcherImpl(); |
| 89 ~DirectoryWatcherImpl(); | 89 ~DirectoryWatcherImpl(); |
| 90 | 90 |
| 91 void EnsureSetupFinished(); | |
| 92 | |
| 93 // Called for each event coming from one of watches. | 91 // Called for each event coming from one of watches. |
| 94 void OnInotifyEvent(const inotify_event* event); | 92 void OnInotifyEvent(const inotify_event* event); |
| 95 | 93 |
| 96 // Callback for RegisterSubtreeWatchesTask. | 94 // Callback for RegisterSubtreeWatchesTask. |
| 97 bool OnEnumeratedSubtree(const FilePathSet& paths); | 95 bool OnEnumeratedSubtree(const FilePathSet& paths); |
| 98 | 96 |
| 99 // Start watching |path| for changes and notify |delegate| on each change. | 97 // Start watching |path| for changes and notify |delegate| on each change. |
| 100 // If |recursive| is true, watch entire subtree. | 98 // If |recursive| is true, watch entire subtree. |
| 101 // Returns true if watch for |path| has been added successfully. Watches | 99 // Returns true if watch for |path| has been added successfully. Watches |
| 102 // required for |recursive| are added on a background thread and have no | 100 // required for |recursive| are added on a background thread and have no |
| (...skipping 10 matching lines...) Expand all Loading... |
| 113 | 111 |
| 114 // Delegate to notify upon changes. | 112 // Delegate to notify upon changes. |
| 115 DirectoryWatcher::Delegate* delegate_; | 113 DirectoryWatcher::Delegate* delegate_; |
| 116 | 114 |
| 117 // Path we're watching (passed to delegate). | 115 // Path we're watching (passed to delegate). |
| 118 FilePath root_path_; | 116 FilePath root_path_; |
| 119 | 117 |
| 120 // Watch returned by InotifyReader. | 118 // Watch returned by InotifyReader. |
| 121 InotifyReader::Watch watch_; | 119 InotifyReader::Watch watch_; |
| 122 | 120 |
| 123 // Set of watched inodes. | 121 // Needed because inotify event-> name only provides name of the |
| 124 InodeSet inodes_watched_; | 122 // newly created/deleted file, without any path information. |
| 123 // event->name = SubDir |
| 124 // With this data structure we can get /tmp/DirectoryWatcherTest/SubDir. |
| 125 base::hash_map<InotifyReader::Watch, FilePath> paths_; |
| 125 | 126 |
| 126 // Keep track of registered watches. | 127 typedef std::pair<ino_t, FilePath> InodeInfo; |
| 127 WatchSet watches_; | 128 base::hash_map<InotifyReader::Watch, InodeInfo> watches_; |
| 128 | 129 |
| 129 // Lock to protect inodes_watched_ and watches_. | 130 // Lock to protect inodes_watched_ and watches_. |
| 130 Lock lock_; | 131 Lock lock_; |
| 131 | 132 |
| 132 // Flag set to true when recursively watching subtree. | 133 // Flag set to true when recursively watching subtree. |
| 133 bool recursive_; | 134 bool recursive_; |
| 134 | 135 |
| 135 // Loop where we post directory change notifications to. | 136 // Loop where we post directory change notifications to. |
| 136 MessageLoop* loop_; | 137 MessageLoop* loop_; |
| 137 | 138 |
| (...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 295 IN_CREATE | IN_DELETE | | 296 IN_CREATE | IN_DELETE | |
| 296 IN_CLOSE_WRITE | IN_MOVE); | 297 IN_CLOSE_WRITE | IN_MOVE); |
| 297 | 298 |
| 298 if (watch == kInvalidWatch) | 299 if (watch == kInvalidWatch) |
| 299 return kInvalidWatch; | 300 return kInvalidWatch; |
| 300 | 301 |
| 301 if (paths_[watch].empty()) | 302 if (paths_[watch].empty()) |
| 302 paths_[watch] = path; // We don't yet watch this path. | 303 paths_[watch] = path; // We don't yet watch this path. |
| 303 | 304 |
| 304 watchers_[watch].insert(watcher); | 305 watchers_[watch].insert(watcher); |
| 305 | |
| 306 return watch; | 306 return watch; |
| 307 } | 307 } |
| 308 | 308 |
| 309 bool InotifyReader::RemoveWatch(Watch watch, | 309 bool InotifyReader::RemoveWatch(Watch watch, |
| 310 DirectoryWatcherImpl* watcher) { | 310 DirectoryWatcherImpl* watcher) { |
| 311 if (!valid_) | 311 if (!valid_) |
| 312 return false; | 312 return false; |
| 313 | 313 |
| 314 AutoLock auto_lock(lock_); | 314 AutoLock auto_lock(lock_); |
| 315 | 315 |
| 316 if (paths_[watch].empty()) | 316 if (paths_[watch].empty()) |
| 317 return false; // We don't recognize this watch. | 317 return false; // We don't recognize this watch. |
| 318 | 318 |
| 319 watchers_[watch].erase(watcher); | 319 watchers_[watch].erase(watcher); |
| 320 | 320 |
| 321 if (watchers_[watch].empty()) { | 321 if (watchers_[watch].empty()) { |
| 322 paths_.erase(watch); | 322 paths_.erase(watch); |
| 323 watchers_.erase(watch); | 323 watchers_.erase(watch); |
| 324 return (inotify_rm_watch(inotify_fd_, watch) == 0); | 324 return (inotify_rm_watch(inotify_fd_, watch) == 0); |
| 325 } | 325 } |
| 326 | |
| 327 return true; | 326 return true; |
| 328 } | 327 } |
| 329 | 328 |
| 330 void InotifyReader::OnInotifyEvent(const inotify_event* event) { | 329 void InotifyReader::OnInotifyEvent(const inotify_event* event) { |
| 331 if (event->mask & IN_IGNORED) | 330 if (event->mask & IN_IGNORED) |
| 332 return; | 331 return; |
| 333 | 332 |
| 334 WatcherSet watchers_to_notify; | 333 WatcherSet watchers_to_notify; |
| 335 FilePath changed_path; | 334 FilePath changed_path; |
| 336 | 335 |
| 337 { | 336 { |
| 338 AutoLock auto_lock(lock_); | 337 AutoLock auto_lock(lock_); |
| 339 changed_path = paths_[event->wd]; | 338 changed_path = paths_[event->wd]; |
| 340 watchers_to_notify.insert(watchers_[event->wd].begin(), | 339 watchers_to_notify.insert(watchers_[event->wd].begin(), |
| 341 watchers_[event->wd].end()); | 340 watchers_[event->wd].end()); |
| 342 } | 341 } |
| 343 | 342 |
| 344 for (WatcherSet::iterator watcher = watchers_to_notify.begin(); | 343 for (WatcherSet::const_iterator watcher = watchers_to_notify.begin(); |
| 345 watcher != watchers_to_notify.end(); | 344 watcher != watchers_to_notify.end(); |
| 346 ++watcher) { | 345 ++watcher) { |
| 347 (*watcher)->OnInotifyEvent(event); | 346 (*watcher)->OnInotifyEvent(event); |
| 348 } | 347 } |
| 349 } | 348 } |
| 350 | 349 |
| 351 DirectoryWatcherImpl::DirectoryWatcherImpl() | 350 DirectoryWatcherImpl::DirectoryWatcherImpl() |
| 352 : watch_(InotifyReader::kInvalidWatch), | 351 : watch_(InotifyReader::kInvalidWatch), |
| 353 recursive_setup_finished_(false, false) { | 352 recursive_setup_finished_(false, false) { |
| 354 } | 353 } |
| 355 | 354 |
| 356 DirectoryWatcherImpl::~DirectoryWatcherImpl() { | 355 DirectoryWatcherImpl::~DirectoryWatcherImpl() { |
| 357 if (watch_ == InotifyReader::kInvalidWatch) | 356 if (watch_ == InotifyReader::kInvalidWatch) |
| 358 return; | 357 return; |
| 359 | 358 |
| 360 if (recursive_) | 359 if (recursive_) |
| 361 recursive_setup_finished_.Wait(); | 360 recursive_setup_finished_.Wait(); |
| 362 for (WatchSet::iterator watch = watches_.begin(); | 361 |
| 363 watch != watches_.end(); | 362 for (base::hash_map<InotifyReader::Watch, InodeInfo>::const_iterator it = |
| 364 ++watch) { | 363 watches_.begin(); |
| 365 Singleton<InotifyReader>::get()->RemoveWatch(*watch, this); | 364 it != watches_.end(); |
| 365 ++it) { |
| 366 Singleton<InotifyReader>::get()->RemoveWatch(it->first, this); |
| 366 } | 367 } |
| 367 watches_.clear(); | 368 watches_.clear(); |
| 368 inodes_watched_.clear(); | |
| 369 } | 369 } |
| 370 | 370 |
| 371 void DirectoryWatcherImpl::OnInotifyEvent(const inotify_event* event) { | 371 void DirectoryWatcherImpl::OnInotifyEvent(const inotify_event* event) { |
| 372 loop_->PostTask(FROM_HERE, | 372 loop_->PostTask(FROM_HERE, |
| 373 new DirectoryWatcherImplNotifyTask(delegate_, root_path_)); | 373 new DirectoryWatcherImplNotifyTask(delegate_, root_path_)); |
| 374 | 374 |
| 375 if (!(event->mask & IN_ISDIR)) | 375 if (!(event->mask & IN_ISDIR)) |
| 376 return; | 376 return; |
| 377 | 377 |
| 378 FilePath path(watches_[event->wd].second); |
| 379 path.Append(event->name); |
| 378 if (event->mask & IN_CREATE || event->mask & IN_MOVED_TO) { | 380 if (event->mask & IN_CREATE || event->mask & IN_MOVED_TO) { |
| 379 // TODO(phajdan.jr): add watch for this new directory. | 381 // Don't do anything, if not recursive watch; |
| 380 NOTIMPLEMENTED(); | 382 if (!recursive_) |
| 383 return; |
| 384 if (event->mask & IN_MOVED_TO) { |
| 385 // Don't do anything, if path is moved outside of root_path_. |
| 386 if (path.value().compare(0, root_path_.value().length(), |
| 387 root_path_.value())) |
| 388 return; |
| 389 } |
| 390 recursive_setup_finished_.Wait(); |
| 391 scoped_ptr<Task> subtree_task(new RegisterSubtreeWatchesTask(this, path)); |
| 392 subtree_task->Run(); |
| 381 } else if (event->mask & IN_DELETE || event->mask & IN_MOVED_FROM) { | 393 } else if (event->mask & IN_DELETE || event->mask & IN_MOVED_FROM) { |
| 382 // TODO(phajdan.jr): remove our watch for this directory. | 394 Singleton<InotifyReader>::get()->RemoveWatch(event->wd, this); |
| 383 NOTIMPLEMENTED(); | 395 |
| 396 WatchSet delete_us; |
| 397 AutoLock auto_lock(lock_); |
| 398 watches_.erase(event->wd); |
| 399 // Iterating on paths_ to find children wathces because inotify only fires |
| 400 // an event for the parent directory, when a directory subtree is deleted. |
| 401 for (base::hash_map<InotifyReader::Watch, InodeInfo>::const_iterator it = |
| 402 watches_.begin(); |
| 403 it != watches_.end(); |
| 404 ++it) { |
| 405 if (it->second.second.value().compare(0, path.value().length(), |
| 406 path.value())) |
| 407 continue; |
| 408 delete_us.insert(it->first); |
| 409 Singleton<InotifyReader>::get()->RemoveWatch(it->first, this); |
| 410 } |
| 411 for (WatchSet::const_iterator it = delete_us.begin(); |
| 412 it != delete_us.end(); ++it) { |
| 413 watches_.erase(*it); |
| 414 } |
| 384 } | 415 } |
| 385 } | 416 } |
| 386 | 417 |
| 387 bool DirectoryWatcherImpl::IsInodeWatched(ino_t inode) const { | 418 bool DirectoryWatcherImpl::IsInodeWatched(ino_t inode) const { |
| 388 return inodes_watched_.find(inode) != inodes_watched_.end(); | 419 for (base::hash_map<InotifyReader::Watch, InodeInfo>::const_iterator it = |
| 420 watches_.begin(); |
| 421 it != watches_.end(); |
| 422 ++it) { |
| 423 if (it->second.first == inode) |
| 424 return true; |
| 425 } |
| 426 return false; |
| 389 } | 427 } |
| 390 | 428 |
| 391 bool DirectoryWatcherImpl::OnEnumeratedSubtree(const FilePathSet& subtree) { | 429 bool DirectoryWatcherImpl::OnEnumeratedSubtree(const FilePathSet& subtree) { |
| 392 DCHECK(recursive_); | 430 DCHECK(recursive_); |
| 393 | 431 |
| 394 if (watch_ == InotifyReader::kInvalidWatch) { | 432 if (watch_ == InotifyReader::kInvalidWatch) { |
| 395 recursive_setup_finished_.Signal(); | 433 recursive_setup_finished_.Signal(); |
| 396 return false; | 434 return false; |
| 397 } | 435 } |
| 398 | |
| 399 bool success = true; | 436 bool success = true; |
| 400 AutoLock auto_lock(lock_); | 437 for (FilePathSet::const_iterator subdirectory = subtree.begin(); |
| 401 for (FilePathSet::iterator subdirectory = subtree.begin(); | |
| 402 subdirectory != subtree.end(); | 438 subdirectory != subtree.end(); |
| 403 ++subdirectory) { | 439 ++subdirectory) { |
| 404 ino_t inode; | 440 ino_t inode; |
| 405 if (!file_util::GetInode(*subdirectory, &inode)) { | 441 if (!file_util::GetInode(*subdirectory, &inode)) { |
| 406 success = false; | 442 success = false; |
| 407 continue; | 443 continue; |
| 408 } | 444 } |
| 409 if (IsInodeWatched(inode)) | 445 if (IsInodeWatched(inode)) |
| 410 continue; | 446 continue; |
| 411 InotifyReader::Watch watch = | 447 InotifyReader::Watch watch = |
| 412 Singleton<InotifyReader>::get()->AddWatch(*subdirectory, this); | 448 Singleton<InotifyReader>::get()->AddWatch(*subdirectory, this); |
| 413 if (watch != InotifyReader::kInvalidWatch) { | 449 if (watch != InotifyReader::kInvalidWatch) { |
| 414 watches_.insert(watch); | 450 { |
| 415 inodes_watched_.insert(inode); | 451 AutoLock auto_lock(lock_); |
| 452 watches_[watch] = std::make_pair(inode, *subdirectory); |
| 453 } |
| 416 } | 454 } |
| 417 } | 455 } |
| 418 recursive_setup_finished_.Signal(); | 456 recursive_setup_finished_.Signal(); |
| 419 return success; | 457 return success; |
| 420 } | 458 } |
| 421 | 459 |
| 422 bool DirectoryWatcherImpl::Watch(const FilePath& path, | 460 bool DirectoryWatcherImpl::Watch(const FilePath& path, |
| 423 DirectoryWatcher::Delegate* delegate, | 461 DirectoryWatcher::Delegate* delegate, |
| 424 MessageLoop* backend_loop, bool recursive) { | 462 MessageLoop* backend_loop, bool recursive) { |
| 425 | |
| 426 // Can only watch one path. | 463 // Can only watch one path. |
| 427 DCHECK(watch_ == InotifyReader::kInvalidWatch); | 464 DCHECK(watch_ == InotifyReader::kInvalidWatch); |
| 428 | 465 |
| 429 ino_t inode; | 466 ino_t inode; |
| 430 if (!file_util::GetInode(path, &inode)) | 467 if (!file_util::GetInode(path, &inode)) |
| 431 return false; | 468 return false; |
| 432 | 469 |
| 433 InotifyReader::Watch watch = | 470 InotifyReader::Watch watch = |
| 434 Singleton<InotifyReader>::get()->AddWatch(path, this); | 471 Singleton<InotifyReader>::get()->AddWatch(path, this); |
| 435 if (watch == InotifyReader::kInvalidWatch) | 472 if (watch == InotifyReader::kInvalidWatch) |
| 436 return false; | 473 return false; |
| 437 | 474 |
| 438 delegate_ = delegate; | 475 delegate_ = delegate; |
| 439 recursive_ = recursive; | 476 recursive_ = recursive; |
| 440 root_path_ = path; | 477 root_path_ = path; |
| 441 watch_ = watch; | 478 watch_ = watch; |
| 442 loop_ = MessageLoop::current(); | 479 loop_ = MessageLoop::current(); |
| 443 | 480 |
| 444 { | 481 { |
| 445 AutoLock auto_lock(lock_); | 482 AutoLock auto_lock(lock_); |
| 446 inodes_watched_.insert(inode); | 483 watches_[watch_] = std::make_pair(inode, root_path_); |
| 447 watches_.insert(watch_); | |
| 448 } | 484 } |
| 449 | 485 |
| 450 if (recursive_) { | 486 if (recursive_) { |
| 451 Task* subtree_task = new RegisterSubtreeWatchesTask(this, root_path_); | 487 Task* subtree_task = new RegisterSubtreeWatchesTask(this, root_path_); |
| 452 if (backend_loop) { | 488 if (backend_loop) { |
| 453 backend_loop->PostTask(FROM_HERE, subtree_task); | 489 backend_loop->PostTask(FROM_HERE, subtree_task); |
| 454 } else { | 490 } else { |
| 455 subtree_task->Run(); | 491 subtree_task->Run(); |
| 456 delete subtree_task; | 492 delete subtree_task; |
| 457 } | 493 } |
| 458 } | 494 } |
| 459 | 495 |
| 460 return true; | 496 return true; |
| 461 } | 497 } |
| 462 | 498 |
| 463 } // namespace | 499 } // namespace |
| 464 | 500 |
| 465 DirectoryWatcher::DirectoryWatcher() { | 501 DirectoryWatcher::DirectoryWatcher() { |
| 466 impl_ = new DirectoryWatcherImpl(); | 502 impl_ = new DirectoryWatcherImpl(); |
| 467 } | 503 } |
| 468 | 504 |
| OLD | NEW |