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 |