Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / base / files / file_path_watcher_linux.cc
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
3 // found in the LICENSE file.
4
5 #include "base/files/file_path_watcher.h"
6
7 #include <errno.h>
8 #include <string.h>
9 #include <sys/inotify.h>
10 #include <sys/ioctl.h>
11 #include <sys/select.h>
12 #include <unistd.h>
13
14 #include <algorithm>
15 #include <set>
16 #include <utility>
17 #include <vector>
18
19 #include "base/bind.h"
20 #include "base/containers/hash_tables.h"
21 #include "base/debug/trace_event.h"
22 #include "base/file_util.h"
23 #include "base/files/file_path.h"
24 #include "base/lazy_instance.h"
25 #include "base/location.h"
26 #include "base/logging.h"
27 #include "base/memory/scoped_ptr.h"
28 #include "base/message_loop/message_loop.h"
29 #include "base/message_loop/message_loop_proxy.h"
30 #include "base/posix/eintr_wrapper.h"
31 #include "base/synchronization/lock.h"
32 #include "base/threading/thread.h"
33
34 namespace base {
35
36 namespace {
37
38 class FilePathWatcherImpl;
39
40 // Singleton to manage all inotify watches.
41 // TODO(tony): It would be nice if this wasn't a singleton.
42 // http://crbug.com/38174
43 class InotifyReader {
44  public:
45   typedef int Watch;  // Watch descriptor used by AddWatch and RemoveWatch.
46   static const Watch kInvalidWatch = -1;
47
48   // Watch directory |path| for changes. |watcher| will be notified on each
49   // change. Returns kInvalidWatch on failure.
50   Watch AddWatch(const FilePath& path, FilePathWatcherImpl* watcher);
51
52   // Remove |watch| if it's valid.
53   void RemoveWatch(Watch watch, FilePathWatcherImpl* watcher);
54
55   // Callback for InotifyReaderTask.
56   void OnInotifyEvent(const inotify_event* event);
57
58  private:
59   friend struct DefaultLazyInstanceTraits<InotifyReader>;
60
61   typedef std::set<FilePathWatcherImpl*> WatcherSet;
62
63   InotifyReader();
64   ~InotifyReader();
65
66   // We keep track of which delegates want to be notified on which watches.
67   hash_map<Watch, WatcherSet> watchers_;
68
69   // Lock to protect watchers_.
70   Lock lock_;
71
72   // Separate thread on which we run blocking read for inotify events.
73   Thread thread_;
74
75   // File descriptor returned by inotify_init.
76   const int inotify_fd_;
77
78   // Use self-pipe trick to unblock select during shutdown.
79   int shutdown_pipe_[2];
80
81   // Flag set to true when startup was successful.
82   bool valid_;
83
84   DISALLOW_COPY_AND_ASSIGN(InotifyReader);
85 };
86
87 class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate,
88                             public MessageLoop::DestructionObserver {
89  public:
90   FilePathWatcherImpl();
91
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
94   // the currently watched path for |fired_watch|. The flag |created| is true if
95   // the object appears.
96   void OnFilePathChanged(InotifyReader::Watch fired_watch,
97                          const FilePath::StringType& child,
98                          bool created);
99
100  protected:
101   virtual ~FilePathWatcherImpl() {}
102
103  private:
104   // Start watching |path| for changes and notify |delegate| on each change.
105   // Returns true if watch for |path| has been added successfully.
106   virtual bool Watch(const FilePath& path,
107                      bool recursive,
108                      const FilePathWatcher::Callback& callback) OVERRIDE;
109
110   // Cancel the watch. This unregisters the instance with InotifyReader.
111   virtual void Cancel() OVERRIDE;
112
113   // Cleans up and stops observing the message_loop() thread.
114   virtual void CancelOnMessageLoopThread() OVERRIDE;
115
116   // Deletion of the FilePathWatcher will call Cancel() to dispose of this
117   // object in the right thread. This also observes destruction of the required
118   // cleanup thread, in case it quits before Cancel() is called.
119   virtual void WillDestroyCurrentMessageLoop() OVERRIDE;
120
121   // Inotify watches are installed for all directory components of |target_|. A
122   // WatchEntry instance holds the watch descriptor for a component and the
123   // subdirectory for that identifies the next component. If a symbolic link
124   // is being watched, the target of the link is also kept.
125   struct WatchEntry {
126     explicit WatchEntry(const FilePath::StringType& dirname)
127         : watch(InotifyReader::kInvalidWatch),
128           subdir(dirname) {}
129
130     InotifyReader::Watch watch;
131     FilePath::StringType subdir;
132     FilePath::StringType linkname;
133   };
134   typedef std::vector<WatchEntry> WatchVector;
135
136   // Reconfigure to watch for the most specific parent directory of |target_|
137   // that exists. Updates |watched_path_|. Returns true on success.
138   bool UpdateWatches() WARN_UNUSED_RESULT;
139
140   // |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
147   // Callback to notify upon changes.
148   FilePathWatcher::Callback callback_;
149
150   // The file or directory we're supposed to watch.
151   FilePath target_;
152
153   // The vector of watches and next component names for all path components,
154   // starting at the root directory. The last entry corresponds to the watch for
155   // |target_| and always stores an empty next component name in |subdir|.
156   WatchVector watches_;
157
158   DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl);
159 };
160
161 void InotifyReaderCallback(InotifyReader* reader, int inotify_fd,
162                            int shutdown_fd) {
163   // Make sure the file descriptors are good for use with select().
164   CHECK_LE(0, inotify_fd);
165   CHECK_GT(FD_SETSIZE, inotify_fd);
166   CHECK_LE(0, shutdown_fd);
167   CHECK_GT(FD_SETSIZE, shutdown_fd);
168
169   debug::TraceLog::GetInstance()->SetCurrentThreadBlocksMessageLoop();
170
171   while (true) {
172     fd_set rfds;
173     FD_ZERO(&rfds);
174     FD_SET(inotify_fd, &rfds);
175     FD_SET(shutdown_fd, &rfds);
176
177     // Wait until some inotify events are available.
178     int select_result =
179       HANDLE_EINTR(select(std::max(inotify_fd, shutdown_fd) + 1,
180                           &rfds, NULL, NULL, NULL));
181     if (select_result < 0) {
182       DPLOG(WARNING) << "select failed";
183       return;
184     }
185
186     if (FD_ISSET(shutdown_fd, &rfds))
187       return;
188
189     // Adjust buffer size to current event queue size.
190     int buffer_size;
191     int ioctl_result = HANDLE_EINTR(ioctl(inotify_fd, FIONREAD,
192                                           &buffer_size));
193
194     if (ioctl_result != 0) {
195       DPLOG(WARNING) << "ioctl failed";
196       return;
197     }
198
199     std::vector<char> buffer(buffer_size);
200
201     ssize_t bytes_read = HANDLE_EINTR(read(inotify_fd, &buffer[0],
202                                            buffer_size));
203
204     if (bytes_read < 0) {
205       DPLOG(WARNING) << "read from inotify fd failed";
206       return;
207     }
208
209     ssize_t i = 0;
210     while (i < bytes_read) {
211       inotify_event* event = reinterpret_cast<inotify_event*>(&buffer[i]);
212       size_t event_size = sizeof(inotify_event) + event->len;
213       DCHECK(i + event_size <= static_cast<size_t>(bytes_read));
214       reader->OnInotifyEvent(event);
215       i += event_size;
216     }
217   }
218 }
219
220 static LazyInstance<InotifyReader>::Leaky g_inotify_reader =
221     LAZY_INSTANCE_INITIALIZER;
222
223 InotifyReader::InotifyReader()
224     : thread_("inotify_reader"),
225       inotify_fd_(inotify_init()),
226       valid_(false) {
227   if (inotify_fd_ < 0)
228     PLOG(ERROR) << "inotify_init() failed";
229
230   shutdown_pipe_[0] = -1;
231   shutdown_pipe_[1] = -1;
232   if (inotify_fd_ >= 0 && pipe(shutdown_pipe_) == 0 && thread_.Start()) {
233     thread_.message_loop()->PostTask(
234         FROM_HERE,
235         Bind(&InotifyReaderCallback, this, inotify_fd_, shutdown_pipe_[0]));
236     valid_ = true;
237   }
238 }
239
240 InotifyReader::~InotifyReader() {
241   if (valid_) {
242     // Write to the self-pipe so that the select call in InotifyReaderTask
243     // returns.
244     ssize_t ret = HANDLE_EINTR(write(shutdown_pipe_[1], "", 1));
245     DPCHECK(ret > 0);
246     DCHECK_EQ(ret, 1);
247     thread_.Stop();
248   }
249   if (inotify_fd_ >= 0)
250     close(inotify_fd_);
251   if (shutdown_pipe_[0] >= 0)
252     close(shutdown_pipe_[0]);
253   if (shutdown_pipe_[1] >= 0)
254     close(shutdown_pipe_[1]);
255 }
256
257 InotifyReader::Watch InotifyReader::AddWatch(
258     const FilePath& path, FilePathWatcherImpl* watcher) {
259   if (!valid_)
260     return kInvalidWatch;
261
262   AutoLock auto_lock(lock_);
263
264   Watch watch = inotify_add_watch(inotify_fd_, path.value().c_str(),
265                                   IN_CREATE | IN_DELETE |
266                                   IN_CLOSE_WRITE | IN_MOVE |
267                                   IN_ONLYDIR);
268
269   if (watch == kInvalidWatch)
270     return kInvalidWatch;
271
272   watchers_[watch].insert(watcher);
273
274   return watch;
275 }
276
277 void InotifyReader::RemoveWatch(Watch watch, FilePathWatcherImpl* watcher) {
278   if (!valid_ || (watch == kInvalidWatch))
279     return;
280
281   AutoLock auto_lock(lock_);
282
283   watchers_[watch].erase(watcher);
284
285   if (watchers_[watch].empty()) {
286     watchers_.erase(watch);
287     inotify_rm_watch(inotify_fd_, watch);
288   }
289 }
290
291 void InotifyReader::OnInotifyEvent(const inotify_event* event) {
292   if (event->mask & IN_IGNORED)
293     return;
294
295   FilePath::StringType child(event->len ? event->name : FILE_PATH_LITERAL(""));
296   AutoLock auto_lock(lock_);
297
298   for (WatcherSet::iterator watcher = watchers_[event->wd].begin();
299        watcher != watchers_[event->wd].end();
300        ++watcher) {
301     (*watcher)->OnFilePathChanged(event->wd,
302                                   child,
303                                   event->mask & (IN_CREATE | IN_MOVED_TO));
304   }
305 }
306
307 FilePathWatcherImpl::FilePathWatcherImpl() {
308 }
309
310 void FilePathWatcherImpl::OnFilePathChanged(InotifyReader::Watch fired_watch,
311                                             const FilePath::StringType& child,
312                                             bool created) {
313   if (!message_loop()->BelongsToCurrentThread()) {
314     // Switch to message_loop() to access |watches_| safely.
315     message_loop()->PostTask(
316         FROM_HERE,
317         Bind(&FilePathWatcherImpl::OnFilePathChanged, this,
318              fired_watch, child, created));
319     return;
320   }
321
322   // Check to see if CancelOnMessageLoopThread() has already been called.
323   // May happen when code flow reaches here from the PostTask() above.
324   if (watches_.empty()) {
325     DCHECK(target_.empty());
326     return;
327   }
328
329   DCHECK(MessageLoopForIO::current());
330   DCHECK(HasValidWatchVector());
331
332   // Find the entry in |watches_| that corresponds to |fired_watch|.
333   for (size_t i = 0; i < watches_.size(); ++i) {
334     const WatchEntry& watch_entry = watches_[i];
335     if (fired_watch != watch_entry.watch)
336       continue;
337
338     // Check whether a path component of |target_| changed.
339     bool change_on_target_path =
340         child.empty() ||
341         (child == watch_entry.linkname) ||
342         (child == watch_entry.subdir);
343
344     // Check if the change references |target_| or a direct child of |target_|.
345     bool is_watch_for_target = watch_entry.subdir.empty();
346     bool target_changed =
347         (is_watch_for_target && (child == watch_entry.linkname)) ||
348         (is_watch_for_target && watch_entry.linkname.empty()) ||
349         (watch_entry.subdir == child && watches_[i + 1].subdir.empty());
350
351     // Update watches if a directory component of the |target_| path
352     // (dis)appears. Note that we don't add the additional restriction of
353     // checking the event mask to see if it is for a directory here as changes
354     // to symlinks on the target path will not have IN_ISDIR set in the event
355     // masks. As a result we may sometimes call UpdateWatches() unnecessarily.
356     if (change_on_target_path && !UpdateWatches()) {
357       callback_.Run(target_, true /* error */);
358       return;
359     }
360
361     // Report the following events:
362     //  - The target or a direct child of the target got changed (in case the
363     //    watched path refers to a directory).
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;
373     }
374   }
375 }
376
377 bool FilePathWatcherImpl::Watch(const FilePath& path,
378                                 bool recursive,
379                                 const FilePathWatcher::Callback& callback) {
380   DCHECK(target_.empty());
381   DCHECK(MessageLoopForIO::current());
382   if (recursive) {
383     // Recursive watch is not supported on this platform.
384     NOTIMPLEMENTED();
385     return false;
386   }
387
388   set_message_loop(MessageLoopProxy::current().get());
389   callback_ = callback;
390   target_ = path;
391   MessageLoop::current()->AddDestructionObserver(this);
392
393   std::vector<FilePath::StringType> comps;
394   target_.GetComponents(&comps);
395   DCHECK(!comps.empty());
396   for (size_t i = 1; i < comps.size(); ++i)
397     watches_.push_back(WatchEntry(comps[i]));
398   watches_.push_back(WatchEntry(FilePath::StringType()));
399   return UpdateWatches();
400 }
401
402 void FilePathWatcherImpl::Cancel() {
403   if (callback_.is_null()) {
404     // Watch was never called, or the message_loop() thread is already gone.
405     set_cancelled();
406     return;
407   }
408
409   // Switch to the message_loop() if necessary so we can access |watches_|.
410   if (!message_loop()->BelongsToCurrentThread()) {
411     message_loop()->PostTask(FROM_HERE,
412                              Bind(&FilePathWatcher::CancelWatch,
413                                   make_scoped_refptr(this)));
414   } else {
415     CancelOnMessageLoopThread();
416   }
417 }
418
419 void FilePathWatcherImpl::CancelOnMessageLoopThread() {
420   set_cancelled();
421
422   if (!callback_.is_null()) {
423     MessageLoop::current()->RemoveDestructionObserver(this);
424     callback_.Reset();
425   }
426
427   for (size_t i = 0; i < watches_.size(); ++i)
428     g_inotify_reader.Get().RemoveWatch(watches_[i].watch, this);
429   watches_.clear();
430   target_.clear();
431 }
432
433 void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() {
434   CancelOnMessageLoopThread();
435 }
436
437 bool FilePathWatcherImpl::UpdateWatches() {
438   // Ensure this runs on the message_loop() exclusively in order to avoid
439   // concurrency issues.
440   DCHECK(message_loop()->BelongsToCurrentThread());
441   DCHECK(HasValidWatchVector());
442
443   // Walk the list of watches and update them as we go.
444   FilePath path(FILE_PATH_LITERAL("/"));
445   bool path_valid = true;
446   for (size_t i = 0; i < watches_.size(); ++i) {
447     WatchEntry& watch_entry = watches_[i];
448     InotifyReader::Watch old_watch = watch_entry.watch;
449     watch_entry.watch = InotifyReader::kInvalidWatch;
450     watch_entry.linkname.clear();
451     if (path_valid) {
452       watch_entry.watch = g_inotify_reader.Get().AddWatch(path, this);
453       if (watch_entry.watch == InotifyReader::kInvalidWatch) {
454         if (IsLink(path)) {
455           path_valid = AddWatchForBrokenSymlink(path, &watch_entry);
456         } else {
457           path_valid = false;
458         }
459       }
460     }
461     if (old_watch != watch_entry.watch)
462       g_inotify_reader.Get().RemoveWatch(old_watch, this);
463     path = path.Append(watch_entry.subdir);
464   }
465
466   return true;
467 }
468
469 bool FilePathWatcherImpl::AddWatchForBrokenSymlink(const FilePath& path,
470                                                    WatchEntry* watch_entry) {
471   DCHECK_EQ(InotifyReader::kInvalidWatch, watch_entry->watch);
472   FilePath link;
473   if (!ReadSymbolicLink(path, &link))
474     return false;
475
476   if (!link.IsAbsolute())
477     link = path.DirName().Append(link);
478
479   // Try watching symlink target directory. If the link target is "/", then we
480   // shouldn't get here in normal situations and if we do, we'd watch "/" for
481   // changes to a component "/" which is harmless so no special treatment of
482   // this case is required.
483   InotifyReader::Watch watch =
484       g_inotify_reader.Get().AddWatch(link.DirName(), this);
485   if (watch == InotifyReader::kInvalidWatch) {
486     // TODO(craig) Symlinks only work if the parent directory for the target
487     // exist. Ideally we should make sure we've watched all the components of
488     // the symlink path for changes. See crbug.com/91561 for details.
489     DPLOG(WARNING) << "Watch failed for "  << link.DirName().value();
490     return false;
491   }
492   watch_entry->watch = watch;
493   watch_entry->linkname = link.BaseName().value();
494   return true;
495 }
496
497 bool FilePathWatcherImpl::HasValidWatchVector() const {
498   if (watches_.empty())
499     return false;
500   for (size_t i = 0; i < watches_.size() - 1; ++i) {
501     if (watches_[i].subdir.empty())
502       return false;
503   }
504   return watches_[watches_.size() - 1].subdir.empty();
505 }
506
507 }  // namespace
508
509 FilePathWatcher::FilePathWatcher() {
510   impl_ = new FilePathWatcherImpl();
511 }
512
513 }  // namespace base