Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 42 // http://crbug.com/38174 | 42 // http://crbug.com/38174 |
| 43 class InotifyReader { | 43 class InotifyReader { |
| 44 public: | 44 public: |
| 45 typedef int Watch; // Watch descriptor used by AddWatch and RemoveWatch. | 45 typedef int Watch; // Watch descriptor used by AddWatch and RemoveWatch. |
| 46 static const Watch kInvalidWatch = -1; | 46 static const Watch kInvalidWatch = -1; |
| 47 | 47 |
| 48 // Watch directory |path| for changes. |watcher| will be notified on each | 48 // Watch directory |path| for changes. |watcher| will be notified on each |
| 49 // change. Returns kInvalidWatch on failure. | 49 // change. Returns kInvalidWatch on failure. |
| 50 Watch AddWatch(const FilePath& path, FilePathWatcherImpl* watcher); | 50 Watch AddWatch(const FilePath& path, FilePathWatcherImpl* watcher); |
| 51 | 51 |
| 52 // Remove |watch|. Returns true on success. | 52 // Remove |watch| if it's valid. |
| 53 bool RemoveWatch(Watch watch, FilePathWatcherImpl* watcher); | 53 void RemoveWatch(Watch watch, FilePathWatcherImpl* watcher); |
|
Lei Zhang
2014/04/25 06:45:23
Nobody checks the return result.
| |
| 54 | 54 |
| 55 // Callback for InotifyReaderTask. | 55 // Callback for InotifyReaderTask. |
| 56 void OnInotifyEvent(const inotify_event* event); | 56 void OnInotifyEvent(const inotify_event* event); |
| 57 | 57 |
| 58 private: | 58 private: |
| 59 friend struct DefaultLazyInstanceTraits<InotifyReader>; | 59 friend struct DefaultLazyInstanceTraits<InotifyReader>; |
| 60 | 60 |
| 61 typedef std::set<FilePathWatcherImpl*> WatcherSet; | 61 typedef std::set<FilePathWatcherImpl*> WatcherSet; |
| 62 | 62 |
| 63 InotifyReader(); | 63 InotifyReader(); |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 90 FilePathWatcherImpl(); | 90 FilePathWatcherImpl(); |
| 91 | 91 |
| 92 // Called for each event coming from the watch. |fired_watch| identifies the | 92 // Called for each event coming from the watch. |fired_watch| identifies the |
| 93 // watch that fired, |child| indicates what has changed, and is relative to | 93 // watch that fired, |child| indicates what has changed, and is relative to |
| 94 // the currently watched path for |fired_watch|. The flag |created| is true if | 94 // the currently watched path for |fired_watch|. The flag |created| is true if |
| 95 // the object appears. | 95 // the object appears. |
| 96 void OnFilePathChanged(InotifyReader::Watch fired_watch, | 96 void OnFilePathChanged(InotifyReader::Watch fired_watch, |
| 97 const FilePath::StringType& child, | 97 const FilePath::StringType& child, |
| 98 bool created); | 98 bool created); |
| 99 | 99 |
| 100 protected: | |
| 101 virtual ~FilePathWatcherImpl() {} | |
| 102 | |
| 103 private: | |
| 100 // Start watching |path| for changes and notify |delegate| on each change. | 104 // Start watching |path| for changes and notify |delegate| on each change. |
| 101 // Returns true if watch for |path| has been added successfully. | 105 // Returns true if watch for |path| has been added successfully. |
| 102 virtual bool Watch(const FilePath& path, | 106 virtual bool Watch(const FilePath& path, |
| 103 bool recursive, | 107 bool recursive, |
| 104 const FilePathWatcher::Callback& callback) OVERRIDE; | 108 const FilePathWatcher::Callback& callback) OVERRIDE; |
| 105 | 109 |
| 106 // Cancel the watch. This unregisters the instance with InotifyReader. | 110 // Cancel the watch. This unregisters the instance with InotifyReader. |
| 107 virtual void Cancel() OVERRIDE; | 111 virtual void Cancel() OVERRIDE; |
| 108 | 112 |
| 113 // Cleans up and stops observing the message_loop() thread. | |
| 114 virtual void CancelOnMessageLoopThread() OVERRIDE; | |
| 115 | |
| 109 // Deletion of the FilePathWatcher will call Cancel() to dispose of this | 116 // Deletion of the FilePathWatcher will call Cancel() to dispose of this |
| 110 // object in the right thread. This also observes destruction of the required | 117 // object in the right thread. This also observes destruction of the required |
| 111 // cleanup thread, in case it quits before Cancel() is called. | 118 // cleanup thread, in case it quits before Cancel() is called. |
| 112 virtual void WillDestroyCurrentMessageLoop() OVERRIDE; | 119 virtual void WillDestroyCurrentMessageLoop() OVERRIDE; |
| 113 | 120 |
| 114 protected: | |
| 115 virtual ~FilePathWatcherImpl() {} | |
| 116 | |
| 117 private: | |
| 118 // Cleans up and stops observing the |message_loop_| thread. | |
| 119 virtual void CancelOnMessageLoopThread() OVERRIDE; | |
| 120 | |
| 121 // Inotify watches are installed for all directory components of |target_|. A | 121 // Inotify watches are installed for all directory components of |target_|. A |
| 122 // WatchEntry instance holds the watch descriptor for a component and the | 122 // WatchEntry instance holds the watch descriptor for a component and the |
| 123 // subdirectory for that identifies the next component. If a symbolic link | 123 // subdirectory for that identifies the next component. If a symbolic link |
| 124 // is being watched, the target of the link is also kept. | 124 // is being watched, the target of the link is also kept. |
| 125 struct WatchEntry { | 125 struct WatchEntry { |
| 126 WatchEntry(InotifyReader::Watch watch, const FilePath::StringType& subdir) | 126 explicit WatchEntry(const FilePath::StringType& subdir) |
|
Lei Zhang
2014/04/25 06:45:23
Always called with |watch| = -1
| |
| 127 : watch_(watch), | 127 : watch_(InotifyReader::kInvalidWatch), |
| 128 subdir_(subdir) {} | 128 subdir_(subdir) {} |
| 129 | 129 |
| 130 InotifyReader::Watch watch_; | 130 InotifyReader::Watch watch_; |
| 131 FilePath::StringType subdir_; | 131 FilePath::StringType subdir_; |
| 132 FilePath::StringType linkname_; | 132 FilePath::StringType linkname_; |
| 133 }; | 133 }; |
| 134 typedef std::vector<WatchEntry> WatchVector; | 134 typedef std::vector<WatchEntry> WatchVector; |
| 135 | 135 |
| 136 // Reconfigure to watch for the most specific parent directory of |target_| | 136 // Reconfigure to watch for the most specific parent directory of |target_| |
| 137 // that exists. Updates |watched_path_|. Returns true on success. | 137 // that exists. Updates |watched_path_|. Returns true on success. |
| 138 bool UpdateWatches() WARN_UNUSED_RESULT; | 138 bool UpdateWatches() WARN_UNUSED_RESULT; |
| 139 | 139 |
| 140 // If |path| is a symlink to a non-existent target, attempt to add a watch to | |
| 141 // the link target's parent directory. Returns true and update watch_entry on | |
| 142 // success. | |
| 143 bool AddWatchForBrokenSymlink(const FilePath& path, WatchEntry* watch_entry); | |
| 144 | |
| 145 bool HasValidWatchVector() const; | |
| 146 | |
| 140 // Callback to notify upon changes. | 147 // Callback to notify upon changes. |
| 141 FilePathWatcher::Callback callback_; | 148 FilePathWatcher::Callback callback_; |
| 142 | 149 |
| 143 // The file or directory we're supposed to watch. | 150 // The file or directory we're supposed to watch. |
| 144 FilePath target_; | 151 FilePath target_; |
| 145 | 152 |
| 146 // The vector of watches and next component names for all path components, | 153 // The vector of watches and next component names for all path components, |
| 147 // starting at the root directory. The last entry corresponds to the watch for | 154 // starting at the root directory. The last entry corresponds to the watch for |
| 148 // |target_| and always stores an empty next component name in |subdir_|. | 155 // |target_| and always stores an empty next component name in |subdir_|. |
| 149 WatchVector watches_; | 156 WatchVector watches_; |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 217 : thread_("inotify_reader"), | 224 : thread_("inotify_reader"), |
| 218 inotify_fd_(inotify_init()), | 225 inotify_fd_(inotify_init()), |
| 219 valid_(false) { | 226 valid_(false) { |
| 220 if (inotify_fd_ < 0) | 227 if (inotify_fd_ < 0) |
| 221 PLOG(ERROR) << "inotify_init() failed"; | 228 PLOG(ERROR) << "inotify_init() failed"; |
| 222 | 229 |
| 223 shutdown_pipe_[0] = -1; | 230 shutdown_pipe_[0] = -1; |
| 224 shutdown_pipe_[1] = -1; | 231 shutdown_pipe_[1] = -1; |
| 225 if (inotify_fd_ >= 0 && pipe(shutdown_pipe_) == 0 && thread_.Start()) { | 232 if (inotify_fd_ >= 0 && pipe(shutdown_pipe_) == 0 && thread_.Start()) { |
| 226 thread_.message_loop()->PostTask( | 233 thread_.message_loop()->PostTask( |
| 227 FROM_HERE, Bind(&InotifyReaderCallback, this, inotify_fd_, | 234 FROM_HERE, |
| 228 shutdown_pipe_[0])); | 235 Bind(&InotifyReaderCallback, this, inotify_fd_, shutdown_pipe_[0])); |
| 229 valid_ = true; | 236 valid_ = true; |
| 230 } | 237 } |
| 231 } | 238 } |
| 232 | 239 |
| 233 InotifyReader::~InotifyReader() { | 240 InotifyReader::~InotifyReader() { |
| 234 if (valid_) { | 241 if (valid_) { |
| 235 // Write to the self-pipe so that the select call in InotifyReaderTask | 242 // Write to the self-pipe so that the select call in InotifyReaderTask |
| 236 // returns. | 243 // returns. |
| 237 ssize_t ret = HANDLE_EINTR(write(shutdown_pipe_[1], "", 1)); | 244 ssize_t ret = HANDLE_EINTR(write(shutdown_pipe_[1], "", 1)); |
| 238 DPCHECK(ret > 0); | 245 DPCHECK(ret > 0); |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 260 IN_ONLYDIR); | 267 IN_ONLYDIR); |
| 261 | 268 |
| 262 if (watch == kInvalidWatch) | 269 if (watch == kInvalidWatch) |
| 263 return kInvalidWatch; | 270 return kInvalidWatch; |
| 264 | 271 |
| 265 watchers_[watch].insert(watcher); | 272 watchers_[watch].insert(watcher); |
| 266 | 273 |
| 267 return watch; | 274 return watch; |
| 268 } | 275 } |
| 269 | 276 |
| 270 bool InotifyReader::RemoveWatch(Watch watch, | 277 void InotifyReader::RemoveWatch(Watch watch, FilePathWatcherImpl* watcher) { |
| 271 FilePathWatcherImpl* watcher) { | 278 if (!valid_ || (watch == kInvalidWatch)) |
|
Lei Zhang
2014/04/25 06:45:23
Slightly easier to check for invalid |watch| here
| |
| 272 if (!valid_) | 279 return; |
| 273 return false; | |
| 274 | 280 |
| 275 AutoLock auto_lock(lock_); | 281 AutoLock auto_lock(lock_); |
| 276 | 282 |
| 277 watchers_[watch].erase(watcher); | 283 watchers_[watch].erase(watcher); |
| 278 | 284 |
| 279 if (watchers_[watch].empty()) { | 285 if (watchers_[watch].empty()) { |
| 280 watchers_.erase(watch); | 286 watchers_.erase(watch); |
| 281 return (inotify_rm_watch(inotify_fd_, watch) == 0); | 287 inotify_rm_watch(inotify_fd_, watch); |
| 282 } | 288 } |
| 283 | |
| 284 return true; | |
| 285 } | 289 } |
| 286 | 290 |
| 287 void InotifyReader::OnInotifyEvent(const inotify_event* event) { | 291 void InotifyReader::OnInotifyEvent(const inotify_event* event) { |
| 288 if (event->mask & IN_IGNORED) | 292 if (event->mask & IN_IGNORED) |
| 289 return; | 293 return; |
| 290 | 294 |
| 291 FilePath::StringType child(event->len ? event->name : FILE_PATH_LITERAL("")); | 295 FilePath::StringType child(event->len ? event->name : ""); |
| 292 AutoLock auto_lock(lock_); | 296 AutoLock auto_lock(lock_); |
| 293 | 297 |
| 294 for (WatcherSet::iterator watcher = watchers_[event->wd].begin(); | 298 for (WatcherSet::iterator watcher = watchers_[event->wd].begin(); |
| 295 watcher != watchers_[event->wd].end(); | 299 watcher != watchers_[event->wd].end(); |
| 296 ++watcher) { | 300 ++watcher) { |
| 297 (*watcher)->OnFilePathChanged(event->wd, | 301 (*watcher)->OnFilePathChanged(event->wd, |
| 298 child, | 302 child, |
| 299 event->mask & (IN_CREATE | IN_MOVED_TO)); | 303 event->mask & (IN_CREATE | IN_MOVED_TO)); |
| 300 } | 304 } |
| 301 } | 305 } |
| 302 | 306 |
| 303 FilePathWatcherImpl::FilePathWatcherImpl() { | 307 FilePathWatcherImpl::FilePathWatcherImpl() { |
| 304 } | 308 } |
| 305 | 309 |
| 306 void FilePathWatcherImpl::OnFilePathChanged(InotifyReader::Watch fired_watch, | 310 void FilePathWatcherImpl::OnFilePathChanged(InotifyReader::Watch fired_watch, |
| 307 const FilePath::StringType& child, | 311 const FilePath::StringType& child, |
| 308 bool created) { | 312 bool created) { |
| 309 if (!message_loop()->BelongsToCurrentThread()) { | 313 if (!message_loop()->BelongsToCurrentThread()) { |
| 310 // Switch to message_loop_ to access watches_ safely. | 314 // Switch to message_loop() to access |watches_| safely. |
| 311 message_loop()->PostTask(FROM_HERE, | 315 message_loop()->PostTask( |
| 312 Bind(&FilePathWatcherImpl::OnFilePathChanged, | 316 FROM_HERE, |
| 313 this, | 317 Bind(&FilePathWatcherImpl::OnFilePathChanged, this, |
| 314 fired_watch, | 318 fired_watch, child, created)); |
| 315 child, | 319 return; |
| 316 created)); | 320 } |
| 321 | |
| 322 // Check to see if CancelOnMessageLoopThread() has already been called. | |
| 323 // Happens when code flow reaches here from the PostTask() above. | |
| 324 if (watches_.empty()) { | |
|
Lei Zhang
2014/04/25 06:45:23
Only needed because we call DCHECK(HasValidWatchVe
| |
| 325 DCHECK(target_.empty()); | |
| 317 return; | 326 return; |
| 318 } | 327 } |
| 319 | 328 |
| 320 DCHECK(MessageLoopForIO::current()); | 329 DCHECK(MessageLoopForIO::current()); |
| 330 DCHECK(HasValidWatchVector()); | |
| 321 | 331 |
| 322 // Find the entry in |watches_| that corresponds to |fired_watch|. | 332 // Find the entry in |watches_| that corresponds to |fired_watch|. |
| 323 WatchVector::const_iterator watch_entry(watches_.begin()); | 333 for (size_t i = 0; i < watches_.size(); ++i) { |
| 324 for ( ; watch_entry != watches_.end(); ++watch_entry) { | 334 const WatchEntry& watch_entry = watches_[i]; |
| 325 if (fired_watch == watch_entry->watch_) { | 335 if (fired_watch != watch_entry.watch_) |
| 326 // Check whether a path component of |target_| changed. | 336 continue; |
| 327 bool change_on_target_path = child.empty() || | |
| 328 ((child == watch_entry->subdir_) && watch_entry->linkname_.empty()) || | |
| 329 (child == watch_entry->linkname_); | |
| 330 | 337 |
| 331 // Check whether the change references |target_| or a direct child. | 338 // Check whether a path component of |target_| changed. |
| 332 DCHECK(watch_entry->subdir_.empty() || | 339 bool change_on_target_path = |
|
Lei Zhang
2014/04/25 06:45:23
No longer needed since we already validated |watch
| |
| 333 (watch_entry + 1) != watches_.end()); | 340 child.empty() || |
| 334 bool target_changed = | 341 (child == watch_entry.linkname_) || |
|
Lei Zhang
2014/04/25 06:45:23
I reordered the checks to avoid checking for linkn
| |
| 335 (watch_entry->subdir_.empty() && (child == watch_entry->linkname_)) || | 342 (child == watch_entry.subdir_); |
| 336 (watch_entry->subdir_.empty() && watch_entry->linkname_.empty()) || | |
| 337 (watch_entry->subdir_ == child && (watch_entry + 1)->subdir_.empty()); | |
| 338 | 343 |
| 339 // Update watches if a directory component of the |target_| path | 344 // Check if the change references |target_| or a direct child of |target_|. |
| 340 // (dis)appears. Note that we don't add the additional restriction | 345 bool is_direct_child = watch_entry.subdir_.empty(); |
| 341 // of checking the event mask to see if it is for a directory here | 346 bool target_changed = |
| 342 // as changes to symlinks on the target path will not have | 347 (is_direct_child && (child == watch_entry.linkname_)) || |
| 343 // IN_ISDIR set in the event masks. As a result we may sometimes | 348 (is_direct_child && watch_entry.linkname_.empty()) || |
| 344 // call UpdateWatches() unnecessarily. | 349 (watch_entry.subdir_ == child && watches_[i + 1].subdir_.empty()); |
| 345 if (change_on_target_path && !UpdateWatches()) { | |
| 346 callback_.Run(target_, true /* error */); | |
| 347 return; | |
| 348 } | |
| 349 | 350 |
| 350 // Report the following events: | 351 // Update watches if a directory component of the |target_| path |
| 351 // - The target or a direct child of the target got changed (in case the | 352 // (dis)appears. Note that we don't add the additional restriction |
| 352 // watched path refers to a directory). | 353 // of checking the event mask to see if it is for a directory here |
| 353 // - One of the parent directories got moved or deleted, since the target | 354 // as changes to symlinks on the target path will not have |
| 354 // disappears in this case. | 355 // IN_ISDIR set in the event masks. As a result we may sometimes |
| 355 // - One of the parent directories appears. The event corresponding to | 356 // call UpdateWatches() unnecessarily. |
| 356 // the target appearing might have been missed in this case, so | 357 if (change_on_target_path && !UpdateWatches()) { |
| 357 // recheck. | 358 callback_.Run(target_, true /* error */); |
| 358 if (target_changed || | 359 return; |
| 359 (change_on_target_path && !created) || | 360 } |
| 360 (change_on_target_path && PathExists(target_))) { | 361 |
| 361 callback_.Run(target_, false); | 362 // Report the following events: |
| 362 return; | 363 // - The target or a direct child of the target got changed (in case the |
| 363 } | 364 // watched path refers to a directory). |
| 365 // - One of the parent directories got moved or deleted, since the target | |
| 366 // disappears in this case. | |
| 367 // - One of the parent directories appears. The event corresponding to | |
| 368 // the target appearing might have been missed in this case, so | |
| 369 // recheck. | |
| 370 if (target_changed || | |
| 371 (change_on_target_path && !created) || | |
| 372 (change_on_target_path && PathExists(target_))) { | |
| 373 callback_.Run(target_, false /* error */); | |
| 374 return; | |
| 364 } | 375 } |
| 365 } | 376 } |
| 366 } | 377 } |
| 367 | 378 |
| 368 bool FilePathWatcherImpl::Watch(const FilePath& path, | 379 bool FilePathWatcherImpl::Watch(const FilePath& path, |
| 369 bool recursive, | 380 bool recursive, |
| 370 const FilePathWatcher::Callback& callback) { | 381 const FilePathWatcher::Callback& callback) { |
| 371 DCHECK(target_.empty()); | 382 DCHECK(target_.empty()); |
| 372 DCHECK(MessageLoopForIO::current()); | 383 DCHECK(MessageLoopForIO::current()); |
| 373 if (recursive) { | 384 if (recursive) { |
| 374 // Recursive watch is not supported on this platform. | 385 // Recursive watch is not supported on this platform. |
| 375 NOTIMPLEMENTED(); | 386 NOTIMPLEMENTED(); |
| 376 return false; | 387 return false; |
| 377 } | 388 } |
| 378 | 389 |
| 379 set_message_loop(MessageLoopProxy::current().get()); | 390 set_message_loop(MessageLoopProxy::current().get()); |
| 380 callback_ = callback; | 391 callback_ = callback; |
| 381 target_ = path; | 392 target_ = path; |
| 382 MessageLoop::current()->AddDestructionObserver(this); | 393 MessageLoop::current()->AddDestructionObserver(this); |
| 383 | 394 |
| 384 std::vector<FilePath::StringType> comps; | 395 std::vector<FilePath::StringType> comps; |
| 385 target_.GetComponents(&comps); | 396 target_.GetComponents(&comps); |
| 386 DCHECK(!comps.empty()); | 397 DCHECK(!comps.empty()); |
| 387 std::vector<FilePath::StringType>::const_iterator comp = comps.begin(); | 398 for (size_t i = 1; i < comps.size(); ++i) |
| 388 for (++comp; comp != comps.end(); ++comp) | 399 watches_.push_back(WatchEntry(comps[i])); |
| 389 watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch, *comp)); | 400 watches_.push_back(WatchEntry(FilePath::StringType())); |
| 390 | |
| 391 watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch, | |
| 392 FilePath::StringType())); | |
| 393 return UpdateWatches(); | 401 return UpdateWatches(); |
| 394 } | 402 } |
| 395 | 403 |
| 396 void FilePathWatcherImpl::Cancel() { | 404 void FilePathWatcherImpl::Cancel() { |
| 397 if (callback_.is_null()) { | 405 if (callback_.is_null()) { |
| 398 // Watch was never called, or the |message_loop_| thread is already gone. | 406 // Watch was never called, or the message_loop() thread is already gone. |
| 399 set_cancelled(); | 407 set_cancelled(); |
| 400 return; | 408 return; |
| 401 } | 409 } |
| 402 | 410 |
| 403 // Switch to the message_loop_ if necessary so we can access |watches_|. | 411 // Switch to the message_loop() if necessary so we can access |watches_|. |
| 404 if (!message_loop()->BelongsToCurrentThread()) { | 412 if (!message_loop()->BelongsToCurrentThread()) { |
| 405 message_loop()->PostTask(FROM_HERE, | 413 message_loop()->PostTask(FROM_HERE, |
| 406 Bind(&FilePathWatcher::CancelWatch, | 414 Bind(&FilePathWatcher::CancelWatch, |
| 407 make_scoped_refptr(this))); | 415 make_scoped_refptr(this))); |
| 408 } else { | 416 } else { |
| 409 CancelOnMessageLoopThread(); | 417 CancelOnMessageLoopThread(); |
| 410 } | 418 } |
| 411 } | 419 } |
| 412 | 420 |
| 413 void FilePathWatcherImpl::CancelOnMessageLoopThread() { | 421 void FilePathWatcherImpl::CancelOnMessageLoopThread() { |
| 414 if (!is_cancelled()) | 422 set_cancelled(); |
| 415 set_cancelled(); | |
| 416 | 423 |
| 417 if (!callback_.is_null()) { | 424 if (!callback_.is_null()) { |
| 418 MessageLoop::current()->RemoveDestructionObserver(this); | 425 MessageLoop::current()->RemoveDestructionObserver(this); |
| 419 callback_.Reset(); | 426 callback_.Reset(); |
| 420 } | 427 } |
| 421 | 428 |
| 422 for (WatchVector::iterator watch_entry(watches_.begin()); | 429 for (size_t i = 0; i < watches_.size(); ++i) |
| 423 watch_entry != watches_.end(); ++watch_entry) { | 430 g_inotify_reader.Get().RemoveWatch(watches_[i].watch_, this); |
| 424 if (watch_entry->watch_ != InotifyReader::kInvalidWatch) | |
| 425 g_inotify_reader.Get().RemoveWatch(watch_entry->watch_, this); | |
| 426 } | |
| 427 watches_.clear(); | 431 watches_.clear(); |
| 428 target_.clear(); | 432 target_.clear(); |
| 429 } | 433 } |
| 430 | 434 |
| 431 void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() { | 435 void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() { |
| 432 CancelOnMessageLoopThread(); | 436 CancelOnMessageLoopThread(); |
| 433 } | 437 } |
| 434 | 438 |
| 435 bool FilePathWatcherImpl::UpdateWatches() { | 439 bool FilePathWatcherImpl::UpdateWatches() { |
| 436 // Ensure this runs on the |message_loop_| exclusively in order to avoid | 440 // Ensure this runs on the message_loop() exclusively in order to avoid |
| 437 // concurrency issues. | 441 // concurrency issues. |
| 438 DCHECK(message_loop()->BelongsToCurrentThread()); | 442 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 443 DCHECK(HasValidWatchVector()); | |
| 439 | 444 |
| 440 // Walk the list of watches and update them as we go. | 445 // Walk the list of watches and update them as we go. |
| 441 FilePath path(FILE_PATH_LITERAL("/")); | 446 FilePath path("/"); |
| 442 bool path_valid = true; | 447 bool path_valid = true; |
| 443 for (WatchVector::iterator watch_entry(watches_.begin()); | 448 for (size_t i = 0; i < watches_.size(); ++i) { |
| 444 watch_entry != watches_.end(); ++watch_entry) { | 449 WatchEntry& watch_entry = watches_[i]; |
| 445 InotifyReader::Watch old_watch = watch_entry->watch_; | 450 InotifyReader::Watch old_watch = watch_entry.watch_; |
| 451 watch_entry.watch_ = InotifyReader::kInvalidWatch; | |
| 452 watch_entry.linkname_.clear(); | |
|
Lei Zhang
2014/04/25 06:45:23
This is one actual change to the functionality. Wh
| |
| 446 if (path_valid) { | 453 if (path_valid) { |
| 447 watch_entry->watch_ = g_inotify_reader.Get().AddWatch(path, this); | 454 watch_entry.watch_ = g_inotify_reader.Get().AddWatch(path, this); |
| 448 if ((watch_entry->watch_ == InotifyReader::kInvalidWatch) && | 455 if (watch_entry.watch_ == InotifyReader::kInvalidWatch) { |
| 449 base::IsLink(path)) { | 456 path_valid = AddWatchForBrokenSymlink(path, &watch_entry); |
| 450 FilePath link; | |
| 451 if (ReadSymbolicLink(path, &link)) { | |
| 452 if (!link.IsAbsolute()) | |
| 453 link = path.DirName().Append(link); | |
| 454 // Try watching symlink target directory. If the link target is "/", | |
| 455 // then we shouldn't get here in normal situations and if we do, we'd | |
| 456 // watch "/" for changes to a component "/" which is harmless so no | |
| 457 // special treatment of this case is required. | |
| 458 watch_entry->watch_ = | |
| 459 g_inotify_reader.Get().AddWatch(link.DirName(), this); | |
| 460 if (watch_entry->watch_ != InotifyReader::kInvalidWatch) { | |
| 461 watch_entry->linkname_ = link.BaseName().value(); | |
| 462 } else { | |
| 463 DPLOG(WARNING) << "Watch failed for " << link.DirName().value(); | |
| 464 // TODO(craig) Symlinks only work if the parent directory | |
| 465 // for the target exist. Ideally we should make sure we've | |
| 466 // watched all the components of the symlink path for | |
| 467 // changes. See crbug.com/91561 for details. | |
| 468 } | |
| 469 } | |
| 470 } | 457 } |
| 471 if (watch_entry->watch_ == InotifyReader::kInvalidWatch) { | |
| 472 path_valid = false; | |
| 473 } | |
| 474 } else { | |
| 475 watch_entry->watch_ = InotifyReader::kInvalidWatch; | |
| 476 } | 458 } |
| 477 if (old_watch != InotifyReader::kInvalidWatch && | 459 if (old_watch != watch_entry.watch_) |
| 478 old_watch != watch_entry->watch_) { | |
| 479 g_inotify_reader.Get().RemoveWatch(old_watch, this); | 460 g_inotify_reader.Get().RemoveWatch(old_watch, this); |
| 480 } | 461 path = path.Append(watch_entry.subdir_); |
| 481 path = path.Append(watch_entry->subdir_); | |
| 482 } | 462 } |
| 483 | 463 |
| 484 return true; | 464 return true; |
| 485 } | 465 } |
| 486 | 466 |
| 467 bool FilePathWatcherImpl::AddWatchForBrokenSymlink(const FilePath& path, | |
| 468 WatchEntry* watch_entry) { | |
| 469 DCHECK_EQ(InotifyReader::kInvalidWatch, watch_entry->watch_); | |
| 470 if (!IsLink(path)) | |
| 471 return false; | |
| 472 | |
| 473 FilePath link; | |
| 474 if (!ReadSymbolicLink(path, &link)) | |
| 475 return false; | |
| 476 | |
| 477 if (!link.IsAbsolute()) | |
| 478 link = path.DirName().Append(link); | |
| 479 | |
| 480 // Try watching symlink target directory. If the link target is "/", | |
| 481 // then we shouldn't get here in normal situations and if we do, we'd | |
| 482 // watch "/" for changes to a component "/" which is harmless so no | |
| 483 // special treatment of this case is required. | |
| 484 InotifyReader::Watch watch = | |
| 485 g_inotify_reader.Get().AddWatch(link.DirName(), this); | |
| 486 if (watch == InotifyReader::kInvalidWatch) { | |
| 487 // TODO(craig) Symlinks only work if the parent directory | |
| 488 // for the target exist. Ideally we should make sure we've | |
| 489 // watched all the components of the symlink path for | |
| 490 // changes. See crbug.com/91561 for details. | |
| 491 DPLOG(WARNING) << "Watch failed for " << link.DirName().value(); | |
| 492 return false; | |
| 493 } | |
| 494 watch_entry->watch_ = watch; | |
| 495 watch_entry->linkname_ = link.BaseName().value(); | |
| 496 return true; | |
| 497 } | |
| 498 | |
| 499 bool FilePathWatcherImpl::HasValidWatchVector() const { | |
| 500 if (watches_.empty()) | |
| 501 return false; | |
| 502 for (size_t i = 0; i < watches_.size() - 1; ++i) { | |
| 503 if (watches_[i].subdir_.empty()) | |
| 504 return false; | |
| 505 } | |
| 506 return watches_[watches_.size() - 1].subdir_.empty(); | |
| 507 } | |
| 508 | |
| 487 } // namespace | 509 } // namespace |
| 488 | 510 |
| 489 FilePathWatcher::FilePathWatcher() { | 511 FilePathWatcher::FilePathWatcher() { |
| 490 impl_ = new FilePathWatcherImpl(); | 512 impl_ = new FilePathWatcherImpl(); |
| 491 } | 513 } |
| 492 | 514 |
| 493 } // namespace base | 515 } // namespace base |
| OLD | NEW |