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); |
| 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& dirname) |
| 127 : watch_(watch), | 127 : watch(InotifyReader::kInvalidWatch), |
| 128 subdir_(subdir) {} | 128 subdir(dirname) {} |
| 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| | |
| 142 // on 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_; |
| 150 | 157 |
| 151 DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl); | 158 DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl); |
| 152 }; | 159 }; |
| 153 | 160 |
| 154 void InotifyReaderCallback(InotifyReader* reader, int inotify_fd, | 161 void InotifyReaderCallback(InotifyReader* reader, int inotify_fd, |
| 155 int shutdown_fd) { | 162 int shutdown_fd) { |
| 156 // Make sure the file descriptors are good for use with select(). | 163 // Make sure the file descriptors are good for use with select(). |
| 157 CHECK_LE(0, inotify_fd); | 164 CHECK_LE(0, inotify_fd); |
| 158 CHECK_GT(FD_SETSIZE, inotify_fd); | 165 CHECK_GT(FD_SETSIZE, inotify_fd); |
| (...skipping 58 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)) |
| 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 : ""); |
|
Mattias Nissler (ping if slow)
2014/04/25 08:38:24
Dropping FILE_PATH_LITERAL adds inconsistency: Eit
Lei Zhang
2014/04/25 08:49:32
Ok, done.
| |
| 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. | |
|
Mattias Nissler (ping if slow)
2014/04/25 08:38:24
nit: s/Happens/May happen/
Lei Zhang
2014/04/25 08:49:32
Done.
| |
| 324 if (watches_.empty()) { | |
| 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) { |
|
Mattias Nissler (ping if slow)
2014/04/25 08:38:24
So you just prefer indexing over iterators? Or is
Lei Zhang
2014/04/25 08:49:32
I do, it makes the for-loop line easier to read in
Mattias Nissler (ping if slow)
2014/04/25 09:06:57
I don't see the advantage to be honest, and I pers
| |
| 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 = |
| 333 (watch_entry + 1) != watches_.end()); | 340 child.empty() || |
| 334 bool target_changed = | 341 (child == watch_entry.linkname) || |
| 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(); |
|
Mattias Nissler (ping if slow)
2014/04/25 08:38:24
is_direct_child is a misleading name. If watch_ent
Lei Zhang
2014/04/25 08:49:32
Done.
| |
| 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 of |
| 352 // watched path refers to a directory). | 353 // checking the event mask to see if it is for a directory here as changes |
| 353 // - One of the parent directories got moved or deleted, since the target | 354 // to symlinks on the target path will not have IN_ISDIR set in the event |
| 354 // disappears in this case. | 355 // masks. As a result we may sometimes call UpdateWatches() unnecessarily. |
| 355 // - One of the parent directories appears. The event corresponding to | 356 if (change_on_target_path && !UpdateWatches()) { |
| 356 // the target appearing might have been missed in this case, so | 357 callback_.Run(target_, true /* error */); |
| 357 // recheck. | 358 return; |
| 358 if (target_changed || | 359 } |
| 359 (change_on_target_path && !created) || | 360 |
| 360 (change_on_target_path && PathExists(target_))) { | 361 // Report the following events: |
| 361 callback_.Run(target_, false); | 362 // - The target or a direct child of the target got changed (in case the |
| 362 return; | 363 // watched path refers to a directory). |
| 363 } | 364 // - One of the parent directories got moved or deleted, since the target |
| 365 // disappears in this case. | |
| 366 // - One of the parent directories appears. The event corresponding to | |
| 367 // the target appearing might have been missed in this case, so recheck. | |
| 368 if (target_changed || | |
| 369 (change_on_target_path && !created) || | |
| 370 (change_on_target_path && PathExists(target_))) { | |
| 371 callback_.Run(target_, false /* error */); | |
| 372 return; | |
| 364 } | 373 } |
| 365 } | 374 } |
| 366 } | 375 } |
| 367 | 376 |
| 368 bool FilePathWatcherImpl::Watch(const FilePath& path, | 377 bool FilePathWatcherImpl::Watch(const FilePath& path, |
| 369 bool recursive, | 378 bool recursive, |
| 370 const FilePathWatcher::Callback& callback) { | 379 const FilePathWatcher::Callback& callback) { |
| 371 DCHECK(target_.empty()); | 380 DCHECK(target_.empty()); |
| 372 DCHECK(MessageLoopForIO::current()); | 381 DCHECK(MessageLoopForIO::current()); |
| 373 if (recursive) { | 382 if (recursive) { |
| 374 // Recursive watch is not supported on this platform. | 383 // Recursive watch is not supported on this platform. |
| 375 NOTIMPLEMENTED(); | 384 NOTIMPLEMENTED(); |
| 376 return false; | 385 return false; |
| 377 } | 386 } |
| 378 | 387 |
| 379 set_message_loop(MessageLoopProxy::current().get()); | 388 set_message_loop(MessageLoopProxy::current().get()); |
| 380 callback_ = callback; | 389 callback_ = callback; |
| 381 target_ = path; | 390 target_ = path; |
| 382 MessageLoop::current()->AddDestructionObserver(this); | 391 MessageLoop::current()->AddDestructionObserver(this); |
| 383 | 392 |
| 384 std::vector<FilePath::StringType> comps; | 393 std::vector<FilePath::StringType> comps; |
| 385 target_.GetComponents(&comps); | 394 target_.GetComponents(&comps); |
| 386 DCHECK(!comps.empty()); | 395 DCHECK(!comps.empty()); |
| 387 std::vector<FilePath::StringType>::const_iterator comp = comps.begin(); | 396 for (size_t i = 1; i < comps.size(); ++i) |
| 388 for (++comp; comp != comps.end(); ++comp) | 397 watches_.push_back(WatchEntry(comps[i])); |
| 389 watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch, *comp)); | 398 watches_.push_back(WatchEntry(FilePath::StringType())); |
| 390 | |
| 391 watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch, | |
| 392 FilePath::StringType())); | |
| 393 return UpdateWatches(); | 399 return UpdateWatches(); |
| 394 } | 400 } |
| 395 | 401 |
| 396 void FilePathWatcherImpl::Cancel() { | 402 void FilePathWatcherImpl::Cancel() { |
| 397 if (callback_.is_null()) { | 403 if (callback_.is_null()) { |
| 398 // Watch was never called, or the |message_loop_| thread is already gone. | 404 // Watch was never called, or the message_loop() thread is already gone. |
| 399 set_cancelled(); | 405 set_cancelled(); |
| 400 return; | 406 return; |
| 401 } | 407 } |
| 402 | 408 |
| 403 // Switch to the message_loop_ if necessary so we can access |watches_|. | 409 // Switch to the message_loop() if necessary so we can access |watches_|. |
| 404 if (!message_loop()->BelongsToCurrentThread()) { | 410 if (!message_loop()->BelongsToCurrentThread()) { |
| 405 message_loop()->PostTask(FROM_HERE, | 411 message_loop()->PostTask(FROM_HERE, |
| 406 Bind(&FilePathWatcher::CancelWatch, | 412 Bind(&FilePathWatcher::CancelWatch, |
| 407 make_scoped_refptr(this))); | 413 make_scoped_refptr(this))); |
| 408 } else { | 414 } else { |
| 409 CancelOnMessageLoopThread(); | 415 CancelOnMessageLoopThread(); |
| 410 } | 416 } |
| 411 } | 417 } |
| 412 | 418 |
| 413 void FilePathWatcherImpl::CancelOnMessageLoopThread() { | 419 void FilePathWatcherImpl::CancelOnMessageLoopThread() { |
| 414 if (!is_cancelled()) | 420 set_cancelled(); |
| 415 set_cancelled(); | |
| 416 | 421 |
| 417 if (!callback_.is_null()) { | 422 if (!callback_.is_null()) { |
| 418 MessageLoop::current()->RemoveDestructionObserver(this); | 423 MessageLoop::current()->RemoveDestructionObserver(this); |
| 419 callback_.Reset(); | 424 callback_.Reset(); |
| 420 } | 425 } |
| 421 | 426 |
| 422 for (WatchVector::iterator watch_entry(watches_.begin()); | 427 for (size_t i = 0; i < watches_.size(); ++i) |
| 423 watch_entry != watches_.end(); ++watch_entry) { | 428 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(); | 429 watches_.clear(); |
| 428 target_.clear(); | 430 target_.clear(); |
| 429 } | 431 } |
| 430 | 432 |
| 431 void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() { | 433 void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() { |
| 432 CancelOnMessageLoopThread(); | 434 CancelOnMessageLoopThread(); |
| 433 } | 435 } |
| 434 | 436 |
| 435 bool FilePathWatcherImpl::UpdateWatches() { | 437 bool FilePathWatcherImpl::UpdateWatches() { |
| 436 // Ensure this runs on the |message_loop_| exclusively in order to avoid | 438 // Ensure this runs on the message_loop() exclusively in order to avoid |
| 437 // concurrency issues. | 439 // concurrency issues. |
| 438 DCHECK(message_loop()->BelongsToCurrentThread()); | 440 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 441 DCHECK(HasValidWatchVector()); | |
| 439 | 442 |
| 440 // Walk the list of watches and update them as we go. | 443 // Walk the list of watches and update them as we go. |
| 441 FilePath path(FILE_PATH_LITERAL("/")); | 444 FilePath path("/"); |
| 442 bool path_valid = true; | 445 bool path_valid = true; |
| 443 for (WatchVector::iterator watch_entry(watches_.begin()); | 446 for (size_t i = 0; i < watches_.size(); ++i) { |
| 444 watch_entry != watches_.end(); ++watch_entry) { | 447 WatchEntry& watch_entry = watches_[i]; |
| 445 InotifyReader::Watch old_watch = watch_entry->watch_; | 448 InotifyReader::Watch old_watch = watch_entry.watch; |
| 449 watch_entry.watch = InotifyReader::kInvalidWatch; | |
| 450 watch_entry.linkname.clear(); | |
| 446 if (path_valid) { | 451 if (path_valid) { |
| 447 watch_entry->watch_ = g_inotify_reader.Get().AddWatch(path, this); | 452 watch_entry.watch = g_inotify_reader.Get().AddWatch(path, this); |
| 448 if ((watch_entry->watch_ == InotifyReader::kInvalidWatch) && | 453 if (watch_entry.watch == InotifyReader::kInvalidWatch) { |
|
Mattias Nissler (ping if slow)
2014/04/25 08:38:24
I would prefer to leave the base::IsLink() check h
Lei Zhang
2014/04/25 08:49:32
Done.
| |
| 449 base::IsLink(path)) { | 454 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 } | 455 } |
| 471 if (watch_entry->watch_ == InotifyReader::kInvalidWatch) { | |
| 472 path_valid = false; | |
| 473 } | |
| 474 } else { | |
| 475 watch_entry->watch_ = InotifyReader::kInvalidWatch; | |
| 476 } | 456 } |
| 477 if (old_watch != InotifyReader::kInvalidWatch && | 457 if (old_watch != watch_entry.watch) |
| 478 old_watch != watch_entry->watch_) { | |
| 479 g_inotify_reader.Get().RemoveWatch(old_watch, this); | 458 g_inotify_reader.Get().RemoveWatch(old_watch, this); |
| 480 } | 459 path = path.Append(watch_entry.subdir); |
| 481 path = path.Append(watch_entry->subdir_); | |
| 482 } | 460 } |
| 483 | 461 |
| 484 return true; | 462 return true; |
| 485 } | 463 } |
| 486 | 464 |
| 465 bool FilePathWatcherImpl::AddWatchForBrokenSymlink(const FilePath& path, | |
| 466 WatchEntry* watch_entry) { | |
| 467 DCHECK_EQ(InotifyReader::kInvalidWatch, watch_entry->watch); | |
| 468 if (!IsLink(path)) | |
| 469 return false; | |
| 470 | |
| 471 FilePath link; | |
| 472 if (!ReadSymbolicLink(path, &link)) | |
| 473 return false; | |
| 474 | |
| 475 if (!link.IsAbsolute()) | |
| 476 link = path.DirName().Append(link); | |
| 477 | |
| 478 // Try watching symlink target directory. If the link target is "/", then we | |
| 479 // shouldn't get here in normal situations and if we do, we'd watch "/" for | |
| 480 // changes to a component "/" which is harmless so no special treatment of | |
| 481 // this case is required. | |
| 482 InotifyReader::Watch watch = | |
| 483 g_inotify_reader.Get().AddWatch(link.DirName(), this); | |
| 484 if (watch == InotifyReader::kInvalidWatch) { | |
| 485 // TODO(craig) Symlinks only work if the parent directory for the target | |
| 486 // exist. Ideally we should make sure we've watched all the components of | |
| 487 // the symlink path for changes. See crbug.com/91561 for details. | |
| 488 DPLOG(WARNING) << "Watch failed for " << link.DirName().value(); | |
| 489 return false; | |
| 490 } | |
| 491 watch_entry->watch = watch; | |
| 492 watch_entry->linkname = link.BaseName().value(); | |
| 493 return true; | |
| 494 } | |
| 495 | |
| 496 bool FilePathWatcherImpl::HasValidWatchVector() const { | |
| 497 if (watches_.empty()) | |
| 498 return false; | |
| 499 for (size_t i = 0; i < watches_.size() - 1; ++i) { | |
| 500 if (watches_[i].subdir.empty()) | |
| 501 return false; | |
| 502 } | |
| 503 return watches_[watches_.size() - 1].subdir.empty(); | |
| 504 } | |
| 505 | |
| 487 } // namespace | 506 } // namespace |
| 488 | 507 |
| 489 FilePathWatcher::FilePathWatcher() { | 508 FilePathWatcher::FilePathWatcher() { |
| 490 impl_ = new FilePathWatcherImpl(); | 509 impl_ = new FilePathWatcherImpl(); |
| 491 } | 510 } |
| 492 | 511 |
| 493 } // namespace base | 512 } // namespace base |
| OLD | NEW |