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

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

Issue 250833003: Clean up the Linux FileWatcher implementation. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: fix style error for struct, misc nits Created 6 years, 8 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 | « no previous file | 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) 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
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
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
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
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
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698