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

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: try watching symlink's dir Created 9 years, 4 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
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_) {
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
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
OLDNEW
« base/files/file_path_watcher_browsertest.cc ('K') | « 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