Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 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 <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 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 112 virtual void WillDestroyCurrentMessageLoop() OVERRIDE; | 112 virtual void WillDestroyCurrentMessageLoop() OVERRIDE; |
| 113 | 113 |
| 114 private: | 114 private: |
| 115 virtual ~FilePathWatcherImpl() {} | 115 virtual ~FilePathWatcherImpl() {} |
| 116 | 116 |
| 117 // Cleans up and stops observing the |message_loop_| thread. | 117 // Cleans up and stops observing the |message_loop_| thread. |
| 118 void CancelOnMessageLoopThread() OVERRIDE; | 118 void CancelOnMessageLoopThread() OVERRIDE; |
| 119 | 119 |
| 120 // Inotify watches are installed for all directory components of |target_|. A | 120 // Inotify watches are installed for all directory components of |target_|. A |
| 121 // WatchEntry instance holds the watch descriptor for a component and the | 121 // WatchEntry instance holds the watch descriptor for a component and the |
| 122 // subdirectory for that identifies the next component. | 122 // subdirectory for that identifies the next component. If a symbolic link |
| 123 // is being watched, the target of the link is also kept. | |
| 123 struct WatchEntry { | 124 struct WatchEntry { |
| 124 WatchEntry(InotifyReader::Watch watch, const FilePath::StringType& subdir) | 125 WatchEntry(InotifyReader::Watch watch, const FilePath::StringType& subdir) |
| 125 : watch_(watch), | 126 : watch_(watch), |
| 126 subdir_(subdir) {} | 127 subdir_(subdir) {} |
| 127 | 128 |
| 128 InotifyReader::Watch watch_; | 129 InotifyReader::Watch watch_; |
| 129 FilePath::StringType subdir_; | 130 FilePath::StringType subdir_; |
| 131 FilePath::StringType linkname_; | |
| 130 }; | 132 }; |
| 131 typedef std::vector<WatchEntry> WatchVector; | 133 typedef std::vector<WatchEntry> WatchVector; |
| 132 | 134 |
| 133 // Reconfigure to watch for the most specific parent directory of |target_| | 135 // Reconfigure to watch for the most specific parent directory of |target_| |
| 134 // that exists. Updates |watched_path_|. Returns true on success. | 136 // that exists. Updates |watched_path_|. Returns true on success. |
| 135 bool UpdateWatches() WARN_UNUSED_RESULT; | 137 bool UpdateWatches() WARN_UNUSED_RESULT; |
| 136 | 138 |
| 137 // Delegate to notify upon changes. | 139 // Delegate to notify upon changes. |
| 138 scoped_refptr<FilePathWatcher::Delegate> delegate_; | 140 scoped_refptr<FilePathWatcher::Delegate> delegate_; |
| 139 | 141 |
| (...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 321 created, | 323 created, |
| 322 is_directory)); | 324 is_directory)); |
| 323 return; | 325 return; |
| 324 } | 326 } |
| 325 | 327 |
| 326 DCHECK(MessageLoopForIO::current()); | 328 DCHECK(MessageLoopForIO::current()); |
| 327 | 329 |
| 328 // Find the entry in |watches_| that corresponds to |fired_watch|. | 330 // Find the entry in |watches_| that corresponds to |fired_watch|. |
| 329 WatchVector::const_iterator watch_entry(watches_.begin()); | 331 WatchVector::const_iterator watch_entry(watches_.begin()); |
| 330 for ( ; watch_entry != watches_.end(); ++watch_entry) { | 332 for ( ; watch_entry != watches_.end(); ++watch_entry) { |
| 331 if (fired_watch == watch_entry->watch_) | 333 if (fired_watch == watch_entry->watch_) { |
|
Mattias Nissler (ping if slow)
2011/08/30 09:53:06
What is the reason you don't break this loop? I ca
Craig
2011/08/30 16:00:16
This covers the situation where the symlink is tar
Mattias Nissler (ping if slow)
2011/08/30 16:38:26
I see. Fair enough.
| |
| 332 break; | 334 // Check whether a path component of |target_| changed. |
| 335 bool change_on_target_path = child.empty() || | |
| 336 ((child == watch_entry->subdir_) && watch_entry->linkname_.empty()); | |
| 337 | |
| 338 // Check whether the change references |target_| or a direct child. | |
| 339 DCHECK(watch_entry->subdir_.empty() || | |
| 340 (watch_entry + 1) != watches_.end()); | |
| 341 bool target_changed = | |
| 342 (watch_entry->subdir_.empty() && (child == watch_entry->linkname_)) || | |
|
Mattias Nissler (ping if slow)
2011/08/30 09:53:06
What if the link occurs somewhere in the middle of
Craig
2011/08/30 16:00:16
The watch for a symlink's target directory is only
Mattias Nissler (ping if slow)
2011/08/30 16:38:26
I don't understand where in the code we reject sym
Craig
2011/09/01 19:04:08
Nothing stops the client from calling us with a sy
| |
| 343 (watch_entry->subdir_.empty() && watch_entry->linkname_.empty()) || | |
| 344 (watch_entry->subdir_ == child && (watch_entry + 1)->subdir_.empty()); | |
| 345 | |
| 346 // Update watches if a directory component of the |target_| path | |
| 347 // (dis)appears. | |
| 348 if (is_directory && change_on_target_path && !UpdateWatches()) { | |
| 349 delegate_->OnFilePathError(target_); | |
| 350 return; | |
| 351 } | |
| 352 | |
| 353 // Report the following events: | |
| 354 // - The target or a direct child of the target got changed (in case the | |
| 355 // watched path refers to a directory). | |
| 356 // - One of the parent directories got moved or deleted, since the target | |
| 357 // disappears in this case. | |
| 358 // - One of the parent directories appears. The event corresponding to | |
| 359 // the target appearing might have been missed in this case, so | |
| 360 // recheck. | |
| 361 if (target_changed || | |
| 362 (change_on_target_path && !created) || | |
| 363 (change_on_target_path && file_util::PathExists(target_))) { | |
| 364 delegate_->OnFilePathChanged(target_); | |
| 365 return; | |
| 366 } | |
| 367 } | |
| 333 } | 368 } |
| 334 | 369 |
| 335 // If this notification is from a previous generation of watches or the watch | |
| 336 // has been cancelled (|watches_| is empty then), bail out. | |
| 337 if (watch_entry == watches_.end()) | |
| 338 return; | |
| 339 | |
| 340 // Check whether a path component of |target_| changed. | |
| 341 bool change_on_target_path = child.empty() || child == watch_entry->subdir_; | |
| 342 | |
| 343 // Check whether the change references |target_| or a direct child. | |
| 344 DCHECK(watch_entry->subdir_.empty() || (watch_entry + 1) != watches_.end()); | |
| 345 bool target_changed = watch_entry->subdir_.empty() || | |
| 346 (watch_entry->subdir_ == child && (++watch_entry)->subdir_.empty()); | |
| 347 | |
| 348 // Update watches if a directory component of the |target_| path (dis)appears. | |
| 349 if (is_directory && change_on_target_path && !UpdateWatches()) { | |
| 350 delegate_->OnFilePathError(target_); | |
| 351 return; | |
| 352 } | |
| 353 | |
| 354 // Report the following events: | |
| 355 // - The target or a direct child of the target got changed (in case the | |
| 356 // watched path refers to a directory). | |
| 357 // - One of the parent directories got moved or deleted, since the target | |
| 358 // disappears in this case. | |
| 359 // - One of the parent directories appears. The event corresponding to the | |
| 360 // target appearing might have been missed in this case, so recheck. | |
| 361 if (target_changed || | |
| 362 (change_on_target_path && !created) || | |
| 363 (change_on_target_path && file_util::PathExists(target_))) { | |
| 364 delegate_->OnFilePathChanged(target_); | |
| 365 } | |
| 366 } | 370 } |
| 367 | 371 |
| 368 bool FilePathWatcherImpl::Watch(const FilePath& path, | 372 bool FilePathWatcherImpl::Watch(const FilePath& path, |
| 369 FilePathWatcher::Delegate* delegate) { | 373 FilePathWatcher::Delegate* delegate) { |
| 370 DCHECK(target_.empty()); | 374 DCHECK(target_.empty()); |
| 371 DCHECK(MessageLoopForIO::current()); | 375 DCHECK(MessageLoopForIO::current()); |
| 372 | 376 |
| 373 set_message_loop(base::MessageLoopProxy::CreateForCurrentThread()); | 377 set_message_loop(base::MessageLoopProxy::CreateForCurrentThread()); |
| 374 delegate_ = delegate; | 378 delegate_ = delegate; |
| 375 target_ = path; | 379 target_ = path; |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 429 DCHECK(message_loop()->BelongsToCurrentThread()); | 433 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 430 | 434 |
| 431 // Walk the list of watches and update them as we go. | 435 // Walk the list of watches and update them as we go. |
| 432 FilePath path(FILE_PATH_LITERAL("/")); | 436 FilePath path(FILE_PATH_LITERAL("/")); |
| 433 bool path_valid = true; | 437 bool path_valid = true; |
| 434 for (WatchVector::iterator watch_entry(watches_.begin()); | 438 for (WatchVector::iterator watch_entry(watches_.begin()); |
| 435 watch_entry != watches_.end(); ++watch_entry) { | 439 watch_entry != watches_.end(); ++watch_entry) { |
| 436 InotifyReader::Watch old_watch = watch_entry->watch_; | 440 InotifyReader::Watch old_watch = watch_entry->watch_; |
| 437 if (path_valid) { | 441 if (path_valid) { |
| 438 watch_entry->watch_ = g_inotify_reader.Get().AddWatch(path, this); | 442 watch_entry->watch_ = g_inotify_reader.Get().AddWatch(path, this); |
| 443 if ((watch_entry->watch_ == InotifyReader::kInvalidWatch) && | |
| 444 file_util::IsLink(path)) { | |
|
Mattias Nissler (ping if slow)
2011/08/30 09:53:06
We're not passing IN_DONT_FOLLOW, so this only kic
Craig
2011/08/30 16:00:16
AddWatch will fail when called with a filename or
Mattias Nissler (ping if slow)
2011/08/30 16:38:26
How can we be sure there is no symlink on the path
Craig
2011/09/01 19:04:08
Yep, I missed that :(
| |
| 445 FilePath link; | |
| 446 file_util::ReadSymbolicLink(path, &link); | |
| 447 if (!link.IsAbsolute()) | |
| 448 link = path.DirName().Append(link); | |
| 449 // Try watching symlink target directory. | |
| 450 watch_entry->watch_ = | |
| 451 g_inotify_reader.Get().AddWatch(link.DirName(), this); | |
|
Mattias Nissler (ping if slow)
2011/08/30 09:53:06
So we fail here if the immediate parent directory
Craig
2011/08/30 16:00:16
Yes. In the final solution I'd like to add watches
Mattias Nissler (ping if slow)
2011/08/30 16:38:26
Sounds good.
| |
| 452 if (watch_entry->watch_ != InotifyReader::kInvalidWatch) | |
| 453 watch_entry->linkname_ = link.BaseName().value(); | |
| 454 } | |
| 439 if (watch_entry->watch_ == InotifyReader::kInvalidWatch) { | 455 if (watch_entry->watch_ == InotifyReader::kInvalidWatch) { |
| 440 path_valid = false; | 456 path_valid = false; |
| 441 } | 457 } |
| 442 } else { | 458 } else { |
| 443 watch_entry->watch_ = InotifyReader::kInvalidWatch; | 459 watch_entry->watch_ = InotifyReader::kInvalidWatch; |
| 444 } | 460 } |
| 445 if (old_watch != InotifyReader::kInvalidWatch && | 461 if (old_watch != InotifyReader::kInvalidWatch && |
| 446 old_watch != watch_entry->watch_) { | 462 old_watch != watch_entry->watch_) { |
| 447 g_inotify_reader.Get().RemoveWatch(old_watch, this); | 463 g_inotify_reader.Get().RemoveWatch(old_watch, this); |
| 448 } | 464 } |
| 449 path = path.Append(watch_entry->subdir_); | 465 path = path.Append(watch_entry->subdir_); |
| 450 } | 466 } |
| 451 | 467 |
| 452 return true; | 468 return true; |
| 453 } | 469 } |
| 454 | 470 |
| 455 } // namespace | 471 } // namespace |
| 456 | 472 |
| 457 FilePathWatcher::FilePathWatcher() { | 473 FilePathWatcher::FilePathWatcher() { |
| 458 impl_ = new FilePathWatcherImpl(); | 474 impl_ = new FilePathWatcherImpl(); |
| 459 } | 475 } |
| 460 | 476 |
| 461 } // namespace files | 477 } // namespace files |
| 462 } // namespace base | 478 } // namespace base |
| OLD | NEW |