OLD | NEW |
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/ioctl.h> | 10 #include <sys/ioctl.h> |
10 #include <sys/inotify.h> | |
11 #include <sys/select.h> | 11 #include <sys/select.h> |
12 #include <unistd.h> | 12 #include <unistd.h> |
13 | 13 |
14 #include <algorithm> | 14 #include <algorithm> |
15 #include <set> | 15 #include <set> |
16 #include <utility> | 16 #include <utility> |
17 #include <vector> | 17 #include <vector> |
18 | 18 |
19 #include "base/file_path.h" | 19 #include "base/file_path.h" |
| 20 #include "base/file_util.h" |
20 #include "base/hash_tables.h" | 21 #include "base/hash_tables.h" |
21 #include "base/lock.h" | 22 #include "base/lock.h" |
22 #include "base/logging.h" | 23 #include "base/logging.h" |
23 #include "base/message_loop.h" | 24 #include "base/message_loop.h" |
24 #include "base/scoped_ptr.h" | 25 #include "base/scoped_ptr.h" |
25 #include "base/singleton.h" | 26 #include "base/singleton.h" |
26 #include "base/task.h" | 27 #include "base/task.h" |
27 #include "base/thread.h" | 28 #include "base/thread.h" |
28 | 29 |
29 namespace { | 30 namespace { |
30 | 31 |
| 32 class DirectoryWatcherImpl; |
| 33 |
31 // Singleton to manage all inotify watches. | 34 // Singleton to manage all inotify watches. |
32 class InotifyReader { | 35 class InotifyReader { |
33 public: | 36 public: |
34 typedef int Watch; // Watch descriptor used by AddWatch and RemoveWatch. | 37 typedef int Watch; // Watch descriptor used by AddWatch and RemoveWatch. |
35 static const Watch kInvalidWatch = -1; | 38 static const Watch kInvalidWatch = -1; |
36 | 39 |
37 // Watch |path| for changes. |delegate| will be notified on each change. Does | 40 // Watch |path| for changes. |watcher| will be notified on each change. |
38 // not check for duplicates. If you call it n times with same |path| | |
39 // and |delegate|, it will receive n notifications for each change | |
40 // in |path|. It makes implementation of DirectoryWatcher simple. | |
41 // Returns kInvalidWatch on failure. | 41 // Returns kInvalidWatch on failure. |
42 Watch AddWatch(const FilePath& path, DirectoryWatcher::Delegate* delegate); | 42 Watch AddWatch(const FilePath& path, DirectoryWatcherImpl* watcher); |
43 | 43 |
44 // Remove |watch| for |delegate|. If you had n watches for same |delegate| | 44 // Remove |watch|. Returns true on success. |
45 // and path, after calling this function you will have n - 1. | 45 bool RemoveWatch(Watch watch, DirectoryWatcherImpl* watcher); |
46 // Returns true on success. | |
47 bool RemoveWatch(Watch watch, DirectoryWatcher::Delegate* delegate); | |
48 | 46 |
49 // Callback for InotifyReaderTask. | 47 // Callback for InotifyReaderTask. |
50 void OnInotifyEvent(inotify_event* event); | 48 void OnInotifyEvent(inotify_event* event); |
51 | 49 |
52 private: | 50 private: |
53 friend struct DefaultSingletonTraits<InotifyReader>; | 51 friend struct DefaultSingletonTraits<InotifyReader>; |
54 | 52 |
55 typedef std::pair<DirectoryWatcher::Delegate*, MessageLoop*> DelegateInfo; | 53 typedef std::set<DirectoryWatcherImpl*> WatcherSet; |
56 typedef std::multiset<DelegateInfo> DelegateSet; | |
57 | 54 |
58 InotifyReader(); | 55 InotifyReader(); |
59 ~InotifyReader(); | 56 ~InotifyReader(); |
60 | 57 |
61 // We keep track of which delegates want to be notified on which watches. | 58 // We keep track of which delegates want to be notified on which watches. |
62 // Multiset is used because there may be many DirectoryWatchers for same path | 59 base::hash_map<Watch, WatcherSet> watchers_; |
63 // and delegate. | |
64 base::hash_map<Watch, DelegateSet> delegates_; | |
65 | 60 |
66 // For each watch we also want to know the path it's watching. | 61 // For each watch we also want to know the path it's watching. |
67 base::hash_map<Watch, FilePath> paths_; | 62 base::hash_map<Watch, FilePath> paths_; |
68 | 63 |
69 // Lock to protect delegates_ and paths_. | 64 // Lock to protect delegates_ and paths_. |
70 Lock lock_; | 65 Lock lock_; |
71 | 66 |
72 // Separate thread on which we run blocking read for inotify events. | 67 // Separate thread on which we run blocking read for inotify events. |
73 base::Thread thread_; | 68 base::Thread thread_; |
74 | 69 |
75 // File descriptor returned by inotify_init. | 70 // File descriptor returned by inotify_init. |
76 const int inotify_fd_; | 71 const int inotify_fd_; |
77 | 72 |
78 // Use self-pipe trick to unblock select during shutdown. | 73 // Use self-pipe trick to unblock select during shutdown. |
79 int shutdown_pipe_[2]; | 74 int shutdown_pipe_[2]; |
80 | 75 |
81 // Flag set to true when startup was successful. | 76 // Flag set to true when startup was successful. |
82 bool valid_; | 77 bool valid_; |
83 | 78 |
84 DISALLOW_COPY_AND_ASSIGN(InotifyReader); | 79 DISALLOW_COPY_AND_ASSIGN(InotifyReader); |
85 }; | 80 }; |
86 | 81 |
| 82 class DirectoryWatcherImpl : public DirectoryWatcher::PlatformDelegate { |
| 83 public: |
| 84 |
| 85 typedef std::set<FilePath> FilePathSet; |
| 86 |
| 87 DirectoryWatcherImpl(); |
| 88 ~DirectoryWatcherImpl(); |
| 89 |
| 90 // Called for each event coming from one of watches. |
| 91 void HandleInotifyEvent(inotify_event* event); |
| 92 |
| 93 // Callback for DirectoryWatcherImplEnumeratePathTask. |
| 94 void OnEnumeratedPath(const std::set<FilePath>& subtree); |
| 95 |
| 96 // Start watching |path| for changes and notify |delegate| on each change. |
| 97 // Watch entire subtree, when |recursive| is true. |
| 98 // Returns true if watch for |path| has been added successfuly. Watches |
| 99 // required for |recursive| are added on a background thread and have no |
| 100 // effect on the return value. |
| 101 virtual bool Watch(const FilePath& path, DirectoryWatcher::Delegate* delegate, |
| 102 bool recursive); |
| 103 |
| 104 private: |
| 105 typedef std::set<InotifyReader::Watch> WatchSet; |
| 106 |
| 107 // Returns true if |inode| is watched by DirectoryWatcherImpl. |
| 108 bool isPathWatched(ino_t inode) const; |
| 109 |
| 110 // Return true if watching subtree is success. |
| 111 bool watchSubtree(const FilePathSet& subtree); |
| 112 |
| 113 // Lock to protect attributes. |
| 114 Lock lock_; |
| 115 |
| 116 // Delegate to notify upon changes. |
| 117 DirectoryWatcher::Delegate* delegate_; |
| 118 |
| 119 // Path we're watching (passed to delegate). |
| 120 FilePath root_path_; |
| 121 |
| 122 // Watch returned by InotifyReader. |
| 123 InotifyReader::Watch root_watch_; |
| 124 |
| 125 typedef std::set<ino_t> InodeSet; |
| 126 |
| 127 // Hold inode for paths that are watched. |
| 128 InodeSet path_watched_; |
| 129 |
| 130 // We need to know who is watching what inode, |
| 131 // for cleaning up and handling events. |
| 132 base::hash_map<ino_t, DirectoryWatcherImpl*> path_watcher_; |
| 133 |
| 134 // Flag set to true when recursively watching subtree. |
| 135 bool recursive_; |
| 136 |
| 137 // Flag set to true when thread startup was successful. |
| 138 bool path_enumerator_thread_started_; |
| 139 |
| 140 // Seperate thread on which we enumerate directory subtree. |
| 141 base::Thread path_enumerator_thread_; |
| 142 |
| 143 MessageLoop* loop_; |
| 144 |
| 145 DISALLOW_COPY_AND_ASSIGN(DirectoryWatcherImpl); |
| 146 }; |
| 147 |
| 148 class DirectoryWatcherImplEnumeratePathTask : public Task { |
| 149 public: |
| 150 DirectoryWatcherImplEnumeratePathTask(DirectoryWatcherImpl* watcher, |
| 151 const FilePath& path) |
| 152 :watcher_(watcher), path_(path) {} |
| 153 |
| 154 virtual void Run() { |
| 155 file_util::FileEnumerator dir_list(path_, true, |
| 156 file_util::FileEnumerator::DIRECTORIES); |
| 157 |
| 158 DirectoryWatcherImpl::FilePathSet subtree; |
| 159 for (FilePath subdirectory = dir_list.Next(); |
| 160 !subdirectory.empty(); |
| 161 subdirectory = dir_list.Next()) { |
| 162 subtree.insert(subdirectory); |
| 163 } |
| 164 watcher_->OnEnumeratedPath(subtree); |
| 165 } |
| 166 |
| 167 private: |
| 168 DirectoryWatcherImpl* watcher_; |
| 169 FilePath path_; |
| 170 |
| 171 DISALLOW_COPY_AND_ASSIGN(DirectoryWatcherImplEnumeratePathTask); |
| 172 }; |
| 173 |
| 174 class DirectoryWatcherImplNotifyTask : public Task { |
| 175 public: |
| 176 DirectoryWatcherImplNotifyTask(DirectoryWatcher::Delegate* delegate, |
| 177 const FilePath& path) |
| 178 :delegate_(delegate), path_(path) {} |
| 179 |
| 180 virtual void Run() { |
| 181 delegate_->OnDirectoryChanged(path_); |
| 182 } |
| 183 |
| 184 private: |
| 185 DirectoryWatcher::Delegate* delegate_; |
| 186 FilePath path_; |
| 187 |
| 188 DISALLOW_COPY_AND_ASSIGN(DirectoryWatcherImplNotifyTask); |
| 189 }; |
| 190 |
87 class InotifyReaderTask : public Task { | 191 class InotifyReaderTask : public Task { |
88 public: | 192 public: |
89 InotifyReaderTask(InotifyReader* reader, int inotify_fd, int shutdown_fd) | 193 InotifyReaderTask(InotifyReader* reader, int inotify_fd, int shutdown_fd) |
90 : reader_(reader), | 194 : reader_(reader), |
91 inotify_fd_(inotify_fd), | 195 inotify_fd_(inotify_fd), |
92 shutdown_fd_(shutdown_fd) { | 196 shutdown_fd_(shutdown_fd) { |
93 } | 197 } |
94 | 198 |
95 virtual void Run() { | 199 virtual void Run() { |
96 while (true) { | 200 while (true) { |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
145 } | 249 } |
146 | 250 |
147 private: | 251 private: |
148 InotifyReader* reader_; | 252 InotifyReader* reader_; |
149 int inotify_fd_; | 253 int inotify_fd_; |
150 int shutdown_fd_; | 254 int shutdown_fd_; |
151 | 255 |
152 DISALLOW_COPY_AND_ASSIGN(InotifyReaderTask); | 256 DISALLOW_COPY_AND_ASSIGN(InotifyReaderTask); |
153 }; | 257 }; |
154 | 258 |
155 class InotifyReaderNotifyTask : public Task { | |
156 public: | |
157 InotifyReaderNotifyTask(DirectoryWatcher::Delegate* delegate, | |
158 const FilePath& path) | |
159 : delegate_(delegate), | |
160 path_(path) { | |
161 } | |
162 | |
163 virtual void Run() { | |
164 delegate_->OnDirectoryChanged(path_); | |
165 } | |
166 | |
167 private: | |
168 DirectoryWatcher::Delegate* delegate_; | |
169 FilePath path_; | |
170 | |
171 DISALLOW_COPY_AND_ASSIGN(InotifyReaderNotifyTask); | |
172 }; | |
173 | |
174 InotifyReader::InotifyReader() | 259 InotifyReader::InotifyReader() |
175 : thread_("inotify_reader"), | 260 : thread_("inotify_reader"), |
176 inotify_fd_(inotify_init()), | 261 inotify_fd_(inotify_init()), |
177 valid_(false) { | 262 valid_(false) { |
178 shutdown_pipe_[0] = -1; | 263 shutdown_pipe_[0] = -1; |
179 shutdown_pipe_[1] = -1; | 264 shutdown_pipe_[1] = -1; |
180 if (inotify_fd_ >= 0 && pipe(shutdown_pipe_) == 0 && thread_.Start()) { | 265 if (inotify_fd_ >= 0 && pipe(shutdown_pipe_) == 0 && thread_.Start()) { |
181 thread_.message_loop()->PostTask( | 266 thread_.message_loop()->PostTask( |
182 FROM_HERE, new InotifyReaderTask(this, inotify_fd_, shutdown_pipe_[0])); | 267 FROM_HERE, new InotifyReaderTask(this, inotify_fd_, shutdown_pipe_[0])); |
183 valid_ = true; | 268 valid_ = true; |
(...skipping 14 matching lines...) Expand all Loading... |
198 } | 283 } |
199 if (inotify_fd_ >= 0) | 284 if (inotify_fd_ >= 0) |
200 close(inotify_fd_); | 285 close(inotify_fd_); |
201 if (shutdown_pipe_[0] >= 0) | 286 if (shutdown_pipe_[0] >= 0) |
202 close(shutdown_pipe_[0]); | 287 close(shutdown_pipe_[0]); |
203 if (shutdown_pipe_[1] >= 0) | 288 if (shutdown_pipe_[1] >= 0) |
204 close(shutdown_pipe_[1]); | 289 close(shutdown_pipe_[1]); |
205 } | 290 } |
206 | 291 |
207 InotifyReader::Watch InotifyReader::AddWatch( | 292 InotifyReader::Watch InotifyReader::AddWatch( |
208 const FilePath& path, DirectoryWatcher::Delegate* delegate) { | 293 const FilePath& path, DirectoryWatcherImpl* watcher) { |
| 294 |
209 if (!valid_) | 295 if (!valid_) |
210 return kInvalidWatch; | 296 return kInvalidWatch; |
211 | 297 |
212 AutoLock auto_lock(lock_); | 298 AutoLock auto_lock(lock_); |
213 | 299 |
214 Watch watch = inotify_add_watch(inotify_fd_, path.value().c_str(), | 300 Watch watch = inotify_add_watch(inotify_fd_, path.value().c_str(), |
215 IN_CREATE | IN_DELETE | | 301 IN_CREATE | IN_DELETE | |
216 IN_CLOSE_WRITE | IN_MOVE); | 302 IN_CLOSE_WRITE | IN_MOVE); |
| 303 |
217 if (watch == kInvalidWatch) | 304 if (watch == kInvalidWatch) |
218 return kInvalidWatch; | 305 return kInvalidWatch; |
219 | 306 |
220 if (paths_[watch].empty()) | 307 if (paths_[watch].empty()) |
221 paths_[watch] = path; // We don't yet watch this path. | 308 paths_[watch] = path; // We don't yet watch this path. |
222 | 309 |
223 delegates_[watch].insert(std::make_pair(delegate, MessageLoop::current())); | 310 watchers_[watch].insert(watcher); |
224 | 311 |
225 return watch; | 312 return watch; |
226 } | 313 } |
227 | 314 |
228 bool InotifyReader::RemoveWatch(Watch watch, | 315 bool InotifyReader::RemoveWatch(Watch watch, |
229 DirectoryWatcher::Delegate* delegate) { | 316 DirectoryWatcherImpl* watcher) { |
230 if (!valid_) | 317 if (!valid_) |
231 return false; | 318 return false; |
232 | 319 |
233 AutoLock auto_lock(lock_); | 320 AutoLock auto_lock(lock_); |
234 | 321 |
235 if (paths_[watch].empty()) | 322 if (paths_[watch].empty()) |
236 return false; // We don't recognize this watch. | 323 return false; // We don't recognize this watch. |
237 | 324 |
238 // Only erase one occurrence of delegate (there may be more). | 325 watchers_[watch].erase(watcher); |
239 delegates_[watch].erase( | |
240 delegates_[watch].find(std::make_pair(delegate, MessageLoop::current()))); | |
241 | 326 |
242 if (delegates_[watch].empty()) { | 327 if (watchers_[watch].empty()) { |
243 paths_.erase(watch); | 328 paths_.erase(watch); |
244 delegates_.erase(watch); | 329 watchers_.erase(watch); |
245 | |
246 return (inotify_rm_watch(inotify_fd_, watch) == 0); | 330 return (inotify_rm_watch(inotify_fd_, watch) == 0); |
247 } | 331 } |
248 | 332 |
249 return true; | 333 return true; |
250 } | 334 } |
251 | 335 |
252 void InotifyReader::OnInotifyEvent(inotify_event* event) { | 336 void InotifyReader::OnInotifyEvent(inotify_event* event) { |
253 if (event->mask & IN_IGNORED) | 337 if (event->mask & IN_IGNORED) |
254 return; | 338 return; |
255 | 339 |
256 DelegateSet delegates_to_notify; | 340 WatcherSet watchers_to_notify; |
257 FilePath changed_path; | 341 FilePath changed_path; |
258 | 342 |
259 { | 343 { |
260 AutoLock auto_lock(lock_); | 344 AutoLock auto_lock(lock_); |
261 changed_path = paths_[event->wd]; | 345 changed_path = paths_[event->wd]; |
262 delegates_to_notify.insert(delegates_[event->wd].begin(), | 346 watchers_to_notify.insert(watchers_[event->wd].begin(), |
263 delegates_[event->wd].end()); | 347 watchers_[event->wd].end()); |
264 } | 348 } |
265 | 349 |
266 DelegateSet::iterator i; | 350 for (WatcherSet::iterator watcher = watchers_to_notify.begin(); |
267 for (i = delegates_to_notify.begin(); i != delegates_to_notify.end(); ++i) { | 351 watcher != watchers_to_notify.end(); |
268 DirectoryWatcher::Delegate* delegate = i->first; | 352 ++watcher) { |
269 MessageLoop* loop = i->second; | 353 (*watcher)->HandleInotifyEvent(event); |
270 loop->PostTask(FROM_HERE, | |
271 new InotifyReaderNotifyTask(delegate, changed_path)); | |
272 } | 354 } |
273 } | 355 } |
274 | 356 |
275 class DirectoryWatcherImpl : public DirectoryWatcher::PlatformDelegate { | 357 DirectoryWatcherImpl::DirectoryWatcherImpl() |
276 public: | 358 : root_watch_(InotifyReader::kInvalidWatch), |
277 DirectoryWatcherImpl() : watch_(InotifyReader::kInvalidWatch) {} | 359 path_enumerator_thread_started_(false), |
278 ~DirectoryWatcherImpl(); | 360 path_enumerator_thread_("directory_enumerator"), |
279 | 361 loop_(MessageLoop::current()) {} |
280 virtual bool Watch(const FilePath& path, DirectoryWatcher::Delegate* delegate, | |
281 bool recursive); | |
282 | |
283 private: | |
284 // Delegate to notify upon changes. | |
285 DirectoryWatcher::Delegate* delegate_; | |
286 | |
287 // Path we're watching (passed to delegate). | |
288 FilePath path_; | |
289 | |
290 // Watch returned by InotifyReader. | |
291 InotifyReader::Watch watch_; | |
292 | |
293 DISALLOW_COPY_AND_ASSIGN(DirectoryWatcherImpl); | |
294 }; | |
295 | 362 |
296 DirectoryWatcherImpl::~DirectoryWatcherImpl() { | 363 DirectoryWatcherImpl::~DirectoryWatcherImpl() { |
297 if (watch_ != InotifyReader::kInvalidWatch) | 364 if (root_watch_ == InotifyReader::kInvalidWatch) |
298 Singleton<InotifyReader>::get()->RemoveWatch(watch_, delegate_); | 365 return; |
| 366 for (InodeSet::iterator i = path_watched_.begin(); |
| 367 i != path_watched_.end(); |
| 368 ++i) { |
| 369 DirectoryWatcherImpl* delete_me = path_watcher_[*i]; |
| 370 if (Singleton<InotifyReader>::get()->RemoveWatch(delete_me->root_watch_, |
| 371 delete_me)) { |
| 372 delete_me->root_watch_ = InotifyReader::kInvalidWatch; |
| 373 } |
| 374 } |
| 375 path_watcher_.clear(); |
| 376 path_watched_.clear(); |
| 377 if (path_enumerator_thread_started_) |
| 378 path_enumerator_thread_.Stop(); |
| 379 } |
| 380 |
| 381 void DirectoryWatcherImpl::HandleInotifyEvent(inotify_event* event) { |
| 382 loop_->PostTask(FROM_HERE, |
| 383 new DirectoryWatcherImplNotifyTask(delegate_, root_path_)); |
| 384 |
| 385 if (!(event->mask & IN_ISDIR)) |
| 386 return; |
| 387 |
| 388 if (event->mask & IN_CREATE || event->mask & IN_MOVED_TO) { |
| 389 NOTIMPLEMENTED(); |
| 390 } else if (event->mask & IN_DELETE || event->mask & IN_MOVED_FROM) { |
| 391 NOTIMPLEMENTED(); |
| 392 } |
| 393 } |
| 394 |
| 395 bool DirectoryWatcherImpl::isPathWatched(ino_t inode) const { |
| 396 return path_watched_.find(inode) != path_watched_.end(); |
| 397 } |
| 398 |
| 399 void DirectoryWatcherImpl::OnEnumeratedPath(const FilePathSet& subtree) { |
| 400 DCHECK(recursive_); |
| 401 watchSubtree(subtree); |
299 } | 402 } |
300 | 403 |
301 bool DirectoryWatcherImpl::Watch(const FilePath& path, | 404 bool DirectoryWatcherImpl::Watch(const FilePath& path, |
302 DirectoryWatcher::Delegate* delegate, bool recursive) { | 405 DirectoryWatcher::Delegate* delegate, bool recursive) { |
303 DCHECK(watch_ == InotifyReader::kInvalidWatch); // Can only watch one path. | |
304 | 406 |
305 if (recursive) { | 407 // Can only watch one path. |
306 // TODO(phajdan.jr): Support recursive watches. | 408 DCHECK(root_watch_ == InotifyReader::kInvalidWatch); |
307 // Unfortunately inotify has no "native" support for them, but it can be | 409 |
308 // emulated by walking the directory tree and setting up watches for each | 410 ino_t inode; |
309 // directory. Of course this is ineffective for large directory trees. | 411 if (!file_util::GetInode(path, &inode)) |
310 // For the algorithm, see the link below: | |
311 // http://osdir.com/ml/gnome.dashboard.devel/2004-10/msg00022.html | |
312 NOTIMPLEMENTED(); | |
313 return false; | 412 return false; |
| 413 |
| 414 InotifyReader::Watch watch = |
| 415 Singleton<InotifyReader>::get()->AddWatch(path, this); |
| 416 if (watch == InotifyReader::kInvalidWatch) |
| 417 return false; |
| 418 |
| 419 delegate_ = delegate; |
| 420 recursive_ = recursive; |
| 421 root_path_ = path; |
| 422 root_watch_ = watch; |
| 423 |
| 424 { |
| 425 AutoLock auto_lock(lock_); |
| 426 path_watched_.insert(inode); |
| 427 path_watcher_[inode] = this; |
314 } | 428 } |
315 | 429 |
316 delegate_ = delegate; | 430 if (recursive_) { |
317 path_ = path; | 431 path_enumerator_thread_started_ = path_enumerator_thread_.Start(); |
318 watch_ = Singleton<InotifyReader>::get()->AddWatch(path, delegate_); | 432 if (path_enumerator_thread_started_) { |
| 433 path_enumerator_thread_.message_loop()->PostTask( |
| 434 FROM_HERE, |
| 435 new DirectoryWatcherImplEnumeratePathTask(this, root_path_)); |
| 436 } |
| 437 } |
319 | 438 |
320 return watch_ != InotifyReader::kInvalidWatch; | 439 return true; |
| 440 } |
| 441 |
| 442 bool DirectoryWatcherImpl::watchSubtree(const FilePathSet& subtree) { |
| 443 DCHECK(recursive_); |
| 444 |
| 445 if (root_watch_ == InotifyReader::kInvalidWatch) |
| 446 return false; |
| 447 |
| 448 bool success = true; |
| 449 AutoLock auto_lock(lock_); |
| 450 for (FilePathSet::iterator subdirectory = subtree.begin(); |
| 451 subdirectory != subtree.end(); |
| 452 ++subdirectory) { |
| 453 ino_t inode; |
| 454 if (!file_util::GetInode(*subdirectory, &inode)) { |
| 455 success = false; |
| 456 continue; |
| 457 } |
| 458 if (isPathWatched(inode)) |
| 459 continue; |
| 460 DirectoryWatcherImpl* child = new DirectoryWatcherImpl(); |
| 461 child->loop_ = loop_; |
| 462 if (!child->Watch(*subdirectory, delegate_, false)) { |
| 463 scoped_refptr<DirectoryWatcherImpl> delete_me = child; |
| 464 } else { |
| 465 path_watcher_[inode] = child; |
| 466 path_watched_.insert(inode); |
| 467 } |
| 468 } |
| 469 return success; |
321 } | 470 } |
322 | 471 |
323 } // namespace | 472 } // namespace |
324 | 473 |
325 DirectoryWatcher::DirectoryWatcher() { | 474 DirectoryWatcher::DirectoryWatcher() { |
326 impl_ = new DirectoryWatcherImpl(); | 475 impl_ = new DirectoryWatcherImpl(); |
327 } | 476 } |
OLD | NEW |