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 |