• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // This module provides a way to monitor a file or directory for changes.
6 
7 #ifndef BASE_FILES_FILE_PATH_WATCHER_H_
8 #define BASE_FILES_FILE_PATH_WATCHER_H_
9 
10 #include <memory>
11 #include <utility>
12 
13 #include "base/base_export.h"
14 #include "base/functional/callback_forward.h"
15 #include "base/memory/scoped_refptr.h"
16 #include "base/sequence_checker.h"
17 #include "base/task/sequenced_task_runner.h"
18 #include "build/build_config.h"
19 
20 namespace base {
21 
22 class FilePath;
23 
24 // This class lets you register interest in changes on a FilePath.
25 // The callback will get called whenever the file or directory referenced by the
26 // FilePath is changed, including created or deleted. Due to limitations in the
27 // underlying OS APIs, FilePathWatcher has slightly different semantics on OS X
28 // than on Windows or Linux. FilePathWatcher on Linux and Windows will detect
29 // modifications to files in a watched directory. FilePathWatcher on Mac will
30 // detect the creation and deletion of files in a watched directory, but will
31 // not detect modifications to those files. See file_path_watcher_kqueue.cc for
32 // details.
33 //
34 // Must be destroyed on the sequence that invokes Watch().
35 class BASE_EXPORT FilePathWatcher {
36  public:
37   enum class Type {
38     // Indicates that the watcher should watch the given path and its
39     // ancestors for changes. If the path does not exist, its ancestors will
40     // be watched in anticipation of it appearing later. If the path names a
41     // directory, changes within the directory are not watched.
42     kNonRecursive,
43 
44     // Indicates that the watcher should watch the given path, its ancestors,
45     // and its descendants for changes. If the path names a directory, changes
46     // within the directory are watched.
47     kRecursive,
48 
49 #if BUILDFLAG(IS_APPLE)
50     // Indicates that the watcher should watch the given path only (neither
51     // ancestors nor descendants). The watch fails if the path does not exist.
52     kTrivial,
53 #endif  // BUILDFLAG(IS_APPLE)
54   };
55 
56   // Flags are a generalization of |Type|. They are used in the new
57   // PlatformDelegate::WatchWithFlags.
58   struct WatchOptions {
59     Type type = Type::kNonRecursive;
60 
61 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) || \
62     BUILDFLAG(IS_FUCHSIA)
63     // The callback will return the full path to a changed file instead of
64     // the watched path supplied as |path| when Watch is called.
65     // So the full path can be different from the watched path when a folder is
66     // watched. In case of any error, it behaves as the original Watch.
67     bool report_modified_path = false;
68 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||
69         // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA)
70   };
71 
72   // Callback type for Watch(). |path| points to the file that was updated,
73   // and |error| is true if the platform specific code detected an error. In
74   // that case, the callback won't be invoked again.
75   using Callback =
76       base::RepeatingCallback<void(const FilePath& path, bool error)>;
77 
78   // Used internally to encapsulate different members on different platforms.
79   class PlatformDelegate {
80    public:
81     using Type = FilePathWatcher::Type;
82     using WatchOptions = FilePathWatcher::WatchOptions;
83 
84     PlatformDelegate();
85     PlatformDelegate(const PlatformDelegate&) = delete;
86     PlatformDelegate& operator=(const PlatformDelegate&) = delete;
87     virtual ~PlatformDelegate();
88 
89     // Start watching for the given |path| and notify |delegate| about changes.
90     [[nodiscard]] virtual bool Watch(const FilePath& path,
91                                      Type type,
92                                      const Callback& callback) = 0;
93 
94     // A new, more general API. It can deal with multiple options.
95     [[nodiscard]] virtual bool WatchWithOptions(const FilePath& path,
96                                                 const WatchOptions& options,
97                                                 const Callback& callback);
98 
99     // Stop watching. This is called from FilePathWatcher's dtor in order to
100     // allow to shut down properly while the object is still alive.
101     virtual void Cancel() = 0;
102 
103    protected:
104     friend class FilePathWatcher;
105 
task_runner()106     scoped_refptr<SequencedTaskRunner> task_runner() const {
107       return task_runner_;
108     }
109 
set_task_runner(scoped_refptr<SequencedTaskRunner> runner)110     void set_task_runner(scoped_refptr<SequencedTaskRunner> runner) {
111       task_runner_ = std::move(runner);
112     }
113 
114     // Must be called before the PlatformDelegate is deleted.
set_cancelled()115     void set_cancelled() { cancelled_ = true; }
116 
is_cancelled()117     bool is_cancelled() const { return cancelled_; }
118 
119    private:
120     scoped_refptr<SequencedTaskRunner> task_runner_;
121     bool cancelled_ = false;
122   };
123 
124   FilePathWatcher();
125   FilePathWatcher(const FilePathWatcher&) = delete;
126   FilePathWatcher& operator=(const FilePathWatcher&) = delete;
127   ~FilePathWatcher();
128 
129   // Returns true if the platform and OS version support recursive watches.
130   static bool RecursiveWatchAvailable();
131 
132 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
133   // Whether there are outstanding inotify watches.
134   static bool HasWatchesForTest();
135 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
136 
137   // Starts watching |path| (and its descendants if |type| is kRecursive) for
138   // changes. |callback| will be run on the caller's sequence to report such
139   // changes. Returns true if the watch was started successfully and |callback|
140   // may one day be run, or false in case of failure (e.g., a recursive watch on
141   // platforms that do not support such).
142   //
143   // On POSIX, this must be called from a thread that supports
144   // FileDescriptorWatcher.
145   bool Watch(const FilePath& path, Type type, const Callback& callback);
146 
147   // A new, more general API. It can deal with multiple options.
148   bool WatchWithOptions(const FilePath& path,
149                         const WatchOptions& options,
150                         const Callback& callback);
151 
152  private:
153   std::unique_ptr<PlatformDelegate> impl_;
154 
155   SEQUENCE_CHECKER(sequence_checker_);
156 };
157 
158 }  // namespace base
159 
160 #endif  // BASE_FILES_FILE_PATH_WATCHER_H_
161