Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(295)

Side by Side Diff: base/files/file_path_watcher_linux.cc

Issue 7583034: [Linux] Make FilePathWatcher somewhat more symlink aware. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: update comments Created 9 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « base/files/file_path_watcher_browsertest.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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. If the link target is "/",
456 // then we shouldn't get here in normal situations and if we do, we'd
457 // watch "/" for changes to a component "/" which is harmless so no
458 // special treatment of this case is required.
459 watch_entry->watch_ =
460 g_inotify_reader.Get().AddWatch(link.DirName(), this);
461 if (watch_entry->watch_ != InotifyReader::kInvalidWatch) {
462 watch_entry->linkname_ = link.BaseName().value();
463 } else {
464 DPLOG(WARNING) << "Watch failed for " << link.DirName().value();
465 // TODO(craig) Symlinks only work if the parent directory
466 // for the target exist. Ideally we should make sure we've
467 // watched all the components of the symlink path for
468 // changes. See crbug.com/91561 for details.
469 }
470 }
441 if (watch_entry->watch_ == InotifyReader::kInvalidWatch) { 471 if (watch_entry->watch_ == InotifyReader::kInvalidWatch) {
442 path_valid = false; 472 path_valid = false;
443 } 473 }
444 } else { 474 } else {
445 watch_entry->watch_ = InotifyReader::kInvalidWatch; 475 watch_entry->watch_ = InotifyReader::kInvalidWatch;
446 } 476 }
447 if (old_watch != InotifyReader::kInvalidWatch && 477 if (old_watch != InotifyReader::kInvalidWatch &&
448 old_watch != watch_entry->watch_) { 478 old_watch != watch_entry->watch_) {
449 g_inotify_reader.Get().RemoveWatch(old_watch, this); 479 g_inotify_reader.Get().RemoveWatch(old_watch, this);
450 } 480 }
451 path = path.Append(watch_entry->subdir_); 481 path = path.Append(watch_entry->subdir_);
452 } 482 }
453 483
454 return true; 484 return true;
455 } 485 }
456 486
457 } // namespace 487 } // namespace
458 488
459 FilePathWatcher::FilePathWatcher() { 489 FilePathWatcher::FilePathWatcher() {
460 impl_ = new FilePathWatcherImpl(); 490 impl_ = new FilePathWatcherImpl();
461 } 491 }
462 492
463 } // namespace files 493 } // namespace files
464 } // namespace base 494 } // namespace base
OLDNEW
« no previous file with comments | « base/files/file_path_watcher_browsertest.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698