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_) { |
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 (child == watch_entry->linkname_); | |
338 | |
339 // Check whether the change references |target_| or a direct child. | |
340 DCHECK(watch_entry->subdir_.empty() || | |
341 (watch_entry + 1) != watches_.end()); | |
342 bool target_changed = | |
343 (watch_entry->subdir_.empty() && (child == watch_entry->linkname_)) || | |
344 (watch_entry->subdir_.empty() && watch_entry->linkname_.empty()) || | |
345 (watch_entry->subdir_ == child && (watch_entry + 1)->subdir_.empty()); | |
346 | |
347 // Update watches if a directory component of the |target_| path | |
348 // (dis)appears. Note that we don't add the additional restriction | |
349 // of checking is_directory here as changes to symlinks on the | |
350 // target path will not have is_directory set but as a result we | |
351 // may sometimes call UpdateWatches unnecessarily. | |
352 if (change_on_target_path && !UpdateWatches()) { | |
353 delegate_->OnFilePathError(target_); | |
354 return; | |
355 } | |
356 | |
357 // Report the following events: | |
358 // - The target or a direct child of the target got changed (in case the | |
359 // watched path refers to a directory). | |
360 // - One of the parent directories got moved or deleted, since the target | |
361 // disappears in this case. | |
362 // - One of the parent directories appears. The event corresponding to | |
363 // the target appearing might have been missed in this case, so | |
364 // recheck. | |
365 if (target_changed || | |
366 (change_on_target_path && !created) || | |
367 (change_on_target_path && file_util::PathExists(target_))) { | |
368 delegate_->OnFilePathChanged(target_); | |
369 return; | |
370 } | |
371 } | |
333 } | 372 } |
334 | 373 |
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 } | 374 } |
367 | 375 |
368 bool FilePathWatcherImpl::Watch(const FilePath& path, | 376 bool FilePathWatcherImpl::Watch(const FilePath& path, |
369 FilePathWatcher::Delegate* delegate) { | 377 FilePathWatcher::Delegate* delegate) { |
370 DCHECK(target_.empty()); | 378 DCHECK(target_.empty()); |
371 DCHECK(MessageLoopForIO::current()); | 379 DCHECK(MessageLoopForIO::current()); |
372 | 380 |
373 set_message_loop(base::MessageLoopProxy::current()); | 381 set_message_loop(base::MessageLoopProxy::current()); |
374 delegate_ = delegate; | 382 delegate_ = delegate; |
375 target_ = path; | 383 target_ = path; |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
431 DCHECK(message_loop()->BelongsToCurrentThread()); | 439 DCHECK(message_loop()->BelongsToCurrentThread()); |
432 | 440 |
433 // Walk the list of watches and update them as we go. | 441 // Walk the list of watches and update them as we go. |
434 FilePath path(FILE_PATH_LITERAL("/")); | 442 FilePath path(FILE_PATH_LITERAL("/")); |
435 bool path_valid = true; | 443 bool path_valid = true; |
436 for (WatchVector::iterator watch_entry(watches_.begin()); | 444 for (WatchVector::iterator watch_entry(watches_.begin()); |
437 watch_entry != watches_.end(); ++watch_entry) { | 445 watch_entry != watches_.end(); ++watch_entry) { |
438 InotifyReader::Watch old_watch = watch_entry->watch_; | 446 InotifyReader::Watch old_watch = watch_entry->watch_; |
439 if (path_valid) { | 447 if (path_valid) { |
440 watch_entry->watch_ = g_inotify_reader.Get().AddWatch(path, this); | 448 watch_entry->watch_ = g_inotify_reader.Get().AddWatch(path, this); |
449 if ((watch_entry->watch_ == InotifyReader::kInvalidWatch) && | |
450 file_util::IsLink(path)) { | |
451 FilePath link; | |
452 file_util::ReadSymbolicLink(path, &link); | |
453 if (!link.IsAbsolute()) | |
454 link = path.DirName().Append(link); | |
455 // Try watching symlink target directory. | |
456 watch_entry->watch_ = | |
457 g_inotify_reader.Get().AddWatch(link.DirName(), this); | |
Mattias Nissler (ping if slow)
2011/09/02 11:34:32
What if link is "/", i.e. a symbolic link pointing
Craig
2011/09/03 07:46:05
It's unlikely we'd get here under normal condition
| |
458 if (watch_entry->watch_ != InotifyReader::kInvalidWatch) { | |
459 watch_entry->linkname_ = link.BaseName().value(); | |
460 } else { | |
461 DPLOG(WARNING) << "Watch failed for " << link.DirName().value(); | |
462 // TODO(craig) Symlinks only work if the parent directory | |
463 // for the target exist. Ideally we should make sure we've | |
464 // watched all the components of the symlink path for | |
465 // changes. See crbug.com/91561 for details. | |
466 } | |
467 } | |
441 if (watch_entry->watch_ == InotifyReader::kInvalidWatch) { | 468 if (watch_entry->watch_ == InotifyReader::kInvalidWatch) { |
442 path_valid = false; | 469 path_valid = false; |
443 } | 470 } |
444 } else { | 471 } else { |
445 watch_entry->watch_ = InotifyReader::kInvalidWatch; | 472 watch_entry->watch_ = InotifyReader::kInvalidWatch; |
446 } | 473 } |
447 if (old_watch != InotifyReader::kInvalidWatch && | 474 if (old_watch != InotifyReader::kInvalidWatch && |
448 old_watch != watch_entry->watch_) { | 475 old_watch != watch_entry->watch_) { |
449 g_inotify_reader.Get().RemoveWatch(old_watch, this); | 476 g_inotify_reader.Get().RemoveWatch(old_watch, this); |
450 } | 477 } |
451 path = path.Append(watch_entry->subdir_); | 478 path = path.Append(watch_entry->subdir_); |
452 } | 479 } |
453 | 480 |
454 return true; | 481 return true; |
455 } | 482 } |
456 | 483 |
457 } // namespace | 484 } // namespace |
458 | 485 |
459 FilePathWatcher::FilePathWatcher() { | 486 FilePathWatcher::FilePathWatcher() { |
460 impl_ = new FilePathWatcherImpl(); | 487 impl_ = new FilePathWatcherImpl(); |
461 } | 488 } |
462 | 489 |
463 } // namespace files | 490 } // namespace files |
464 } // namespace base | 491 } // namespace base |
OLD | NEW |