OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 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/files/file_path_watcher.h" | 5 #include "base/files/file_path_watcher.h" |
6 | 6 |
7 #include <errno.h> | 7 #include <errno.h> |
8 #include <stddef.h> | 8 #include <stddef.h> |
9 #include <string.h> | 9 #include <string.h> |
10 #include <sys/inotify.h> | 10 #include <sys/inotify.h> |
11 #include <sys/ioctl.h> | 11 #include <sys/ioctl.h> |
12 #include <sys/select.h> | 12 #include <sys/select.h> |
13 #include <unistd.h> | 13 #include <unistd.h> |
14 | 14 |
15 #include <algorithm> | 15 #include <algorithm> |
16 #include <map> | 16 #include <map> |
17 #include <memory> | 17 #include <memory> |
18 #include <set> | 18 #include <set> |
19 #include <utility> | 19 #include <utility> |
20 #include <vector> | 20 #include <vector> |
21 | 21 |
22 #include "base/bind.h" | 22 #include "base/bind.h" |
23 #include "base/containers/hash_tables.h" | 23 #include "base/containers/hash_tables.h" |
24 #include "base/files/file_enumerator.h" | 24 #include "base/files/file_enumerator.h" |
25 #include "base/files/file_path.h" | 25 #include "base/files/file_path.h" |
26 #include "base/files/file_util.h" | 26 #include "base/files/file_util.h" |
27 #include "base/lazy_instance.h" | |
28 #include "base/location.h" | 27 #include "base/location.h" |
29 #include "base/logging.h" | 28 #include "base/logging.h" |
30 #include "base/macros.h" | 29 #include "base/macros.h" |
31 #include "base/memory/ptr_util.h" | 30 #include "base/memory/ptr_util.h" |
32 #include "base/memory/weak_ptr.h" | 31 #include "base/memory/weak_ptr.h" |
33 #include "base/posix/eintr_wrapper.h" | 32 #include "base/posix/eintr_wrapper.h" |
34 #include "base/single_thread_task_runner.h" | 33 #include "base/single_thread_task_runner.h" |
35 #include "base/stl_util.h" | 34 #include "base/stl_util.h" |
36 #include "base/synchronization/lock.h" | 35 #include "base/synchronization/lock.h" |
37 #include "base/threading/sequenced_task_runner_handle.h" | 36 #include "base/threading/sequenced_task_runner_handle.h" |
(...skipping 18 matching lines...) Expand all Loading... |
56 // change. Returns kInvalidWatch on failure. | 55 // change. Returns kInvalidWatch on failure. |
57 Watch AddWatch(const FilePath& path, FilePathWatcherImpl* watcher); | 56 Watch AddWatch(const FilePath& path, FilePathWatcherImpl* watcher); |
58 | 57 |
59 // Remove |watch| if it's valid. | 58 // Remove |watch| if it's valid. |
60 void RemoveWatch(Watch watch, FilePathWatcherImpl* watcher); | 59 void RemoveWatch(Watch watch, FilePathWatcherImpl* watcher); |
61 | 60 |
62 // Callback for InotifyReaderTask. | 61 // Callback for InotifyReaderTask. |
63 void OnInotifyEvent(const inotify_event* event); | 62 void OnInotifyEvent(const inotify_event* event); |
64 | 63 |
65 private: | 64 private: |
66 friend struct DefaultLazyInstanceTraits<InotifyReader>; | 65 friend InotifyReader* GetInotifyReader(); |
67 | |
68 typedef std::set<FilePathWatcherImpl*> WatcherSet; | 66 typedef std::set<FilePathWatcherImpl*> WatcherSet; |
69 | 67 |
70 InotifyReader(); | 68 InotifyReader(); |
71 // There is no destructor because |g_inotify_reader| is a | 69 // There is no destructor because GetInotifyReader() is leaky. Having a |
72 // base::LazyInstace::Leaky object. Having a destructor causes build | 70 // destructor causes build issues with GCC 6 (http://crbug.com/636346). |
73 // issues with GCC 6 (http://crbug.com/636346). | |
74 | 71 |
75 // We keep track of which delegates want to be notified on which watches. | 72 // We keep track of which delegates want to be notified on which watches. |
76 hash_map<Watch, WatcherSet> watchers_; | 73 hash_map<Watch, WatcherSet> watchers_; |
77 | 74 |
78 // Lock to protect watchers_. | 75 // Lock to protect watchers_. |
79 Lock lock_; | 76 Lock lock_; |
80 | 77 |
81 // Separate thread on which we run blocking read for inotify events. | 78 // Separate thread on which we run blocking read for inotify events. |
82 Thread thread_; | 79 Thread thread_; |
83 | 80 |
(...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
236 while (i < bytes_read) { | 233 while (i < bytes_read) { |
237 inotify_event* event = reinterpret_cast<inotify_event*>(&buffer[i]); | 234 inotify_event* event = reinterpret_cast<inotify_event*>(&buffer[i]); |
238 size_t event_size = sizeof(inotify_event) + event->len; | 235 size_t event_size = sizeof(inotify_event) + event->len; |
239 DCHECK(i + event_size <= static_cast<size_t>(bytes_read)); | 236 DCHECK(i + event_size <= static_cast<size_t>(bytes_read)); |
240 reader->OnInotifyEvent(event); | 237 reader->OnInotifyEvent(event); |
241 i += event_size; | 238 i += event_size; |
242 } | 239 } |
243 } | 240 } |
244 } | 241 } |
245 | 242 |
246 static LazyInstance<InotifyReader>::Leaky g_inotify_reader = | 243 InotifyReader* GetInotifyReader() { |
247 LAZY_INSTANCE_INITIALIZER; | 244 static auto inotify_reader = new InotifyReader(); |
| 245 return inotify_reader; |
| 246 } |
248 | 247 |
249 InotifyReader::InotifyReader() | 248 InotifyReader::InotifyReader() |
250 : thread_("inotify_reader"), | 249 : thread_("inotify_reader"), |
251 inotify_fd_(inotify_init()), | 250 inotify_fd_(inotify_init()), |
252 valid_(false) { | 251 valid_(false) { |
253 if (inotify_fd_ < 0) | 252 if (inotify_fd_ < 0) |
254 PLOG(ERROR) << "inotify_init() failed"; | 253 PLOG(ERROR) << "inotify_init() failed"; |
255 | 254 |
256 if (inotify_fd_ >= 0 && thread_.Start()) { | 255 if (inotify_fd_ >= 0 && thread_.Start()) { |
257 thread_.task_runner()->PostTask( | 256 thread_.task_runner()->PostTask( |
(...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
451 return; | 450 return; |
452 } | 451 } |
453 | 452 |
454 DCHECK(task_runner()->RunsTasksOnCurrentThread()); | 453 DCHECK(task_runner()->RunsTasksOnCurrentThread()); |
455 DCHECK(!is_cancelled()); | 454 DCHECK(!is_cancelled()); |
456 | 455 |
457 set_cancelled(); | 456 set_cancelled(); |
458 callback_.Reset(); | 457 callback_.Reset(); |
459 | 458 |
460 for (size_t i = 0; i < watches_.size(); ++i) | 459 for (size_t i = 0; i < watches_.size(); ++i) |
461 g_inotify_reader.Get().RemoveWatch(watches_[i].watch, this); | 460 GetInotifyReader()->RemoveWatch(watches_[i].watch, this); |
462 watches_.clear(); | 461 watches_.clear(); |
463 target_.clear(); | 462 target_.clear(); |
464 RemoveRecursiveWatches(); | 463 RemoveRecursiveWatches(); |
465 } | 464 } |
466 | 465 |
467 void FilePathWatcherImpl::UpdateWatches() { | 466 void FilePathWatcherImpl::UpdateWatches() { |
468 // Ensure this runs on the task_runner() exclusively in order to avoid | 467 // Ensure this runs on the task_runner() exclusively in order to avoid |
469 // concurrency issues. | 468 // concurrency issues. |
470 DCHECK(task_runner()->RunsTasksOnCurrentThread()); | 469 DCHECK(task_runner()->RunsTasksOnCurrentThread()); |
471 DCHECK(HasValidWatchVector()); | 470 DCHECK(HasValidWatchVector()); |
472 | 471 |
473 // Walk the list of watches and update them as we go. | 472 // Walk the list of watches and update them as we go. |
474 FilePath path(FILE_PATH_LITERAL("/")); | 473 FilePath path(FILE_PATH_LITERAL("/")); |
475 for (size_t i = 0; i < watches_.size(); ++i) { | 474 for (size_t i = 0; i < watches_.size(); ++i) { |
476 WatchEntry& watch_entry = watches_[i]; | 475 WatchEntry& watch_entry = watches_[i]; |
477 InotifyReader::Watch old_watch = watch_entry.watch; | 476 InotifyReader::Watch old_watch = watch_entry.watch; |
478 watch_entry.watch = InotifyReader::kInvalidWatch; | 477 watch_entry.watch = InotifyReader::kInvalidWatch; |
479 watch_entry.linkname.clear(); | 478 watch_entry.linkname.clear(); |
480 watch_entry.watch = g_inotify_reader.Get().AddWatch(path, this); | 479 watch_entry.watch = GetInotifyReader()->AddWatch(path, this); |
481 if (watch_entry.watch == InotifyReader::kInvalidWatch) { | 480 if (watch_entry.watch == InotifyReader::kInvalidWatch) { |
482 // Ignore the error code (beyond symlink handling) to attempt to add | 481 // Ignore the error code (beyond symlink handling) to attempt to add |
483 // watches on accessible children of unreadable directories. Note that | 482 // watches on accessible children of unreadable directories. Note that |
484 // this is a best-effort attempt; we may not catch events in this | 483 // this is a best-effort attempt; we may not catch events in this |
485 // scenario. | 484 // scenario. |
486 if (IsLink(path)) | 485 if (IsLink(path)) |
487 AddWatchForBrokenSymlink(path, &watch_entry); | 486 AddWatchForBrokenSymlink(path, &watch_entry); |
488 } | 487 } |
489 if (old_watch != watch_entry.watch) | 488 if (old_watch != watch_entry.watch) |
490 g_inotify_reader.Get().RemoveWatch(old_watch, this); | 489 GetInotifyReader()->RemoveWatch(old_watch, this); |
491 path = path.Append(watch_entry.subdir); | 490 path = path.Append(watch_entry.subdir); |
492 } | 491 } |
493 | 492 |
494 UpdateRecursiveWatches(InotifyReader::kInvalidWatch, | 493 UpdateRecursiveWatches(InotifyReader::kInvalidWatch, |
495 false /* is directory? */); | 494 false /* is directory? */); |
496 } | 495 } |
497 | 496 |
498 void FilePathWatcherImpl::UpdateRecursiveWatches( | 497 void FilePathWatcherImpl::UpdateRecursiveWatches( |
499 InotifyReader::Watch fired_watch, | 498 InotifyReader::Watch fired_watch, |
500 bool is_dir) { | 499 bool is_dir) { |
(...skipping 25 matching lines...) Expand all Loading... |
526 target_; | 525 target_; |
527 | 526 |
528 std::map<FilePath, InotifyReader::Watch>::iterator start_it = | 527 std::map<FilePath, InotifyReader::Watch>::iterator start_it = |
529 recursive_watches_by_path_.lower_bound(changed_dir); | 528 recursive_watches_by_path_.lower_bound(changed_dir); |
530 std::map<FilePath, InotifyReader::Watch>::iterator end_it = start_it; | 529 std::map<FilePath, InotifyReader::Watch>::iterator end_it = start_it; |
531 for (; end_it != recursive_watches_by_path_.end(); ++end_it) { | 530 for (; end_it != recursive_watches_by_path_.end(); ++end_it) { |
532 const FilePath& cur_path = end_it->first; | 531 const FilePath& cur_path = end_it->first; |
533 if (!changed_dir.IsParent(cur_path)) | 532 if (!changed_dir.IsParent(cur_path)) |
534 break; | 533 break; |
535 if (!DirectoryExists(cur_path)) | 534 if (!DirectoryExists(cur_path)) |
536 g_inotify_reader.Get().RemoveWatch(end_it->second, this); | 535 GetInotifyReader()->RemoveWatch(end_it->second, this); |
537 } | 536 } |
538 recursive_watches_by_path_.erase(start_it, end_it); | 537 recursive_watches_by_path_.erase(start_it, end_it); |
539 UpdateRecursiveWatchesForPath(changed_dir); | 538 UpdateRecursiveWatchesForPath(changed_dir); |
540 } | 539 } |
541 | 540 |
542 void FilePathWatcherImpl::UpdateRecursiveWatchesForPath(const FilePath& path) { | 541 void FilePathWatcherImpl::UpdateRecursiveWatchesForPath(const FilePath& path) { |
543 DCHECK(recursive_); | 542 DCHECK(recursive_); |
544 DCHECK(!path.empty()); | 543 DCHECK(!path.empty()); |
545 DCHECK(DirectoryExists(path)); | 544 DCHECK(DirectoryExists(path)); |
546 | 545 |
547 // Note: SHOW_SYM_LINKS exposes symlinks as symlinks, so they are ignored | 546 // Note: SHOW_SYM_LINKS exposes symlinks as symlinks, so they are ignored |
548 // rather than followed. Following symlinks can easily lead to the undesirable | 547 // rather than followed. Following symlinks can easily lead to the undesirable |
549 // situation where the entire file system is being watched. | 548 // situation where the entire file system is being watched. |
550 FileEnumerator enumerator( | 549 FileEnumerator enumerator( |
551 path, | 550 path, |
552 true /* recursive enumeration */, | 551 true /* recursive enumeration */, |
553 FileEnumerator::DIRECTORIES | FileEnumerator::SHOW_SYM_LINKS); | 552 FileEnumerator::DIRECTORIES | FileEnumerator::SHOW_SYM_LINKS); |
554 for (FilePath current = enumerator.Next(); | 553 for (FilePath current = enumerator.Next(); |
555 !current.empty(); | 554 !current.empty(); |
556 current = enumerator.Next()) { | 555 current = enumerator.Next()) { |
557 DCHECK(enumerator.GetInfo().IsDirectory()); | 556 DCHECK(enumerator.GetInfo().IsDirectory()); |
558 | 557 |
559 if (!ContainsKey(recursive_watches_by_path_, current)) { | 558 if (!ContainsKey(recursive_watches_by_path_, current)) { |
560 // Add new watches. | 559 // Add new watches. |
561 InotifyReader::Watch watch = | 560 InotifyReader::Watch watch = GetInotifyReader()->AddWatch(current, this); |
562 g_inotify_reader.Get().AddWatch(current, this); | |
563 TrackWatchForRecursion(watch, current); | 561 TrackWatchForRecursion(watch, current); |
564 } else { | 562 } else { |
565 // Update existing watches. | 563 // Update existing watches. |
566 InotifyReader::Watch old_watch = recursive_watches_by_path_[current]; | 564 InotifyReader::Watch old_watch = recursive_watches_by_path_[current]; |
567 DCHECK_NE(InotifyReader::kInvalidWatch, old_watch); | 565 DCHECK_NE(InotifyReader::kInvalidWatch, old_watch); |
568 InotifyReader::Watch watch = | 566 InotifyReader::Watch watch = GetInotifyReader()->AddWatch(current, this); |
569 g_inotify_reader.Get().AddWatch(current, this); | |
570 if (watch != old_watch) { | 567 if (watch != old_watch) { |
571 g_inotify_reader.Get().RemoveWatch(old_watch, this); | 568 GetInotifyReader()->RemoveWatch(old_watch, this); |
572 recursive_paths_by_watch_.erase(old_watch); | 569 recursive_paths_by_watch_.erase(old_watch); |
573 recursive_watches_by_path_.erase(current); | 570 recursive_watches_by_path_.erase(current); |
574 TrackWatchForRecursion(watch, current); | 571 TrackWatchForRecursion(watch, current); |
575 } | 572 } |
576 } | 573 } |
577 } | 574 } |
578 } | 575 } |
579 | 576 |
580 void FilePathWatcherImpl::TrackWatchForRecursion(InotifyReader::Watch watch, | 577 void FilePathWatcherImpl::TrackWatchForRecursion(InotifyReader::Watch watch, |
581 const FilePath& path) { | 578 const FilePath& path) { |
(...skipping 11 matching lines...) Expand all Loading... |
593 } | 590 } |
594 | 591 |
595 void FilePathWatcherImpl::RemoveRecursiveWatches() { | 592 void FilePathWatcherImpl::RemoveRecursiveWatches() { |
596 if (!recursive_) | 593 if (!recursive_) |
597 return; | 594 return; |
598 | 595 |
599 for (hash_map<InotifyReader::Watch, FilePath>::const_iterator it = | 596 for (hash_map<InotifyReader::Watch, FilePath>::const_iterator it = |
600 recursive_paths_by_watch_.begin(); | 597 recursive_paths_by_watch_.begin(); |
601 it != recursive_paths_by_watch_.end(); | 598 it != recursive_paths_by_watch_.end(); |
602 ++it) { | 599 ++it) { |
603 g_inotify_reader.Get().RemoveWatch(it->first, this); | 600 GetInotifyReader()->RemoveWatch(it->first, this); |
604 } | 601 } |
605 recursive_paths_by_watch_.clear(); | 602 recursive_paths_by_watch_.clear(); |
606 recursive_watches_by_path_.clear(); | 603 recursive_watches_by_path_.clear(); |
607 } | 604 } |
608 | 605 |
609 void FilePathWatcherImpl::AddWatchForBrokenSymlink(const FilePath& path, | 606 void FilePathWatcherImpl::AddWatchForBrokenSymlink(const FilePath& path, |
610 WatchEntry* watch_entry) { | 607 WatchEntry* watch_entry) { |
611 DCHECK_EQ(InotifyReader::kInvalidWatch, watch_entry->watch); | 608 DCHECK_EQ(InotifyReader::kInvalidWatch, watch_entry->watch); |
612 FilePath link; | 609 FilePath link; |
613 if (!ReadSymbolicLink(path, &link)) | 610 if (!ReadSymbolicLink(path, &link)) |
614 return; | 611 return; |
615 | 612 |
616 if (!link.IsAbsolute()) | 613 if (!link.IsAbsolute()) |
617 link = path.DirName().Append(link); | 614 link = path.DirName().Append(link); |
618 | 615 |
619 // Try watching symlink target directory. If the link target is "/", then we | 616 // Try watching symlink target directory. If the link target is "/", then we |
620 // shouldn't get here in normal situations and if we do, we'd watch "/" for | 617 // shouldn't get here in normal situations and if we do, we'd watch "/" for |
621 // changes to a component "/" which is harmless so no special treatment of | 618 // changes to a component "/" which is harmless so no special treatment of |
622 // this case is required. | 619 // this case is required. |
623 InotifyReader::Watch watch = | 620 InotifyReader::Watch watch = |
624 g_inotify_reader.Get().AddWatch(link.DirName(), this); | 621 GetInotifyReader()->AddWatch(link.DirName(), this); |
625 if (watch == InotifyReader::kInvalidWatch) { | 622 if (watch == InotifyReader::kInvalidWatch) { |
626 // TODO(craig) Symlinks only work if the parent directory for the target | 623 // TODO(craig) Symlinks only work if the parent directory for the target |
627 // exist. Ideally we should make sure we've watched all the components of | 624 // exist. Ideally we should make sure we've watched all the components of |
628 // the symlink path for changes. See crbug.com/91561 for details. | 625 // the symlink path for changes. See crbug.com/91561 for details. |
629 DPLOG(WARNING) << "Watch failed for " << link.DirName().value(); | 626 DPLOG(WARNING) << "Watch failed for " << link.DirName().value(); |
630 return; | 627 return; |
631 } | 628 } |
632 watch_entry->watch = watch; | 629 watch_entry->watch = watch; |
633 watch_entry->linkname = link.BaseName().value(); | 630 watch_entry->linkname = link.BaseName().value(); |
634 } | 631 } |
635 | 632 |
636 bool FilePathWatcherImpl::HasValidWatchVector() const { | 633 bool FilePathWatcherImpl::HasValidWatchVector() const { |
637 if (watches_.empty()) | 634 if (watches_.empty()) |
638 return false; | 635 return false; |
639 for (size_t i = 0; i < watches_.size() - 1; ++i) { | 636 for (size_t i = 0; i < watches_.size() - 1; ++i) { |
640 if (watches_[i].subdir.empty()) | 637 if (watches_[i].subdir.empty()) |
641 return false; | 638 return false; |
642 } | 639 } |
643 return watches_.back().subdir.empty(); | 640 return watches_.back().subdir.empty(); |
644 } | 641 } |
645 | 642 |
646 } // namespace | 643 } // namespace |
647 | 644 |
648 FilePathWatcher::FilePathWatcher() { | 645 FilePathWatcher::FilePathWatcher() { |
649 sequence_checker_.DetachFromSequence(); | 646 sequence_checker_.DetachFromSequence(); |
650 impl_ = MakeUnique<FilePathWatcherImpl>(); | 647 impl_ = MakeUnique<FilePathWatcherImpl>(); |
651 } | 648 } |
652 | 649 |
653 } // namespace base | 650 } // namespace base |
OLD | NEW |