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

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: 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);
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
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
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))
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
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