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

Side by Side Diff: base/directory_watcher_inotify.cc

Issue 52005: Implement recursive DirectoryWatcher watches on Linux (Closed) Base URL: http://src.chromium.org/svn/trunk/src/
Patch Set: '' Created 11 years, 7 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 | base/directory_watcher_unittest.cc » ('j') | 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) 2009 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2009 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/directory_watcher.h" 5 #include "base/directory_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 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
81 DISALLOW_COPY_AND_ASSIGN(InotifyReader); 81 DISALLOW_COPY_AND_ASSIGN(InotifyReader);
82 }; 82 };
83 83
84 class DirectoryWatcherImpl : public DirectoryWatcher::PlatformDelegate { 84 class DirectoryWatcherImpl : public DirectoryWatcher::PlatformDelegate {
85 public: 85 public:
86 typedef std::set<FilePath> FilePathSet; 86 typedef std::set<FilePath> FilePathSet;
87 87
88 DirectoryWatcherImpl(); 88 DirectoryWatcherImpl();
89 ~DirectoryWatcherImpl(); 89 ~DirectoryWatcherImpl();
90 90
91 void EnsureSetupFinished();
92
93 // Called for each event coming from one of watches. 91 // Called for each event coming from one of watches.
94 void OnInotifyEvent(const inotify_event* event); 92 void OnInotifyEvent(const inotify_event* event);
95 93
96 // Callback for RegisterSubtreeWatchesTask. 94 // Callback for RegisterSubtreeWatchesTask.
97 bool OnEnumeratedSubtree(const FilePathSet& paths); 95 bool OnEnumeratedSubtree(const FilePathSet& paths);
98 96
99 // Start watching |path| for changes and notify |delegate| on each change. 97 // Start watching |path| for changes and notify |delegate| on each change.
100 // If |recursive| is true, watch entire subtree. 98 // If |recursive| is true, watch entire subtree.
101 // Returns true if watch for |path| has been added successfully. Watches 99 // Returns true if watch for |path| has been added successfully. Watches
102 // required for |recursive| are added on a background thread and have no 100 // required for |recursive| are added on a background thread and have no
(...skipping 10 matching lines...) Expand all
113 111
114 // Delegate to notify upon changes. 112 // Delegate to notify upon changes.
115 DirectoryWatcher::Delegate* delegate_; 113 DirectoryWatcher::Delegate* delegate_;
116 114
117 // Path we're watching (passed to delegate). 115 // Path we're watching (passed to delegate).
118 FilePath root_path_; 116 FilePath root_path_;
119 117
120 // Watch returned by InotifyReader. 118 // Watch returned by InotifyReader.
121 InotifyReader::Watch watch_; 119 InotifyReader::Watch watch_;
122 120
123 // Set of watched inodes. 121 // Needed because inotify event-> name only provides name of the
124 InodeSet inodes_watched_; 122 // newly created/deleted file, without any path information.
123 // event->name = SubDir
124 // With this data structure we can get /tmp/DirectoryWatcherTest/SubDir.
125 base::hash_map<InotifyReader::Watch, FilePath> paths_;
125 126
126 // Keep track of registered watches. 127 typedef std::pair<ino_t, FilePath> InodeInfo;
127 WatchSet watches_; 128 base::hash_map<InotifyReader::Watch, InodeInfo> watches_;
128 129
129 // Lock to protect inodes_watched_ and watches_. 130 // Lock to protect inodes_watched_ and watches_.
130 Lock lock_; 131 Lock lock_;
131 132
132 // Flag set to true when recursively watching subtree. 133 // Flag set to true when recursively watching subtree.
133 bool recursive_; 134 bool recursive_;
134 135
135 // Loop where we post directory change notifications to. 136 // Loop where we post directory change notifications to.
136 MessageLoop* loop_; 137 MessageLoop* loop_;
137 138
(...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after
295 IN_CREATE | IN_DELETE | 296 IN_CREATE | IN_DELETE |
296 IN_CLOSE_WRITE | IN_MOVE); 297 IN_CLOSE_WRITE | IN_MOVE);
297 298
298 if (watch == kInvalidWatch) 299 if (watch == kInvalidWatch)
299 return kInvalidWatch; 300 return kInvalidWatch;
300 301
301 if (paths_[watch].empty()) 302 if (paths_[watch].empty())
302 paths_[watch] = path; // We don't yet watch this path. 303 paths_[watch] = path; // We don't yet watch this path.
303 304
304 watchers_[watch].insert(watcher); 305 watchers_[watch].insert(watcher);
305
306 return watch; 306 return watch;
307 } 307 }
308 308
309 bool InotifyReader::RemoveWatch(Watch watch, 309 bool InotifyReader::RemoveWatch(Watch watch,
310 DirectoryWatcherImpl* watcher) { 310 DirectoryWatcherImpl* watcher) {
311 if (!valid_) 311 if (!valid_)
312 return false; 312 return false;
313 313
314 AutoLock auto_lock(lock_); 314 AutoLock auto_lock(lock_);
315 315
316 if (paths_[watch].empty()) 316 if (paths_[watch].empty())
317 return false; // We don't recognize this watch. 317 return false; // We don't recognize this watch.
318 318
319 watchers_[watch].erase(watcher); 319 watchers_[watch].erase(watcher);
320 320
321 if (watchers_[watch].empty()) { 321 if (watchers_[watch].empty()) {
322 paths_.erase(watch); 322 paths_.erase(watch);
323 watchers_.erase(watch); 323 watchers_.erase(watch);
324 return (inotify_rm_watch(inotify_fd_, watch) == 0); 324 return (inotify_rm_watch(inotify_fd_, watch) == 0);
325 } 325 }
326
327 return true; 326 return true;
328 } 327 }
329 328
330 void InotifyReader::OnInotifyEvent(const inotify_event* event) { 329 void InotifyReader::OnInotifyEvent(const inotify_event* event) {
331 if (event->mask & IN_IGNORED) 330 if (event->mask & IN_IGNORED)
332 return; 331 return;
333 332
334 WatcherSet watchers_to_notify; 333 WatcherSet watchers_to_notify;
335 FilePath changed_path; 334 FilePath changed_path;
336 335
337 { 336 {
338 AutoLock auto_lock(lock_); 337 AutoLock auto_lock(lock_);
339 changed_path = paths_[event->wd]; 338 changed_path = paths_[event->wd];
340 watchers_to_notify.insert(watchers_[event->wd].begin(), 339 watchers_to_notify.insert(watchers_[event->wd].begin(),
341 watchers_[event->wd].end()); 340 watchers_[event->wd].end());
342 } 341 }
343 342
344 for (WatcherSet::iterator watcher = watchers_to_notify.begin(); 343 for (WatcherSet::const_iterator watcher = watchers_to_notify.begin();
345 watcher != watchers_to_notify.end(); 344 watcher != watchers_to_notify.end();
346 ++watcher) { 345 ++watcher) {
347 (*watcher)->OnInotifyEvent(event); 346 (*watcher)->OnInotifyEvent(event);
348 } 347 }
349 } 348 }
350 349
351 DirectoryWatcherImpl::DirectoryWatcherImpl() 350 DirectoryWatcherImpl::DirectoryWatcherImpl()
352 : watch_(InotifyReader::kInvalidWatch), 351 : watch_(InotifyReader::kInvalidWatch),
353 recursive_setup_finished_(false, false) { 352 recursive_setup_finished_(false, false) {
354 } 353 }
355 354
356 DirectoryWatcherImpl::~DirectoryWatcherImpl() { 355 DirectoryWatcherImpl::~DirectoryWatcherImpl() {
357 if (watch_ == InotifyReader::kInvalidWatch) 356 if (watch_ == InotifyReader::kInvalidWatch)
358 return; 357 return;
359 358
360 if (recursive_) 359 if (recursive_)
361 recursive_setup_finished_.Wait(); 360 recursive_setup_finished_.Wait();
362 for (WatchSet::iterator watch = watches_.begin(); 361
363 watch != watches_.end(); 362 for (base::hash_map<InotifyReader::Watch, InodeInfo>::const_iterator it =
364 ++watch) { 363 watches_.begin();
365 Singleton<InotifyReader>::get()->RemoveWatch(*watch, this); 364 it != watches_.end();
365 ++it) {
366 Singleton<InotifyReader>::get()->RemoveWatch(it->first, this);
366 } 367 }
367 watches_.clear(); 368 watches_.clear();
368 inodes_watched_.clear();
369 } 369 }
370 370
371 void DirectoryWatcherImpl::OnInotifyEvent(const inotify_event* event) { 371 void DirectoryWatcherImpl::OnInotifyEvent(const inotify_event* event) {
372 loop_->PostTask(FROM_HERE, 372 loop_->PostTask(FROM_HERE,
373 new DirectoryWatcherImplNotifyTask(delegate_, root_path_)); 373 new DirectoryWatcherImplNotifyTask(delegate_, root_path_));
374 374
375 if (!(event->mask & IN_ISDIR)) 375 if (!(event->mask & IN_ISDIR))
376 return; 376 return;
377 377
378 FilePath path(watches_[event->wd].second);
379 path.Append(event->name);
378 if (event->mask & IN_CREATE || event->mask & IN_MOVED_TO) { 380 if (event->mask & IN_CREATE || event->mask & IN_MOVED_TO) {
379 // TODO(phajdan.jr): add watch for this new directory. 381 // Don't do anything, if not recursive watch;
380 NOTIMPLEMENTED(); 382 if (!recursive_)
383 return;
384 if (event->mask & IN_MOVED_TO) {
385 // Don't do anything, if path is moved outside of root_path_.
386 if (path.value().compare(0, root_path_.value().length(),
387 root_path_.value()))
388 return;
389 }
390 recursive_setup_finished_.Wait();
391 scoped_ptr<Task> subtree_task(new RegisterSubtreeWatchesTask(this, path));
392 subtree_task->Run();
381 } else if (event->mask & IN_DELETE || event->mask & IN_MOVED_FROM) { 393 } else if (event->mask & IN_DELETE || event->mask & IN_MOVED_FROM) {
382 // TODO(phajdan.jr): remove our watch for this directory. 394 Singleton<InotifyReader>::get()->RemoveWatch(event->wd, this);
383 NOTIMPLEMENTED(); 395
396 WatchSet delete_us;
397 AutoLock auto_lock(lock_);
398 watches_.erase(event->wd);
399 // Iterating on paths_ to find children wathces because inotify only fires
400 // an event for the parent directory, when a directory subtree is deleted.
401 for (base::hash_map<InotifyReader::Watch, InodeInfo>::const_iterator it =
402 watches_.begin();
403 it != watches_.end();
404 ++it) {
405 if (it->second.second.value().compare(0, path.value().length(),
406 path.value()))
407 continue;
408 delete_us.insert(it->first);
409 Singleton<InotifyReader>::get()->RemoveWatch(it->first, this);
410 }
411 for (WatchSet::const_iterator it = delete_us.begin();
412 it != delete_us.end(); ++it) {
413 watches_.erase(*it);
414 }
384 } 415 }
385 } 416 }
386 417
387 bool DirectoryWatcherImpl::IsInodeWatched(ino_t inode) const { 418 bool DirectoryWatcherImpl::IsInodeWatched(ino_t inode) const {
388 return inodes_watched_.find(inode) != inodes_watched_.end(); 419 for (base::hash_map<InotifyReader::Watch, InodeInfo>::const_iterator it =
420 watches_.begin();
421 it != watches_.end();
422 ++it) {
423 if (it->second.first == inode)
424 return true;
425 }
426 return false;
389 } 427 }
390 428
391 bool DirectoryWatcherImpl::OnEnumeratedSubtree(const FilePathSet& subtree) { 429 bool DirectoryWatcherImpl::OnEnumeratedSubtree(const FilePathSet& subtree) {
392 DCHECK(recursive_); 430 DCHECK(recursive_);
393 431
394 if (watch_ == InotifyReader::kInvalidWatch) { 432 if (watch_ == InotifyReader::kInvalidWatch) {
395 recursive_setup_finished_.Signal(); 433 recursive_setup_finished_.Signal();
396 return false; 434 return false;
397 } 435 }
398
399 bool success = true; 436 bool success = true;
400 AutoLock auto_lock(lock_); 437 for (FilePathSet::const_iterator subdirectory = subtree.begin();
401 for (FilePathSet::iterator subdirectory = subtree.begin();
402 subdirectory != subtree.end(); 438 subdirectory != subtree.end();
403 ++subdirectory) { 439 ++subdirectory) {
404 ino_t inode; 440 ino_t inode;
405 if (!file_util::GetInode(*subdirectory, &inode)) { 441 if (!file_util::GetInode(*subdirectory, &inode)) {
406 success = false; 442 success = false;
407 continue; 443 continue;
408 } 444 }
409 if (IsInodeWatched(inode)) 445 if (IsInodeWatched(inode))
410 continue; 446 continue;
411 InotifyReader::Watch watch = 447 InotifyReader::Watch watch =
412 Singleton<InotifyReader>::get()->AddWatch(*subdirectory, this); 448 Singleton<InotifyReader>::get()->AddWatch(*subdirectory, this);
413 if (watch != InotifyReader::kInvalidWatch) { 449 if (watch != InotifyReader::kInvalidWatch) {
414 watches_.insert(watch); 450 {
415 inodes_watched_.insert(inode); 451 AutoLock auto_lock(lock_);
452 watches_[watch] = std::make_pair(inode, *subdirectory);
453 }
416 } 454 }
417 } 455 }
418 recursive_setup_finished_.Signal(); 456 recursive_setup_finished_.Signal();
419 return success; 457 return success;
420 } 458 }
421 459
422 bool DirectoryWatcherImpl::Watch(const FilePath& path, 460 bool DirectoryWatcherImpl::Watch(const FilePath& path,
423 DirectoryWatcher::Delegate* delegate, 461 DirectoryWatcher::Delegate* delegate,
424 MessageLoop* backend_loop, bool recursive) { 462 MessageLoop* backend_loop, bool recursive) {
425
426 // Can only watch one path. 463 // Can only watch one path.
427 DCHECK(watch_ == InotifyReader::kInvalidWatch); 464 DCHECK(watch_ == InotifyReader::kInvalidWatch);
428 465
429 ino_t inode; 466 ino_t inode;
430 if (!file_util::GetInode(path, &inode)) 467 if (!file_util::GetInode(path, &inode))
431 return false; 468 return false;
432 469
433 InotifyReader::Watch watch = 470 InotifyReader::Watch watch =
434 Singleton<InotifyReader>::get()->AddWatch(path, this); 471 Singleton<InotifyReader>::get()->AddWatch(path, this);
435 if (watch == InotifyReader::kInvalidWatch) 472 if (watch == InotifyReader::kInvalidWatch)
436 return false; 473 return false;
437 474
438 delegate_ = delegate; 475 delegate_ = delegate;
439 recursive_ = recursive; 476 recursive_ = recursive;
440 root_path_ = path; 477 root_path_ = path;
441 watch_ = watch; 478 watch_ = watch;
442 loop_ = MessageLoop::current(); 479 loop_ = MessageLoop::current();
443 480
444 { 481 {
445 AutoLock auto_lock(lock_); 482 AutoLock auto_lock(lock_);
446 inodes_watched_.insert(inode); 483 watches_[watch_] = std::make_pair(inode, root_path_);
447 watches_.insert(watch_);
448 } 484 }
449 485
450 if (recursive_) { 486 if (recursive_) {
451 Task* subtree_task = new RegisterSubtreeWatchesTask(this, root_path_); 487 Task* subtree_task = new RegisterSubtreeWatchesTask(this, root_path_);
452 if (backend_loop) { 488 if (backend_loop) {
453 backend_loop->PostTask(FROM_HERE, subtree_task); 489 backend_loop->PostTask(FROM_HERE, subtree_task);
454 } else { 490 } else {
455 subtree_task->Run(); 491 subtree_task->Run();
456 delete subtree_task; 492 delete subtree_task;
457 } 493 }
458 } 494 }
459 495
460 return true; 496 return true;
461 } 497 }
462 498
463 } // namespace 499 } // namespace
464 500
465 DirectoryWatcher::DirectoryWatcher() { 501 DirectoryWatcher::DirectoryWatcher() {
466 impl_ = new DirectoryWatcherImpl(); 502 impl_ = new DirectoryWatcherImpl();
467 } 503 }
468 504
OLDNEW
« no previous file with comments | « no previous file | base/directory_watcher_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698