OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 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/files/file_path_watcher.h" | 5 #include "base/files/file_path_watcher.h" |
6 | 6 |
7 #include <fcntl.h> | 7 #include <fcntl.h> |
8 #include <sys/event.h> | 8 #include <sys/event.h> |
9 #include <sys/param.h> | 9 #include <sys/param.h> |
10 | 10 |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
87 // Returns true if the kevent values are error free. | 87 // Returns true if the kevent values are error free. |
88 bool AreKeventValuesValid(struct kevent* kevents, int count); | 88 bool AreKeventValuesValid(struct kevent* kevents, int count); |
89 | 89 |
90 // Respond to a change of attributes of the path component represented by | 90 // Respond to a change of attributes of the path component represented by |
91 // |event|. Sets |target_file_affected| to true if |target_| is affected. | 91 // |event|. Sets |target_file_affected| to true if |target_| is affected. |
92 // Sets |update_watches| to true if |events_| need to be updated. | 92 // Sets |update_watches| to true if |events_| need to be updated. |
93 void HandleAttributesChange(const EventVector::iterator& event, | 93 void HandleAttributesChange(const EventVector::iterator& event, |
94 bool* target_file_affected, | 94 bool* target_file_affected, |
95 bool* update_watches); | 95 bool* update_watches); |
96 | 96 |
97 // Respond to a move of deletion of the path component represented by | 97 // Respond to a move or deletion of the path component represented by |
98 // |event|. Sets |target_file_affected| to true if |target_| is affected. | 98 // |event|. Sets |target_file_affected| to true if |target_| is affected. |
99 // Sets |update_watches| to true if |events_| need to be updated. | 99 // Sets |update_watches| to true if |events_| need to be updated. |
100 void HandleDeleteOrMoveChange(const EventVector::iterator& event, | 100 void HandleDeleteOrMoveChange(const EventVector::iterator& event, |
101 bool* target_file_affected, | 101 bool* target_file_affected, |
102 bool* update_watches); | 102 bool* update_watches); |
103 | 103 |
104 // Respond to a creation of an item in the path component represented by | 104 // Respond to a creation of an item in the path component represented by |
105 // |event|. Sets |target_file_affected| to true if |target_| is affected. | 105 // |event|. Sets |target_file_affected| to true if |target_| is affected. |
106 // Sets |update_watches| to true if |events_| need to be updated. | 106 // Sets |update_watches| to true if |events_| need to be updated. |
107 void HandleCreateItemChange(const EventVector::iterator& event, | 107 void HandleCreateItemChange(const EventVector::iterator& event, |
108 bool* target_file_affected, | 108 bool* target_file_affected, |
109 bool* update_watches); | 109 bool* update_watches); |
110 | 110 |
111 // Update |events_| with the current status of the system. | 111 // Update |events_| with the current status of the system. |
112 // Sets |target_file_affected| to true if |target_| is affected. | 112 // Sets |target_file_affected| to true if |target_| is affected. |
113 // Returns false if an error occurs. | 113 // Returns false if an error occurs. |
114 bool UpdateWatches(bool* target_file_affected); | 114 bool UpdateWatches(bool* target_file_affected); |
115 | 115 |
116 // Fills |events| with one kevent per component in |path|. | 116 // Fills |events| with one kevent per component in |path|. |
117 // Returns the number of valid events created where a valid event is | 117 // Returns the number of valid events created where a valid event is |
118 // defined as one that has a ident (file descriptor) field != -1. | 118 // defined as one that has a ident (file descriptor) field != -1. |
119 static int EventsForPath(FilePath path, EventVector *events); | 119 static int EventsForPath(FilePath path, EventVector *events); |
120 | 120 |
121 // Release a kevent generated by EventsForPath. | 121 // Release a kevent generated by EventsForPath. |
122 static void ReleaseEvent(struct kevent& event); | 122 static void ReleaseEvent(struct kevent& event); |
123 | 123 |
124 // Returns a file descriptor that will not block the system from deleting | 124 // Returns a file descriptor that will not block the system from deleting |
125 // the file it references. | 125 // the file it references. |
126 static int FileDescriptorForPath(const FilePath& path); | 126 static uintptr_t FileDescriptorForPath(const FilePath& path); |
| 127 |
| 128 static const uintptr_t kNoFileDescriptor = static_cast<uintptr_t>(-1); |
127 | 129 |
128 // Closes |*fd| and sets |*fd| to -1. | 130 // Closes |*fd| and sets |*fd| to -1. |
129 static void CloseFileDescriptor(int* fd); | 131 static void CloseFileDescriptor(uintptr_t* fd); |
130 | 132 |
131 // Returns true if kevent has open file descriptor. | 133 // Returns true if kevent has open file descriptor. |
132 static bool IsKeventFileDescriptorOpen(const struct kevent& event) { | 134 static bool IsKeventFileDescriptorOpen(const struct kevent& event) { |
133 return event.ident != static_cast<uintptr_t>(-1); | 135 return event.ident != kNoFileDescriptor; |
134 } | 136 } |
135 | 137 |
136 static EventData* EventDataForKevent(const struct kevent& event) { | 138 static EventData* EventDataForKevent(const struct kevent& event) { |
137 return reinterpret_cast<EventData*>(event.udata); | 139 return reinterpret_cast<EventData*>(event.udata); |
138 } | 140 } |
139 | 141 |
140 EventVector events_; | 142 EventVector events_; |
141 scoped_refptr<base::MessageLoopProxy> io_message_loop_; | 143 scoped_refptr<base::MessageLoopProxy> io_message_loop_; |
142 MessageLoopForIO::FileDescriptorWatcher kqueue_watcher_; | 144 MessageLoopForIO::FileDescriptorWatcher kqueue_watcher_; |
143 FilePathWatcher::Callback callback_; | 145 FilePathWatcher::Callback callback_; |
144 FilePath target_; | 146 FilePath target_; |
145 int kqueue_; | 147 int kqueue_; |
146 | 148 |
147 DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl); | 149 DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl); |
148 }; | 150 }; |
149 | 151 |
150 void FilePathWatcherImpl::ReleaseEvent(struct kevent& event) { | 152 void FilePathWatcherImpl::ReleaseEvent(struct kevent& event) { |
151 CloseFileDescriptor(reinterpret_cast<int*>(&event.ident)); | 153 CloseFileDescriptor(&event.ident); |
152 EventData* entry = EventDataForKevent(event); | 154 EventData* entry = EventDataForKevent(event); |
153 delete entry; | 155 delete entry; |
154 event.udata = NULL; | 156 event.udata = NULL; |
155 } | 157 } |
156 | 158 |
157 int FilePathWatcherImpl::EventsForPath(FilePath path, EventVector* events) { | 159 int FilePathWatcherImpl::EventsForPath(FilePath path, EventVector* events) { |
158 DCHECK(MessageLoopForIO::current()); | 160 DCHECK(MessageLoopForIO::current()); |
159 // Make sure that we are working with a clean slate. | 161 // Make sure that we are working with a clean slate. |
160 DCHECK(events->empty()); | 162 DCHECK(events->empty()); |
161 | 163 |
162 std::vector<FilePath::StringType> components; | 164 std::vector<FilePath::StringType> components; |
163 path.GetComponents(&components); | 165 path.GetComponents(&components); |
164 | 166 |
165 if (components.size() < 1) { | 167 if (components.size() < 1) { |
166 return -1; | 168 return -1; |
167 } | 169 } |
168 | 170 |
169 int last_existing_entry = 0; | 171 int last_existing_entry = 0; |
170 FilePath built_path; | 172 FilePath built_path; |
171 bool path_still_exists = true; | 173 bool path_still_exists = true; |
172 for(std::vector<FilePath::StringType>::iterator i = components.begin(); | 174 for(std::vector<FilePath::StringType>::iterator i = components.begin(); |
173 i != components.end(); ++i) { | 175 i != components.end(); ++i) { |
174 if (i == components.begin()) { | 176 if (i == components.begin()) { |
175 built_path = FilePath(*i); | 177 built_path = FilePath(*i); |
176 } else { | 178 } else { |
177 built_path = built_path.Append(*i); | 179 built_path = built_path.Append(*i); |
178 } | 180 } |
179 int fd = -1; | 181 uintptr_t fd = kNoFileDescriptor; |
180 if (path_still_exists) { | 182 if (path_still_exists) { |
181 fd = FileDescriptorForPath(built_path); | 183 fd = FileDescriptorForPath(built_path); |
182 if (fd == -1) { | 184 if (fd == kNoFileDescriptor) { |
183 path_still_exists = false; | 185 path_still_exists = false; |
184 } else { | 186 } else { |
185 ++last_existing_entry; | 187 ++last_existing_entry; |
186 } | 188 } |
187 } | 189 } |
188 FilePath::StringType subdir = (i != (components.end() - 1)) ? *(i + 1) : ""; | 190 FilePath::StringType subdir = (i != (components.end() - 1)) ? *(i + 1) : ""; |
189 EventData* data = new EventData(built_path, subdir); | 191 EventData* data = new EventData(built_path, subdir); |
190 struct kevent event; | 192 struct kevent event; |
191 EV_SET(&event, fd, EVFILT_VNODE, (EV_ADD | EV_CLEAR | EV_RECEIPT), | 193 EV_SET(&event, fd, EVFILT_VNODE, (EV_ADD | EV_CLEAR | EV_RECEIPT), |
192 (NOTE_DELETE | NOTE_WRITE | NOTE_ATTRIB | | 194 (NOTE_DELETE | NOTE_WRITE | NOTE_ATTRIB | |
193 NOTE_RENAME | NOTE_REVOKE | NOTE_EXTEND), 0, data); | 195 NOTE_RENAME | NOTE_REVOKE | NOTE_EXTEND), 0, data); |
194 events->push_back(event); | 196 events->push_back(event); |
195 } | 197 } |
196 return last_existing_entry; | 198 return last_existing_entry; |
197 } | 199 } |
198 | 200 |
199 int FilePathWatcherImpl::FileDescriptorForPath(const FilePath& path) { | 201 uintptr_t FilePathWatcherImpl::FileDescriptorForPath(const FilePath& path) { |
200 return HANDLE_EINTR(open(path.value().c_str(), O_EVTONLY)); | 202 int fd = HANDLE_EINTR(open(path.value().c_str(), O_EVTONLY)); |
| 203 if (fd == -1) |
| 204 return kNoFileDescriptor; |
| 205 return fd; |
201 } | 206 } |
202 | 207 |
203 void FilePathWatcherImpl::CloseFileDescriptor(int *fd) { | 208 void FilePathWatcherImpl::CloseFileDescriptor(uintptr_t* fd) { |
204 if (*fd == -1) { | 209 if (*fd == kNoFileDescriptor) { |
205 return; | 210 return; |
206 } | 211 } |
207 | 212 |
208 if (HANDLE_EINTR(close(*fd)) != 0) { | 213 if (HANDLE_EINTR(close(*fd)) != 0) { |
209 DPLOG(ERROR) << "close"; | 214 DPLOG(ERROR) << "close"; |
210 } | 215 } |
211 *fd = -1; | 216 *fd = kNoFileDescriptor; |
212 } | 217 } |
213 | 218 |
214 bool FilePathWatcherImpl::AreKeventValuesValid(struct kevent* kevents, | 219 bool FilePathWatcherImpl::AreKeventValuesValid(struct kevent* kevents, |
215 int count) { | 220 int count) { |
216 if (count < 0) { | 221 if (count < 0) { |
217 DPLOG(ERROR) << "kevent"; | 222 DPLOG(ERROR) << "kevent"; |
218 return false; | 223 return false; |
219 } | 224 } |
220 bool valid = true; | 225 bool valid = true; |
221 for (int i = 0; i < count; ++i) { | 226 for (int i = 0; i < count; ++i) { |
222 if (kevents[i].flags & EV_ERROR && kevents[i].data) { | 227 if (kevents[i].flags & EV_ERROR && kevents[i].data) { |
223 // Find the kevent in |events_| that matches the kevent with the error. | 228 // Find the kevent in |events_| that matches the kevent with the error. |
224 EventVector::iterator event = events_.begin(); | 229 EventVector::iterator event = events_.begin(); |
225 for (; event != events_.end(); ++event) { | 230 for (; event != events_.end(); ++event) { |
226 if (event->ident == kevents[i].ident) { | 231 if (event->ident == kevents[i].ident) { |
227 break; | 232 break; |
228 } | 233 } |
229 } | 234 } |
230 std::string path_name; | 235 std::string path_name; |
231 if (event != events_.end()) { | 236 if (event != events_.end()) { |
232 EventData* event_data = EventDataForKevent(*event); | 237 EventData* event_data = EventDataForKevent(*event); |
233 if (event_data != NULL) { | 238 if (event_data != NULL) { |
234 path_name = event_data->path_.value(); | 239 path_name = event_data->path_.value(); |
235 } | 240 } |
236 } | 241 } |
237 if (path_name.empty()) { | 242 if (path_name.empty()) { |
238 path_name = base::StringPrintf( | 243 path_name = base::StringPrintf( |
239 "fd %d", *reinterpret_cast<int*>(&kevents[i].ident)); | 244 "fd %ld", reinterpret_cast<long>(&kevents[i].ident)); |
240 } | 245 } |
241 DLOG(ERROR) << "Error: " << kevents[i].data << " for " << path_name; | 246 DLOG(ERROR) << "Error: " << kevents[i].data << " for " << path_name; |
242 valid = false; | 247 valid = false; |
243 } | 248 } |
244 } | 249 } |
245 return valid; | 250 return valid; |
246 } | 251 } |
247 | 252 |
248 void FilePathWatcherImpl::HandleAttributesChange( | 253 void FilePathWatcherImpl::HandleAttributesChange( |
249 const EventVector::iterator& event, | 254 const EventVector::iterator& event, |
250 bool* target_file_affected, | 255 bool* target_file_affected, |
251 bool* update_watches) { | 256 bool* update_watches) { |
252 EventVector::iterator next_event = event + 1; | 257 EventVector::iterator next_event = event + 1; |
253 EventData* next_event_data = EventDataForKevent(*next_event); | 258 EventData* next_event_data = EventDataForKevent(*next_event); |
254 // Check to see if the next item in path is still accessible. | 259 // Check to see if the next item in path is still accessible. |
255 int have_access = FileDescriptorForPath(next_event_data->path_); | 260 uintptr_t have_access = FileDescriptorForPath(next_event_data->path_); |
256 if (have_access == -1) { | 261 if (have_access == kNoFileDescriptor) { |
257 *target_file_affected = true; | 262 *target_file_affected = true; |
258 *update_watches = true; | 263 *update_watches = true; |
259 EventVector::iterator local_event(event); | 264 EventVector::iterator local_event(event); |
260 for (; local_event != events_.end(); ++local_event) { | 265 for (; local_event != events_.end(); ++local_event) { |
261 // Close all nodes from the event down. This has the side effect of | 266 // Close all nodes from the event down. This has the side effect of |
262 // potentially rendering other events in |updates| invalid. | 267 // potentially rendering other events in |updates| invalid. |
263 // There is no need to remove the events from |kqueue_| because this | 268 // There is no need to remove the events from |kqueue_| because this |
264 // happens as a side effect of closing the file descriptor. | 269 // happens as a side effect of closing the file descriptor. |
265 CloseFileDescriptor(reinterpret_cast<int*>(&local_event->ident)); | 270 CloseFileDescriptor(&local_event->ident); |
266 } | 271 } |
267 } else { | 272 } else { |
268 CloseFileDescriptor(&have_access); | 273 CloseFileDescriptor(&have_access); |
269 } | 274 } |
270 } | 275 } |
271 | 276 |
272 void FilePathWatcherImpl::HandleDeleteOrMoveChange( | 277 void FilePathWatcherImpl::HandleDeleteOrMoveChange( |
273 const EventVector::iterator& event, | 278 const EventVector::iterator& event, |
274 bool* target_file_affected, | 279 bool* target_file_affected, |
275 bool* update_watches) { | 280 bool* update_watches) { |
276 *target_file_affected = true; | 281 *target_file_affected = true; |
277 *update_watches = true; | 282 *update_watches = true; |
278 EventVector::iterator local_event(event); | 283 EventVector::iterator local_event(event); |
279 for (; local_event != events_.end(); ++local_event) { | 284 for (; local_event != events_.end(); ++local_event) { |
280 // Close all nodes from the event down. This has the side effect of | 285 // Close all nodes from the event down. This has the side effect of |
281 // potentially rendering other events in |updates| invalid. | 286 // potentially rendering other events in |updates| invalid. |
282 // There is no need to remove the events from |kqueue_| because this | 287 // There is no need to remove the events from |kqueue_| because this |
283 // happens as a side effect of closing the file descriptor. | 288 // happens as a side effect of closing the file descriptor. |
284 CloseFileDescriptor(reinterpret_cast<int*>(&local_event->ident)); | 289 CloseFileDescriptor(&local_event->ident); |
285 } | 290 } |
286 } | 291 } |
287 | 292 |
288 void FilePathWatcherImpl::HandleCreateItemChange( | 293 void FilePathWatcherImpl::HandleCreateItemChange( |
289 const EventVector::iterator& event, | 294 const EventVector::iterator& event, |
290 bool* target_file_affected, | 295 bool* target_file_affected, |
291 bool* update_watches) { | 296 bool* update_watches) { |
292 // Get the next item in the path. | 297 // Get the next item in the path. |
293 EventVector::iterator next_event = event + 1; | 298 EventVector::iterator next_event = event + 1; |
294 EventData* next_event_data = EventDataForKevent(*next_event); | |
295 | |
296 // Check to see if it already has a valid file descriptor. | 299 // Check to see if it already has a valid file descriptor. |
297 if (!IsKeventFileDescriptorOpen(*next_event)) { | 300 if (!IsKeventFileDescriptorOpen(*next_event)) { |
| 301 EventData* next_event_data = EventDataForKevent(*next_event); |
298 // If not, attempt to open a file descriptor for it. | 302 // If not, attempt to open a file descriptor for it. |
299 next_event->ident = FileDescriptorForPath(next_event_data->path_); | 303 next_event->ident = FileDescriptorForPath(next_event_data->path_); |
300 if (IsKeventFileDescriptorOpen(*next_event)) { | 304 if (IsKeventFileDescriptorOpen(*next_event)) { |
301 *update_watches = true; | 305 *update_watches = true; |
302 if (next_event_data->subdir_.empty()) { | 306 if (next_event_data->subdir_.empty()) { |
303 *target_file_affected = true; | 307 *target_file_affected = true; |
304 } | 308 } |
305 } | 309 } |
306 } | 310 } |
307 } | 311 } |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
374 // Iterate through each of the updates and react to them. | 378 // Iterate through each of the updates and react to them. |
375 for (int i = 0; i < count; ++i) { | 379 for (int i = 0; i < count; ++i) { |
376 // Find our kevent record that matches the update notification. | 380 // Find our kevent record that matches the update notification. |
377 EventVector::iterator event = events_.begin(); | 381 EventVector::iterator event = events_.begin(); |
378 for (; event != events_.end(); ++event) { | 382 for (; event != events_.end(); ++event) { |
379 if (!IsKeventFileDescriptorOpen(*event) || | 383 if (!IsKeventFileDescriptorOpen(*event) || |
380 event->ident == updates[i].ident) { | 384 event->ident == updates[i].ident) { |
381 break; | 385 break; |
382 } | 386 } |
383 } | 387 } |
384 if (!IsKeventFileDescriptorOpen(*event) || event == events_.end()) { | 388 if (event == events_.end() || !IsKeventFileDescriptorOpen(*event)) { |
385 // The event may no longer exist in |events_| because another event | 389 // The event may no longer exist in |events_| because another event |
386 // modified |events_| in such a way to make it invalid. For example if | 390 // modified |events_| in such a way to make it invalid. For example if |
387 // the path is /foo/bar/bam and foo is deleted, NOTE_DELETE events for | 391 // the path is /foo/bar/bam and foo is deleted, NOTE_DELETE events for |
388 // foo, bar and bam will be sent. If foo is processed first, then | 392 // foo, bar and bam will be sent. If foo is processed first, then |
389 // the file descriptors for bar and bam will already be closed and set | 393 // the file descriptors for bar and bam will already be closed and set |
390 // to -1 before they get a chance to be processed. | 394 // to -1 before they get a chance to be processed. |
391 continue; | 395 continue; |
392 } | 396 } |
393 | 397 |
394 EventData* event_data = EventDataForKevent(*event); | 398 EventData* event_data = EventDataForKevent(*event); |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
486 return; | 490 return; |
487 } | 491 } |
488 CancelOnMessageLoopThread(); | 492 CancelOnMessageLoopThread(); |
489 } | 493 } |
490 | 494 |
491 void FilePathWatcherImpl::CancelOnMessageLoopThread() { | 495 void FilePathWatcherImpl::CancelOnMessageLoopThread() { |
492 DCHECK(MessageLoopForIO::current()); | 496 DCHECK(MessageLoopForIO::current()); |
493 if (!is_cancelled()) { | 497 if (!is_cancelled()) { |
494 set_cancelled(); | 498 set_cancelled(); |
495 kqueue_watcher_.StopWatchingFileDescriptor(); | 499 kqueue_watcher_.StopWatchingFileDescriptor(); |
496 CloseFileDescriptor(&kqueue_); | 500 if (HANDLE_EINTR(close(kqueue_)) != 0) { |
| 501 DPLOG(ERROR) << "close kqueue"; |
| 502 } |
| 503 kqueue_ = -1; |
497 std::for_each(events_.begin(), events_.end(), ReleaseEvent); | 504 std::for_each(events_.begin(), events_.end(), ReleaseEvent); |
498 events_.clear(); | 505 events_.clear(); |
499 io_message_loop_ = NULL; | 506 io_message_loop_ = NULL; |
500 MessageLoop::current()->RemoveDestructionObserver(this); | 507 MessageLoop::current()->RemoveDestructionObserver(this); |
501 callback_.Reset(); | 508 callback_.Reset(); |
502 } | 509 } |
503 } | 510 } |
504 | 511 |
505 } // namespace | 512 } // namespace |
506 | 513 |
507 FilePathWatcher::FilePathWatcher() { | 514 FilePathWatcher::FilePathWatcher() { |
508 impl_ = new FilePathWatcherImpl(); | 515 impl_ = new FilePathWatcherImpl(); |
509 } | 516 } |
510 | 517 |
511 } // namespace base | 518 } // namespace base |
OLD | NEW |