[email protected] | e7b3a61 | 2012-01-05 02:18:18 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
[email protected] | 493c800 | 2011-04-14 16:56:01 | [diff] [blame] | 5 | #include "base/files/file_path_watcher.h" |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 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> |
[email protected] | f8d025c | 2014-05-13 19:31:30 | [diff] [blame] | 15 | #include <map> |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 16 | #include <set> |
| 17 | #include <utility> |
| 18 | #include <vector> |
| 19 | |
[email protected] | f4412a88 | 2011-10-05 16:58:20 | [diff] [blame] | 20 | #include "base/bind.h" |
[email protected] | 14c1c23 | 2013-06-11 17:52:44 | [diff] [blame] | 21 | #include "base/containers/hash_tables.h" |
[email protected] | f8d025c | 2014-05-13 19:31:30 | [diff] [blame] | 22 | #include "base/files/file_enumerator.h" |
[email protected] | 5799981 | 2013-02-24 05:40:52 | [diff] [blame] | 23 | #include "base/files/file_path.h" |
[email protected] | e3177dd5 | 2014-08-13 20:22:14 | [diff] [blame] | 24 | #include "base/files/file_util.h" |
[email protected] | fee46a8 | 2010-12-09 16:42:15 | [diff] [blame] | 25 | #include "base/lazy_instance.h" |
[email protected] | c62dd9d | 2011-09-21 18:05:41 | [diff] [blame] | 26 | #include "base/location.h" |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 27 | #include "base/logging.h" |
[email protected] | 3b63f8f4 | 2011-03-28 01:54:15 | [diff] [blame] | 28 | #include "base/memory/scoped_ptr.h" |
[email protected] | 2025d00 | 2012-11-14 20:54:35 | [diff] [blame] | 29 | #include "base/posix/eintr_wrapper.h" |
skyostil | 054861d | 2015-04-30 19:06:15 | [diff] [blame] | 30 | #include "base/single_thread_task_runner.h" |
[email protected] | 20305ec | 2011-01-21 04:55:52 | [diff] [blame] | 31 | #include "base/synchronization/lock.h" |
skyostil | 054861d | 2015-04-30 19:06:15 | [diff] [blame] | 32 | #include "base/thread_task_runner_handle.h" |
[email protected] | 34b9963 | 2011-01-01 01:01:06 | [diff] [blame] | 33 | #include "base/threading/thread.h" |
primiano | a7b8dcb | 2015-01-31 18:21:14 | [diff] [blame] | 34 | #include "base/trace_event/trace_event.h" |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 35 | |
[email protected] | 493c800 | 2011-04-14 16:56:01 | [diff] [blame] | 36 | namespace base { |
[email protected] | 493c800 | 2011-04-14 16:56:01 | [diff] [blame] | 37 | |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 38 | namespace { |
| 39 | |
| 40 | class FilePathWatcherImpl; |
| 41 | |
| 42 | // Singleton to manage all inotify watches. |
| 43 | // TODO(tony): It would be nice if this wasn't a singleton. |
| 44 | // http://crbug.com/38174 |
| 45 | class InotifyReader { |
| 46 | public: |
| 47 | typedef int Watch; // Watch descriptor used by AddWatch and RemoveWatch. |
| 48 | static const Watch kInvalidWatch = -1; |
| 49 | |
| 50 | // Watch directory |path| for changes. |watcher| will be notified on each |
| 51 | // change. Returns kInvalidWatch on failure. |
| 52 | Watch AddWatch(const FilePath& path, FilePathWatcherImpl* watcher); |
| 53 | |
[email protected] | dae3f53 | 2014-04-26 22:26:38 | [diff] [blame] | 54 | // Remove |watch| if it's valid. |
| 55 | void RemoveWatch(Watch watch, FilePathWatcherImpl* watcher); |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 56 | |
| 57 | // Callback for InotifyReaderTask. |
| 58 | void OnInotifyEvent(const inotify_event* event); |
| 59 | |
| 60 | private: |
[email protected] | b264eab | 2013-11-27 23:22:08 | [diff] [blame] | 61 | friend struct DefaultLazyInstanceTraits<InotifyReader>; |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 62 | |
| 63 | typedef std::set<FilePathWatcherImpl*> WatcherSet; |
| 64 | |
| 65 | InotifyReader(); |
| 66 | ~InotifyReader(); |
| 67 | |
| 68 | // We keep track of which delegates want to be notified on which watches. |
[email protected] | b264eab | 2013-11-27 23:22:08 | [diff] [blame] | 69 | hash_map<Watch, WatcherSet> watchers_; |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 70 | |
| 71 | // Lock to protect watchers_. |
[email protected] | b264eab | 2013-11-27 23:22:08 | [diff] [blame] | 72 | Lock lock_; |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 73 | |
| 74 | // Separate thread on which we run blocking read for inotify events. |
[email protected] | b264eab | 2013-11-27 23:22:08 | [diff] [blame] | 75 | Thread thread_; |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 76 | |
| 77 | // File descriptor returned by inotify_init. |
| 78 | const int inotify_fd_; |
| 79 | |
| 80 | // Use self-pipe trick to unblock select during shutdown. |
| 81 | int shutdown_pipe_[2]; |
| 82 | |
| 83 | // Flag set to true when startup was successful. |
| 84 | bool valid_; |
| 85 | |
| 86 | DISALLOW_COPY_AND_ASSIGN(InotifyReader); |
| 87 | }; |
| 88 | |
[email protected] | d64abe1 | 2011-03-22 20:14:06 | [diff] [blame] | 89 | class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate, |
| 90 | public MessageLoop::DestructionObserver { |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 91 | public: |
| 92 | FilePathWatcherImpl(); |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 93 | |
| 94 | // Called for each event coming from the watch. |fired_watch| identifies the |
| 95 | // watch that fired, |child| indicates what has changed, and is relative to |
[email protected] | f8d025c | 2014-05-13 19:31:30 | [diff] [blame] | 96 | // the currently watched path for |fired_watch|. |
| 97 | // |
| 98 | // |created| is true if the object appears. |
| 99 | // |deleted| is true if the object disappears. |
| 100 | // |is_dir| is true if the object is a directory. |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 101 | void OnFilePathChanged(InotifyReader::Watch fired_watch, |
| 102 | const FilePath::StringType& child, |
[email protected] | f8d025c | 2014-05-13 19:31:30 | [diff] [blame] | 103 | bool created, |
| 104 | bool deleted, |
| 105 | bool is_dir); |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 106 | |
[email protected] | dae3f53 | 2014-04-26 22:26:38 | [diff] [blame] | 107 | protected: |
viettrungluu | b9f1b81 | 2014-10-22 01:48:30 | [diff] [blame] | 108 | ~FilePathWatcherImpl() override {} |
[email protected] | dae3f53 | 2014-04-26 22:26:38 | [diff] [blame] | 109 | |
| 110 | private: |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 111 | // Start watching |path| for changes and notify |delegate| on each change. |
| 112 | // Returns true if watch for |path| has been added successfully. |
viettrungluu | b9f1b81 | 2014-10-22 01:48:30 | [diff] [blame] | 113 | bool Watch(const FilePath& path, |
| 114 | bool recursive, |
| 115 | const FilePathWatcher::Callback& callback) override; |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 116 | |
| 117 | // Cancel the watch. This unregisters the instance with InotifyReader. |
viettrungluu | b9f1b81 | 2014-10-22 01:48:30 | [diff] [blame] | 118 | void Cancel() override; |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 119 | |
[email protected] | dae3f53 | 2014-04-26 22:26:38 | [diff] [blame] | 120 | // Cleans up and stops observing the message_loop() thread. |
viettrungluu | b9f1b81 | 2014-10-22 01:48:30 | [diff] [blame] | 121 | void CancelOnMessageLoopThread() override; |
[email protected] | dae3f53 | 2014-04-26 22:26:38 | [diff] [blame] | 122 | |
[email protected] | d64abe1 | 2011-03-22 20:14:06 | [diff] [blame] | 123 | // Deletion of the FilePathWatcher will call Cancel() to dispose of this |
| 124 | // object in the right thread. This also observes destruction of the required |
| 125 | // cleanup thread, in case it quits before Cancel() is called. |
viettrungluu | b9f1b81 | 2014-10-22 01:48:30 | [diff] [blame] | 126 | void WillDestroyCurrentMessageLoop() override; |
[email protected] | d64abe1 | 2011-03-22 20:14:06 | [diff] [blame] | 127 | |
thestig | bd265f969 | 2014-12-19 22:51:16 | [diff] [blame] | 128 | // Inotify watches are installed for all directory components of |target_|. |
| 129 | // A WatchEntry instance holds: |
| 130 | // - |watch|: the watch descriptor for a component. |
| 131 | // - |subdir|: the subdirectory that identifies the next component. |
| 132 | // - For the last component, there is no next component, so it is empty. |
| 133 | // - |linkname|: the target of the symlink. |
| 134 | // - Only if the target being watched is a symbolic link. |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 135 | struct WatchEntry { |
[email protected] | dae3f53 | 2014-04-26 22:26:38 | [diff] [blame] | 136 | explicit WatchEntry(const FilePath::StringType& dirname) |
| 137 | : watch(InotifyReader::kInvalidWatch), |
| 138 | subdir(dirname) {} |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 139 | |
[email protected] | dae3f53 | 2014-04-26 22:26:38 | [diff] [blame] | 140 | InotifyReader::Watch watch; |
| 141 | FilePath::StringType subdir; |
| 142 | FilePath::StringType linkname; |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 143 | }; |
| 144 | typedef std::vector<WatchEntry> WatchVector; |
| 145 | |
| 146 | // Reconfigure to watch for the most specific parent directory of |target_| |
[email protected] | f8d025c | 2014-05-13 19:31:30 | [diff] [blame] | 147 | // that exists. Also calls UpdateRecursiveWatches() below. |
| 148 | void UpdateWatches(); |
| 149 | |
| 150 | // Reconfigure to recursively watch |target_| and all its sub-directories. |
| 151 | // - This is a no-op if the watch is not recursive. |
| 152 | // - If |target_| does not exist, then clear all the recursive watches. |
| 153 | // - Assuming |target_| exists, passing kInvalidWatch as |fired_watch| forces |
| 154 | // addition of recursive watches for |target_|. |
| 155 | // - Otherwise, only the directory associated with |fired_watch| and its |
| 156 | // sub-directories will be reconfigured. |
| 157 | void UpdateRecursiveWatches(InotifyReader::Watch fired_watch, bool is_dir); |
| 158 | |
| 159 | // Enumerate recursively through |path| and add / update watches. |
| 160 | void UpdateRecursiveWatchesForPath(const FilePath& path); |
| 161 | |
| 162 | // Do internal bookkeeping to update mappings between |watch| and its |
| 163 | // associated full path |path|. |
| 164 | void TrackWatchForRecursion(InotifyReader::Watch watch, const FilePath& path); |
| 165 | |
| 166 | // Remove all the recursive watches. |
| 167 | void RemoveRecursiveWatches(); |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 168 | |
[email protected] | dae3f53 | 2014-04-26 22:26:38 | [diff] [blame] | 169 | // |path| is a symlink to a non-existent target. Attempt to add a watch to |
jbudorick | 93e9741 | 2015-07-23 15:26:25 | [diff] [blame] | 170 | // the link target's parent directory. Update |watch_entry| on success. |
| 171 | void AddWatchForBrokenSymlink(const FilePath& path, WatchEntry* watch_entry); |
[email protected] | dae3f53 | 2014-04-26 22:26:38 | [diff] [blame] | 172 | |
| 173 | bool HasValidWatchVector() const; |
| 174 | |
[email protected] | 6b5d002 | 2013-01-15 00:37:47 | [diff] [blame] | 175 | // Callback to notify upon changes. |
| 176 | FilePathWatcher::Callback callback_; |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 177 | |
| 178 | // The file or directory we're supposed to watch. |
| 179 | FilePath target_; |
| 180 | |
[email protected] | f8d025c | 2014-05-13 19:31:30 | [diff] [blame] | 181 | bool recursive_; |
| 182 | |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 183 | // The vector of watches and next component names for all path components, |
| 184 | // starting at the root directory. The last entry corresponds to the watch for |
[email protected] | dae3f53 | 2014-04-26 22:26:38 | [diff] [blame] | 185 | // |target_| and always stores an empty next component name in |subdir|. |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 186 | WatchVector watches_; |
| 187 | |
[email protected] | f8d025c | 2014-05-13 19:31:30 | [diff] [blame] | 188 | hash_map<InotifyReader::Watch, FilePath> recursive_paths_by_watch_; |
| 189 | std::map<FilePath, InotifyReader::Watch> recursive_watches_by_path_; |
| 190 | |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 191 | DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl); |
| 192 | }; |
| 193 | |
[email protected] | e7b3a61 | 2012-01-05 02:18:18 | [diff] [blame] | 194 | void InotifyReaderCallback(InotifyReader* reader, int inotify_fd, |
| 195 | int shutdown_fd) { |
| 196 | // Make sure the file descriptors are good for use with select(). |
| 197 | CHECK_LE(0, inotify_fd); |
| 198 | CHECK_GT(FD_SETSIZE, inotify_fd); |
| 199 | CHECK_LE(0, shutdown_fd); |
| 200 | CHECK_GT(FD_SETSIZE, shutdown_fd); |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 201 | |
ssid | 759dd8c | 2015-02-09 21:25:39 | [diff] [blame] | 202 | trace_event::TraceLog::GetInstance()->SetCurrentThreadBlocksMessageLoop(); |
[email protected] | 14040a0 | 2013-10-22 02:16:58 | [diff] [blame] | 203 | |
[email protected] | e7b3a61 | 2012-01-05 02:18:18 | [diff] [blame] | 204 | while (true) { |
| 205 | fd_set rfds; |
| 206 | FD_ZERO(&rfds); |
| 207 | FD_SET(inotify_fd, &rfds); |
| 208 | FD_SET(shutdown_fd, &rfds); |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 209 | |
[email protected] | e7b3a61 | 2012-01-05 02:18:18 | [diff] [blame] | 210 | // Wait until some inotify events are available. |
| 211 | int select_result = |
| 212 | HANDLE_EINTR(select(std::max(inotify_fd, shutdown_fd) + 1, |
| 213 | &rfds, NULL, NULL, NULL)); |
| 214 | if (select_result < 0) { |
| 215 | DPLOG(WARNING) << "select failed"; |
| 216 | return; |
| 217 | } |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 218 | |
[email protected] | e7b3a61 | 2012-01-05 02:18:18 | [diff] [blame] | 219 | if (FD_ISSET(shutdown_fd, &rfds)) |
| 220 | return; |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 221 | |
[email protected] | e7b3a61 | 2012-01-05 02:18:18 | [diff] [blame] | 222 | // Adjust buffer size to current event queue size. |
| 223 | int buffer_size; |
| 224 | int ioctl_result = HANDLE_EINTR(ioctl(inotify_fd, FIONREAD, |
| 225 | &buffer_size)); |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 226 | |
[email protected] | e7b3a61 | 2012-01-05 02:18:18 | [diff] [blame] | 227 | if (ioctl_result != 0) { |
| 228 | DPLOG(WARNING) << "ioctl failed"; |
| 229 | return; |
| 230 | } |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 231 | |
[email protected] | e7b3a61 | 2012-01-05 02:18:18 | [diff] [blame] | 232 | std::vector<char> buffer(buffer_size); |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 233 | |
[email protected] | e7b3a61 | 2012-01-05 02:18:18 | [diff] [blame] | 234 | ssize_t bytes_read = HANDLE_EINTR(read(inotify_fd, &buffer[0], |
| 235 | buffer_size)); |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 236 | |
[email protected] | e7b3a61 | 2012-01-05 02:18:18 | [diff] [blame] | 237 | if (bytes_read < 0) { |
| 238 | DPLOG(WARNING) << "read from inotify fd failed"; |
| 239 | return; |
| 240 | } |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 241 | |
[email protected] | e7b3a61 | 2012-01-05 02:18:18 | [diff] [blame] | 242 | ssize_t i = 0; |
| 243 | while (i < bytes_read) { |
| 244 | inotify_event* event = reinterpret_cast<inotify_event*>(&buffer[i]); |
| 245 | size_t event_size = sizeof(inotify_event) + event->len; |
| 246 | DCHECK(i + event_size <= static_cast<size_t>(bytes_read)); |
| 247 | reader->OnInotifyEvent(event); |
| 248 | i += event_size; |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 249 | } |
| 250 | } |
[email protected] | e7b3a61 | 2012-01-05 02:18:18 | [diff] [blame] | 251 | } |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 252 | |
[email protected] | b264eab | 2013-11-27 23:22:08 | [diff] [blame] | 253 | static LazyInstance<InotifyReader>::Leaky g_inotify_reader = |
[email protected] | 6de0fd1d | 2011-11-15 13:31:49 | [diff] [blame] | 254 | LAZY_INSTANCE_INITIALIZER; |
[email protected] | fee46a8 | 2010-12-09 16:42:15 | [diff] [blame] | 255 | |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 256 | InotifyReader::InotifyReader() |
| 257 | : thread_("inotify_reader"), |
| 258 | inotify_fd_(inotify_init()), |
| 259 | valid_(false) { |
[email protected] | de85c52 | 2013-11-26 03:41:58 | [diff] [blame] | 260 | if (inotify_fd_ < 0) |
| 261 | PLOG(ERROR) << "inotify_init() failed"; |
| 262 | |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 263 | shutdown_pipe_[0] = -1; |
| 264 | shutdown_pipe_[1] = -1; |
| 265 | if (inotify_fd_ >= 0 && pipe(shutdown_pipe_) == 0 && thread_.Start()) { |
skyostil | 054861d | 2015-04-30 19:06:15 | [diff] [blame] | 266 | thread_.task_runner()->PostTask( |
[email protected] | dae3f53 | 2014-04-26 22:26:38 | [diff] [blame] | 267 | FROM_HERE, |
| 268 | Bind(&InotifyReaderCallback, this, inotify_fd_, shutdown_pipe_[0])); |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 269 | valid_ = true; |
| 270 | } |
| 271 | } |
| 272 | |
| 273 | InotifyReader::~InotifyReader() { |
| 274 | if (valid_) { |
| 275 | // Write to the self-pipe so that the select call in InotifyReaderTask |
| 276 | // returns. |
| 277 | ssize_t ret = HANDLE_EINTR(write(shutdown_pipe_[1], "", 1)); |
| 278 | DPCHECK(ret > 0); |
| 279 | DCHECK_EQ(ret, 1); |
| 280 | thread_.Stop(); |
| 281 | } |
| 282 | if (inotify_fd_ >= 0) |
| 283 | close(inotify_fd_); |
| 284 | if (shutdown_pipe_[0] >= 0) |
| 285 | close(shutdown_pipe_[0]); |
| 286 | if (shutdown_pipe_[1] >= 0) |
| 287 | close(shutdown_pipe_[1]); |
| 288 | } |
| 289 | |
| 290 | InotifyReader::Watch InotifyReader::AddWatch( |
| 291 | const FilePath& path, FilePathWatcherImpl* watcher) { |
| 292 | if (!valid_) |
| 293 | return kInvalidWatch; |
| 294 | |
[email protected] | b264eab | 2013-11-27 23:22:08 | [diff] [blame] | 295 | AutoLock auto_lock(lock_); |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 296 | |
| 297 | Watch watch = inotify_add_watch(inotify_fd_, path.value().c_str(), |
[email protected] | f8d025c | 2014-05-13 19:31:30 | [diff] [blame] | 298 | IN_ATTRIB | IN_CREATE | IN_DELETE | |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 299 | IN_CLOSE_WRITE | IN_MOVE | |
| 300 | IN_ONLYDIR); |
| 301 | |
| 302 | if (watch == kInvalidWatch) |
| 303 | return kInvalidWatch; |
| 304 | |
| 305 | watchers_[watch].insert(watcher); |
| 306 | |
| 307 | return watch; |
| 308 | } |
| 309 | |
[email protected] | dae3f53 | 2014-04-26 22:26:38 | [diff] [blame] | 310 | void InotifyReader::RemoveWatch(Watch watch, FilePathWatcherImpl* watcher) { |
| 311 | if (!valid_ || (watch == kInvalidWatch)) |
| 312 | return; |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 313 | |
[email protected] | b264eab | 2013-11-27 23:22:08 | [diff] [blame] | 314 | AutoLock auto_lock(lock_); |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 315 | |
| 316 | watchers_[watch].erase(watcher); |
| 317 | |
| 318 | if (watchers_[watch].empty()) { |
| 319 | watchers_.erase(watch); |
[email protected] | dae3f53 | 2014-04-26 22:26:38 | [diff] [blame] | 320 | inotify_rm_watch(inotify_fd_, watch); |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 321 | } |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 322 | } |
| 323 | |
| 324 | void InotifyReader::OnInotifyEvent(const inotify_event* event) { |
| 325 | if (event->mask & IN_IGNORED) |
| 326 | return; |
| 327 | |
| 328 | FilePath::StringType child(event->len ? event->name : FILE_PATH_LITERAL("")); |
[email protected] | b264eab | 2013-11-27 23:22:08 | [diff] [blame] | 329 | AutoLock auto_lock(lock_); |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 330 | |
| 331 | for (WatcherSet::iterator watcher = watchers_[event->wd].begin(); |
| 332 | watcher != watchers_[event->wd].end(); |
| 333 | ++watcher) { |
[email protected] | b8ca91f | 2011-03-18 04:11:49 | [diff] [blame] | 334 | (*watcher)->OnFilePathChanged(event->wd, |
| 335 | child, |
[email protected] | f8d025c | 2014-05-13 19:31:30 | [diff] [blame] | 336 | event->mask & (IN_CREATE | IN_MOVED_TO), |
| 337 | event->mask & (IN_DELETE | IN_MOVED_FROM), |
| 338 | event->mask & IN_ISDIR); |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 339 | } |
| 340 | } |
| 341 | |
[email protected] | f8d025c | 2014-05-13 19:31:30 | [diff] [blame] | 342 | FilePathWatcherImpl::FilePathWatcherImpl() |
| 343 | : recursive_(false) { |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 344 | } |
| 345 | |
[email protected] | aa8946b | 2012-03-13 08:33:59 | [diff] [blame] | 346 | void FilePathWatcherImpl::OnFilePathChanged(InotifyReader::Watch fired_watch, |
| 347 | const FilePath::StringType& child, |
[email protected] | f8d025c | 2014-05-13 19:31:30 | [diff] [blame] | 348 | bool created, |
| 349 | bool deleted, |
| 350 | bool is_dir) { |
skyostil | 054861d | 2015-04-30 19:06:15 | [diff] [blame] | 351 | if (!task_runner()->BelongsToCurrentThread()) { |
| 352 | // Switch to task_runner() to access |watches_| safely. |
| 353 | task_runner()->PostTask(FROM_HERE, |
| 354 | Bind(&FilePathWatcherImpl::OnFilePathChanged, this, |
| 355 | fired_watch, child, created, deleted, is_dir)); |
[email protected] | dae3f53 | 2014-04-26 22:26:38 | [diff] [blame] | 356 | return; |
| 357 | } |
| 358 | |
| 359 | // Check to see if CancelOnMessageLoopThread() has already been called. |
| 360 | // May happen when code flow reaches here from the PostTask() above. |
| 361 | if (watches_.empty()) { |
| 362 | DCHECK(target_.empty()); |
[email protected] | b8ca91f | 2011-03-18 04:11:49 | [diff] [blame] | 363 | return; |
| 364 | } |
| 365 | |
| 366 | DCHECK(MessageLoopForIO::current()); |
[email protected] | dae3f53 | 2014-04-26 22:26:38 | [diff] [blame] | 367 | DCHECK(HasValidWatchVector()); |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 368 | |
[email protected] | f8d025c | 2014-05-13 19:31:30 | [diff] [blame] | 369 | // Used below to avoid multiple recursive updates. |
| 370 | bool did_update = false; |
| 371 | |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 372 | // Find the entry in |watches_| that corresponds to |fired_watch|. |
[email protected] | dae3f53 | 2014-04-26 22:26:38 | [diff] [blame] | 373 | for (size_t i = 0; i < watches_.size(); ++i) { |
| 374 | const WatchEntry& watch_entry = watches_[i]; |
| 375 | if (fired_watch != watch_entry.watch) |
| 376 | continue; |
[email protected] | 9a2ab28 | 2011-09-05 15:02:12 | [diff] [blame] | 377 | |
[email protected] | dae3f53 | 2014-04-26 22:26:38 | [diff] [blame] | 378 | // Check whether a path component of |target_| changed. |
| 379 | bool change_on_target_path = |
| 380 | child.empty() || |
| 381 | (child == watch_entry.linkname) || |
| 382 | (child == watch_entry.subdir); |
[email protected] | 9a2ab28 | 2011-09-05 15:02:12 | [diff] [blame] | 383 | |
[email protected] | dae3f53 | 2014-04-26 22:26:38 | [diff] [blame] | 384 | // Check if the change references |target_| or a direct child of |target_|. |
thestig | bd265f969 | 2014-12-19 22:51:16 | [diff] [blame] | 385 | bool target_changed; |
| 386 | if (watch_entry.subdir.empty()) { |
| 387 | // The fired watch is for a WatchEntry without a subdir. Thus for a given |
| 388 | // |target_| = "/path/to/foo", this is for "foo". Here, check either: |
| 389 | // - the target has no symlink: it is the target and it changed. |
| 390 | // - the target has a symlink, and it matches |child|. |
| 391 | target_changed = (watch_entry.linkname.empty() || |
| 392 | child == watch_entry.linkname); |
| 393 | } else { |
| 394 | // The fired watch is for a WatchEntry with a subdir. Thus for a given |
| 395 | // |target_| = "/path/to/foo", this is for {"/", "/path", "/path/to"}. |
| 396 | // So we can safely access the next WatchEntry since we have not reached |
| 397 | // the end yet. Check |watch_entry| is for "/path/to", i.e. the next |
| 398 | // element is "foo". |
| 399 | bool next_watch_may_be_for_target = watches_[i + 1].subdir.empty(); |
| 400 | if (next_watch_may_be_for_target) { |
| 401 | // The current |watch_entry| is for "/path/to", so check if the |child| |
| 402 | // that changed is "foo". |
| 403 | target_changed = watch_entry.subdir == child; |
| 404 | } else { |
| 405 | // The current |watch_entry| is not for "/path/to", so the next entry |
| 406 | // cannot be "foo". Thus |target_| has not changed. |
| 407 | target_changed = false; |
| 408 | } |
| 409 | } |
[email protected] | 9a2ab28 | 2011-09-05 15:02:12 | [diff] [blame] | 410 | |
[email protected] | dae3f53 | 2014-04-26 22:26:38 | [diff] [blame] | 411 | // Update watches if a directory component of the |target_| path |
| 412 | // (dis)appears. Note that we don't add the additional restriction of |
| 413 | // checking the event mask to see if it is for a directory here as changes |
| 414 | // to symlinks on the target path will not have IN_ISDIR set in the event |
| 415 | // masks. As a result we may sometimes call UpdateWatches() unnecessarily. |
[email protected] | f8d025c | 2014-05-13 19:31:30 | [diff] [blame] | 416 | if (change_on_target_path && (created || deleted) && !did_update) { |
| 417 | UpdateWatches(); |
| 418 | did_update = true; |
[email protected] | dae3f53 | 2014-04-26 22:26:38 | [diff] [blame] | 419 | } |
| 420 | |
| 421 | // Report the following events: |
| 422 | // - The target or a direct child of the target got changed (in case the |
| 423 | // watched path refers to a directory). |
| 424 | // - One of the parent directories got moved or deleted, since the target |
| 425 | // disappears in this case. |
| 426 | // - One of the parent directories appears. The event corresponding to |
| 427 | // the target appearing might have been missed in this case, so recheck. |
| 428 | if (target_changed || |
[email protected] | f8d025c | 2014-05-13 19:31:30 | [diff] [blame] | 429 | (change_on_target_path && deleted) || |
| 430 | (change_on_target_path && created && PathExists(target_))) { |
| 431 | if (!did_update) { |
| 432 | UpdateRecursiveWatches(fired_watch, is_dir); |
| 433 | did_update = true; |
| 434 | } |
[email protected] | dae3f53 | 2014-04-26 22:26:38 | [diff] [blame] | 435 | callback_.Run(target_, false /* error */); |
| 436 | return; |
[email protected] | 9a2ab28 | 2011-09-05 15:02:12 | [diff] [blame] | 437 | } |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 438 | } |
[email protected] | f8d025c | 2014-05-13 19:31:30 | [diff] [blame] | 439 | |
| 440 | if (ContainsKey(recursive_paths_by_watch_, fired_watch)) { |
| 441 | if (!did_update) |
| 442 | UpdateRecursiveWatches(fired_watch, is_dir); |
| 443 | callback_.Run(target_, false /* error */); |
| 444 | } |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 445 | } |
| 446 | |
| 447 | bool FilePathWatcherImpl::Watch(const FilePath& path, |
[email protected] | 66a5940 | 2012-12-05 00:36:39 | [diff] [blame] | 448 | bool recursive, |
[email protected] | 6b5d002 | 2013-01-15 00:37:47 | [diff] [blame] | 449 | const FilePathWatcher::Callback& callback) { |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 450 | DCHECK(target_.empty()); |
[email protected] | b8ca91f | 2011-03-18 04:11:49 | [diff] [blame] | 451 | DCHECK(MessageLoopForIO::current()); |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 452 | |
skyostil | 054861d | 2015-04-30 19:06:15 | [diff] [blame] | 453 | set_task_runner(ThreadTaskRunnerHandle::Get()); |
[email protected] | 6b5d002 | 2013-01-15 00:37:47 | [diff] [blame] | 454 | callback_ = callback; |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 455 | target_ = path; |
[email protected] | f8d025c | 2014-05-13 19:31:30 | [diff] [blame] | 456 | recursive_ = recursive; |
[email protected] | d64abe1 | 2011-03-22 20:14:06 | [diff] [blame] | 457 | MessageLoop::current()->AddDestructionObserver(this); |
| 458 | |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 459 | std::vector<FilePath::StringType> comps; |
| 460 | target_.GetComponents(&comps); |
| 461 | DCHECK(!comps.empty()); |
[email protected] | dae3f53 | 2014-04-26 22:26:38 | [diff] [blame] | 462 | for (size_t i = 1; i < comps.size(); ++i) |
| 463 | watches_.push_back(WatchEntry(comps[i])); |
| 464 | watches_.push_back(WatchEntry(FilePath::StringType())); |
[email protected] | f8d025c | 2014-05-13 19:31:30 | [diff] [blame] | 465 | UpdateWatches(); |
| 466 | return true; |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 467 | } |
| 468 | |
| 469 | void FilePathWatcherImpl::Cancel() { |
[email protected] | 6b5d002 | 2013-01-15 00:37:47 | [diff] [blame] | 470 | if (callback_.is_null()) { |
[email protected] | dae3f53 | 2014-04-26 22:26:38 | [diff] [blame] | 471 | // Watch was never called, or the message_loop() thread is already gone. |
[email protected] | d64abe1 | 2011-03-22 20:14:06 | [diff] [blame] | 472 | set_cancelled(); |
[email protected] | b8ca91f | 2011-03-18 04:11:49 | [diff] [blame] | 473 | return; |
| 474 | } |
| 475 | |
[email protected] | dae3f53 | 2014-04-26 22:26:38 | [diff] [blame] | 476 | // Switch to the message_loop() if necessary so we can access |watches_|. |
skyostil | 054861d | 2015-04-30 19:06:15 | [diff] [blame] | 477 | if (!task_runner()->BelongsToCurrentThread()) { |
| 478 | task_runner()->PostTask(FROM_HERE, Bind(&FilePathWatcher::CancelWatch, |
| 479 | make_scoped_refptr(this))); |
[email protected] | d64abe1 | 2011-03-22 20:14:06 | [diff] [blame] | 480 | } else { |
| 481 | CancelOnMessageLoopThread(); |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 482 | } |
[email protected] | d64abe1 | 2011-03-22 20:14:06 | [diff] [blame] | 483 | } |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 484 | |
[email protected] | d64abe1 | 2011-03-22 20:14:06 | [diff] [blame] | 485 | void FilePathWatcherImpl::CancelOnMessageLoopThread() { |
skyostil | 054861d | 2015-04-30 19:06:15 | [diff] [blame] | 486 | DCHECK(task_runner()->BelongsToCurrentThread()); |
[email protected] | dae3f53 | 2014-04-26 22:26:38 | [diff] [blame] | 487 | set_cancelled(); |
[email protected] | d64abe1 | 2011-03-22 20:14:06 | [diff] [blame] | 488 | |
[email protected] | 6b5d002 | 2013-01-15 00:37:47 | [diff] [blame] | 489 | if (!callback_.is_null()) { |
[email protected] | f8a3f137 | 2011-08-18 00:26:25 | [diff] [blame] | 490 | MessageLoop::current()->RemoveDestructionObserver(this); |
[email protected] | 6b5d002 | 2013-01-15 00:37:47 | [diff] [blame] | 491 | callback_.Reset(); |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 492 | } |
[email protected] | f8a3f137 | 2011-08-18 00:26:25 | [diff] [blame] | 493 | |
[email protected] | dae3f53 | 2014-04-26 22:26:38 | [diff] [blame] | 494 | for (size_t i = 0; i < watches_.size(); ++i) |
| 495 | g_inotify_reader.Get().RemoveWatch(watches_[i].watch, this); |
[email protected] | f8a3f137 | 2011-08-18 00:26:25 | [diff] [blame] | 496 | watches_.clear(); |
| 497 | target_.clear(); |
[email protected] | f8d025c | 2014-05-13 19:31:30 | [diff] [blame] | 498 | |
| 499 | if (recursive_) |
| 500 | RemoveRecursiveWatches(); |
[email protected] | d64abe1 | 2011-03-22 20:14:06 | [diff] [blame] | 501 | } |
| 502 | |
| 503 | void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() { |
| 504 | CancelOnMessageLoopThread(); |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 505 | } |
| 506 | |
[email protected] | f8d025c | 2014-05-13 19:31:30 | [diff] [blame] | 507 | void FilePathWatcherImpl::UpdateWatches() { |
[email protected] | dae3f53 | 2014-04-26 22:26:38 | [diff] [blame] | 508 | // Ensure this runs on the message_loop() exclusively in order to avoid |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 509 | // concurrency issues. |
skyostil | 054861d | 2015-04-30 19:06:15 | [diff] [blame] | 510 | DCHECK(task_runner()->BelongsToCurrentThread()); |
[email protected] | dae3f53 | 2014-04-26 22:26:38 | [diff] [blame] | 511 | DCHECK(HasValidWatchVector()); |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 512 | |
| 513 | // Walk the list of watches and update them as we go. |
| 514 | FilePath path(FILE_PATH_LITERAL("/")); |
[email protected] | dae3f53 | 2014-04-26 22:26:38 | [diff] [blame] | 515 | for (size_t i = 0; i < watches_.size(); ++i) { |
| 516 | WatchEntry& watch_entry = watches_[i]; |
| 517 | InotifyReader::Watch old_watch = watch_entry.watch; |
| 518 | watch_entry.watch = InotifyReader::kInvalidWatch; |
| 519 | watch_entry.linkname.clear(); |
jbudorick | 93e9741 | 2015-07-23 15:26:25 | [diff] [blame] | 520 | watch_entry.watch = g_inotify_reader.Get().AddWatch(path, this); |
| 521 | if (watch_entry.watch == InotifyReader::kInvalidWatch) { |
| 522 | // Ignore the error code (beyond symlink handling) to attempt to add |
| 523 | // watches on accessible children of unreadable directories. Note that |
| 524 | // this is a best-effort attempt; we may not catch events in this |
| 525 | // scenario. |
| 526 | if (IsLink(path)) |
| 527 | AddWatchForBrokenSymlink(path, &watch_entry); |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 528 | } |
[email protected] | dae3f53 | 2014-04-26 22:26:38 | [diff] [blame] | 529 | if (old_watch != watch_entry.watch) |
[email protected] | fee46a8 | 2010-12-09 16:42:15 | [diff] [blame] | 530 | g_inotify_reader.Get().RemoveWatch(old_watch, this); |
[email protected] | dae3f53 | 2014-04-26 22:26:38 | [diff] [blame] | 531 | path = path.Append(watch_entry.subdir); |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 532 | } |
| 533 | |
[email protected] | f8d025c | 2014-05-13 19:31:30 | [diff] [blame] | 534 | UpdateRecursiveWatches(InotifyReader::kInvalidWatch, |
| 535 | false /* is directory? */); |
| 536 | } |
| 537 | |
| 538 | void FilePathWatcherImpl::UpdateRecursiveWatches( |
| 539 | InotifyReader::Watch fired_watch, |
| 540 | bool is_dir) { |
| 541 | if (!recursive_) |
| 542 | return; |
| 543 | |
| 544 | if (!DirectoryExists(target_)) { |
| 545 | RemoveRecursiveWatches(); |
| 546 | return; |
| 547 | } |
| 548 | |
| 549 | // Check to see if this is a forced update or if some component of |target_| |
| 550 | // has changed. For these cases, redo the watches for |target_| and below. |
| 551 | if (!ContainsKey(recursive_paths_by_watch_, fired_watch)) { |
| 552 | UpdateRecursiveWatchesForPath(target_); |
| 553 | return; |
| 554 | } |
| 555 | |
| 556 | // Underneath |target_|, only directory changes trigger watch updates. |
| 557 | if (!is_dir) |
| 558 | return; |
| 559 | |
| 560 | const FilePath& changed_dir = recursive_paths_by_watch_[fired_watch]; |
| 561 | |
| 562 | std::map<FilePath, InotifyReader::Watch>::iterator start_it = |
| 563 | recursive_watches_by_path_.lower_bound(changed_dir); |
| 564 | std::map<FilePath, InotifyReader::Watch>::iterator end_it = start_it; |
| 565 | for (; end_it != recursive_watches_by_path_.end(); ++end_it) { |
| 566 | const FilePath& cur_path = end_it->first; |
| 567 | if (!changed_dir.IsParent(cur_path)) |
| 568 | break; |
| 569 | if (!DirectoryExists(cur_path)) |
| 570 | g_inotify_reader.Get().RemoveWatch(end_it->second, this); |
| 571 | } |
| 572 | recursive_watches_by_path_.erase(start_it, end_it); |
| 573 | UpdateRecursiveWatchesForPath(changed_dir); |
| 574 | } |
| 575 | |
| 576 | void FilePathWatcherImpl::UpdateRecursiveWatchesForPath(const FilePath& path) { |
| 577 | DCHECK(recursive_); |
| 578 | DCHECK(!path.empty()); |
| 579 | DCHECK(DirectoryExists(path)); |
| 580 | |
| 581 | // Note: SHOW_SYM_LINKS exposes symlinks as symlinks, so they are ignored |
| 582 | // rather than followed. Following symlinks can easily lead to the undesirable |
| 583 | // situation where the entire file system is being watched. |
| 584 | FileEnumerator enumerator( |
| 585 | path, |
| 586 | true /* recursive enumeration */, |
| 587 | FileEnumerator::DIRECTORIES | FileEnumerator::SHOW_SYM_LINKS); |
| 588 | for (FilePath current = enumerator.Next(); |
| 589 | !current.empty(); |
| 590 | current = enumerator.Next()) { |
| 591 | DCHECK(enumerator.GetInfo().IsDirectory()); |
| 592 | |
| 593 | if (!ContainsKey(recursive_watches_by_path_, current)) { |
| 594 | // Add new watches. |
| 595 | InotifyReader::Watch watch = |
| 596 | g_inotify_reader.Get().AddWatch(current, this); |
| 597 | TrackWatchForRecursion(watch, current); |
| 598 | } else { |
| 599 | // Update existing watches. |
| 600 | InotifyReader::Watch old_watch = recursive_watches_by_path_[current]; |
| 601 | DCHECK_NE(InotifyReader::kInvalidWatch, old_watch); |
| 602 | InotifyReader::Watch watch = |
| 603 | g_inotify_reader.Get().AddWatch(current, this); |
| 604 | if (watch != old_watch) { |
| 605 | g_inotify_reader.Get().RemoveWatch(old_watch, this); |
| 606 | recursive_paths_by_watch_.erase(old_watch); |
| 607 | recursive_watches_by_path_.erase(current); |
| 608 | TrackWatchForRecursion(watch, current); |
| 609 | } |
| 610 | } |
| 611 | } |
| 612 | } |
| 613 | |
| 614 | void FilePathWatcherImpl::TrackWatchForRecursion(InotifyReader::Watch watch, |
| 615 | const FilePath& path) { |
| 616 | DCHECK(recursive_); |
| 617 | DCHECK(!path.empty()); |
| 618 | DCHECK(target_.IsParent(path)); |
| 619 | |
| 620 | if (watch == InotifyReader::kInvalidWatch) |
| 621 | return; |
| 622 | |
| 623 | DCHECK(!ContainsKey(recursive_paths_by_watch_, watch)); |
| 624 | DCHECK(!ContainsKey(recursive_watches_by_path_, path)); |
| 625 | recursive_paths_by_watch_[watch] = path; |
| 626 | recursive_watches_by_path_[path] = watch; |
| 627 | } |
| 628 | |
| 629 | void FilePathWatcherImpl::RemoveRecursiveWatches() { |
| 630 | if (!recursive_) |
| 631 | return; |
| 632 | |
| 633 | for (hash_map<InotifyReader::Watch, FilePath>::const_iterator it = |
| 634 | recursive_paths_by_watch_.begin(); |
| 635 | it != recursive_paths_by_watch_.end(); |
| 636 | ++it) { |
| 637 | g_inotify_reader.Get().RemoveWatch(it->first, this); |
| 638 | } |
| 639 | recursive_paths_by_watch_.clear(); |
| 640 | recursive_watches_by_path_.clear(); |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 641 | } |
| 642 | |
jbudorick | 93e9741 | 2015-07-23 15:26:25 | [diff] [blame] | 643 | void FilePathWatcherImpl::AddWatchForBrokenSymlink(const FilePath& path, |
[email protected] | dae3f53 | 2014-04-26 22:26:38 | [diff] [blame] | 644 | WatchEntry* watch_entry) { |
| 645 | DCHECK_EQ(InotifyReader::kInvalidWatch, watch_entry->watch); |
| 646 | FilePath link; |
| 647 | if (!ReadSymbolicLink(path, &link)) |
jbudorick | 93e9741 | 2015-07-23 15:26:25 | [diff] [blame] | 648 | return; |
[email protected] | dae3f53 | 2014-04-26 22:26:38 | [diff] [blame] | 649 | |
| 650 | if (!link.IsAbsolute()) |
| 651 | link = path.DirName().Append(link); |
| 652 | |
| 653 | // Try watching symlink target directory. If the link target is "/", then we |
| 654 | // shouldn't get here in normal situations and if we do, we'd watch "/" for |
| 655 | // changes to a component "/" which is harmless so no special treatment of |
| 656 | // this case is required. |
| 657 | InotifyReader::Watch watch = |
| 658 | g_inotify_reader.Get().AddWatch(link.DirName(), this); |
| 659 | if (watch == InotifyReader::kInvalidWatch) { |
| 660 | // TODO(craig) Symlinks only work if the parent directory for the target |
| 661 | // exist. Ideally we should make sure we've watched all the components of |
| 662 | // the symlink path for changes. See crbug.com/91561 for details. |
| 663 | DPLOG(WARNING) << "Watch failed for " << link.DirName().value(); |
jbudorick | 93e9741 | 2015-07-23 15:26:25 | [diff] [blame] | 664 | return; |
[email protected] | dae3f53 | 2014-04-26 22:26:38 | [diff] [blame] | 665 | } |
| 666 | watch_entry->watch = watch; |
| 667 | watch_entry->linkname = link.BaseName().value(); |
[email protected] | dae3f53 | 2014-04-26 22:26:38 | [diff] [blame] | 668 | } |
| 669 | |
| 670 | bool FilePathWatcherImpl::HasValidWatchVector() const { |
| 671 | if (watches_.empty()) |
| 672 | return false; |
| 673 | for (size_t i = 0; i < watches_.size() - 1; ++i) { |
| 674 | if (watches_[i].subdir.empty()) |
| 675 | return false; |
| 676 | } |
| 677 | return watches_[watches_.size() - 1].subdir.empty(); |
| 678 | } |
| 679 | |
[email protected] | 5199d74 | 2010-08-19 10:35:46 | [diff] [blame] | 680 | } // namespace |
| 681 | |
| 682 | FilePathWatcher::FilePathWatcher() { |
| 683 | impl_ = new FilePathWatcherImpl(); |
| 684 | } |
[email protected] | 493c800 | 2011-04-14 16:56:01 | [diff] [blame] | 685 | |
[email protected] | 493c800 | 2011-04-14 16:56:01 | [diff] [blame] | 686 | } // namespace base |