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_kqueue.h" 6 7 #include <fcntl.h> 8 #include <stddef.h> 9 #include <sys/param.h> 10 11 #include "base/bind.h" 12 #include "base/files/file_util.h" 13 #include "base/logging.h" 14 #include "base/strings/stringprintf.h" 15 #include "base/threading/thread_task_runner_handle.h" 16 17 // On some platforms these are not defined. 18 #if !defined(EV_RECEIPT) 19 #define EV_RECEIPT 0 20 #endif 21 #if !defined(O_EVTONLY) 22 #define O_EVTONLY O_RDONLY 23 #endif 24 25 namespace base { 26 27 FilePathWatcherKQueue::FilePathWatcherKQueue() : kqueue_(-1) {} 28 29 FilePathWatcherKQueue::~FilePathWatcherKQueue() {} 30 31 void FilePathWatcherKQueue::ReleaseEvent(struct kevent& event) { 32 CloseFileDescriptor(&event.ident); 33 EventData* entry = EventDataForKevent(event); 34 delete entry; 35 event.udata = NULL; 36 } 37 38 int FilePathWatcherKQueue::EventsForPath(FilePath path, EventVector* events) { 39 DCHECK(MessageLoopForIO::current()); 40 // Make sure that we are working with a clean slate. 41 DCHECK(events->empty()); 42 43 std::vector<FilePath::StringType> components; 44 path.GetComponents(&components); 45 46 if (components.size() < 1) { 47 return -1; 48 } 49 50 int last_existing_entry = 0; 51 FilePath built_path; 52 bool path_still_exists = true; 53 for (std::vector<FilePath::StringType>::iterator i = components.begin(); 54 i != components.end(); ++i) { 55 if (i == components.begin()) { 56 built_path = FilePath(*i); 57 } else { 58 built_path = built_path.Append(*i); 59 } 60 uintptr_t fd = kNoFileDescriptor; 61 if (path_still_exists) { 62 fd = FileDescriptorForPath(built_path); 63 if (fd == kNoFileDescriptor) { 64 path_still_exists = false; 65 } else { 66 ++last_existing_entry; 67 } 68 } 69 FilePath::StringType subdir = (i != (components.end() - 1)) ? *(i + 1) : ""; 70 EventData* data = new EventData(built_path, subdir); 71 struct kevent event; 72 EV_SET(&event, fd, EVFILT_VNODE, (EV_ADD | EV_CLEAR | EV_RECEIPT), 73 (NOTE_DELETE | NOTE_WRITE | NOTE_ATTRIB | 74 NOTE_RENAME | NOTE_REVOKE | NOTE_EXTEND), 0, data); 75 events->push_back(event); 76 } 77 return last_existing_entry; 78 } 79 80 uintptr_t FilePathWatcherKQueue::FileDescriptorForPath(const FilePath& path) { 81 int fd = HANDLE_EINTR(open(path.value().c_str(), O_EVTONLY)); 82 if (fd == -1) 83 return kNoFileDescriptor; 84 return fd; 85 } 86 87 void FilePathWatcherKQueue::CloseFileDescriptor(uintptr_t* fd) { 88 if (*fd == kNoFileDescriptor) { 89 return; 90 } 91 92 if (IGNORE_EINTR(close(*fd)) != 0) { 93 DPLOG(ERROR) << "close"; 94 } 95 *fd = kNoFileDescriptor; 96 } 97 98 bool FilePathWatcherKQueue::AreKeventValuesValid(struct kevent* kevents, 99 int count) { 100 if (count < 0) { 101 DPLOG(ERROR) << "kevent"; 102 return false; 103 } 104 bool valid = true; 105 for (int i = 0; i < count; ++i) { 106 if (kevents[i].flags & EV_ERROR && kevents[i].data) { 107 // Find the kevent in |events_| that matches the kevent with the error. 108 EventVector::iterator event = events_.begin(); 109 for (; event != events_.end(); ++event) { 110 if (event->ident == kevents[i].ident) { 111 break; 112 } 113 } 114 std::string path_name; 115 if (event != events_.end()) { 116 EventData* event_data = EventDataForKevent(*event); 117 if (event_data != NULL) { 118 path_name = event_data->path_.value(); 119 } 120 } 121 if (path_name.empty()) { 122 path_name = base::StringPrintf( 123 "fd %ld", reinterpret_cast<long>(&kevents[i].ident)); 124 } 125 DLOG(ERROR) << "Error: " << kevents[i].data << " for " << path_name; 126 valid = false; 127 } 128 } 129 return valid; 130 } 131 132 void FilePathWatcherKQueue::HandleAttributesChange( 133 const EventVector::iterator& event, 134 bool* target_file_affected, 135 bool* update_watches) { 136 EventVector::iterator next_event = event + 1; 137 EventData* next_event_data = EventDataForKevent(*next_event); 138 // Check to see if the next item in path is still accessible. 139 uintptr_t have_access = FileDescriptorForPath(next_event_data->path_); 140 if (have_access == kNoFileDescriptor) { 141 *target_file_affected = true; 142 *update_watches = true; 143 EventVector::iterator local_event(event); 144 for (; local_event != events_.end(); ++local_event) { 145 // Close all nodes from the event down. This has the side effect of 146 // potentially rendering other events in |updates| invalid. 147 // There is no need to remove the events from |kqueue_| because this 148 // happens as a side effect of closing the file descriptor. 149 CloseFileDescriptor(&local_event->ident); 150 } 151 } else { 152 CloseFileDescriptor(&have_access); 153 } 154 } 155 156 void FilePathWatcherKQueue::HandleDeleteOrMoveChange( 157 const EventVector::iterator& event, 158 bool* target_file_affected, 159 bool* update_watches) { 160 *target_file_affected = true; 161 *update_watches = true; 162 EventVector::iterator local_event(event); 163 for (; local_event != events_.end(); ++local_event) { 164 // Close all nodes from the event down. This has the side effect of 165 // potentially rendering other events in |updates| invalid. 166 // There is no need to remove the events from |kqueue_| because this 167 // happens as a side effect of closing the file descriptor. 168 CloseFileDescriptor(&local_event->ident); 169 } 170 } 171 172 void FilePathWatcherKQueue::HandleCreateItemChange( 173 const EventVector::iterator& event, 174 bool* target_file_affected, 175 bool* update_watches) { 176 // Get the next item in the path. 177 EventVector::iterator next_event = event + 1; 178 // Check to see if it already has a valid file descriptor. 179 if (!IsKeventFileDescriptorOpen(*next_event)) { 180 EventData* next_event_data = EventDataForKevent(*next_event); 181 // If not, attempt to open a file descriptor for it. 182 next_event->ident = FileDescriptorForPath(next_event_data->path_); 183 if (IsKeventFileDescriptorOpen(*next_event)) { 184 *update_watches = true; 185 if (next_event_data->subdir_.empty()) { 186 *target_file_affected = true; 187 } 188 } 189 } 190 } 191 192 bool FilePathWatcherKQueue::UpdateWatches(bool* target_file_affected) { 193 // Iterate over events adding kevents for items that exist to the kqueue. 194 // Then check to see if new components in the path have been created. 195 // Repeat until no new components in the path are detected. 196 // This is to get around races in directory creation in a watched path. 197 bool update_watches = true; 198 while (update_watches) { 199 size_t valid; 200 for (valid = 0; valid < events_.size(); ++valid) { 201 if (!IsKeventFileDescriptorOpen(events_[valid])) { 202 break; 203 } 204 } 205 if (valid == 0) { 206 // The root of the file path is inaccessible? 207 return false; 208 } 209 210 EventVector updates(valid); 211 int count = HANDLE_EINTR(kevent(kqueue_, &events_[0], valid, &updates[0], 212 valid, NULL)); 213 if (!AreKeventValuesValid(&updates[0], count)) { 214 return false; 215 } 216 update_watches = false; 217 for (; valid < events_.size(); ++valid) { 218 EventData* event_data = EventDataForKevent(events_[valid]); 219 events_[valid].ident = FileDescriptorForPath(event_data->path_); 220 if (IsKeventFileDescriptorOpen(events_[valid])) { 221 update_watches = true; 222 if (event_data->subdir_.empty()) { 223 *target_file_affected = true; 224 } 225 } else { 226 break; 227 } 228 } 229 } 230 return true; 231 } 232 233 void FilePathWatcherKQueue::OnFileCanReadWithoutBlocking(int fd) { 234 DCHECK(MessageLoopForIO::current()); 235 DCHECK_EQ(fd, kqueue_); 236 DCHECK(events_.size()); 237 238 // Request the file system update notifications that have occurred and return 239 // them in |updates|. |count| will contain the number of updates that have 240 // occurred. 241 EventVector updates(events_.size()); 242 struct timespec timeout = {0, 0}; 243 int count = HANDLE_EINTR(kevent(kqueue_, NULL, 0, &updates[0], updates.size(), 244 &timeout)); 245 246 // Error values are stored within updates, so check to make sure that no 247 // errors occurred. 248 if (!AreKeventValuesValid(&updates[0], count)) { 249 callback_.Run(target_, true /* error */); 250 Cancel(); 251 return; 252 } 253 254 bool update_watches = false; 255 bool send_notification = false; 256 257 // Iterate through each of the updates and react to them. 258 for (int i = 0; i < count; ++i) { 259 // Find our kevent record that matches the update notification. 260 EventVector::iterator event = events_.begin(); 261 for (; event != events_.end(); ++event) { 262 if (!IsKeventFileDescriptorOpen(*event) || 263 event->ident == updates[i].ident) { 264 break; 265 } 266 } 267 if (event == events_.end() || !IsKeventFileDescriptorOpen(*event)) { 268 // The event may no longer exist in |events_| because another event 269 // modified |events_| in such a way to make it invalid. For example if 270 // the path is /foo/bar/bam and foo is deleted, NOTE_DELETE events for 271 // foo, bar and bam will be sent. If foo is processed first, then 272 // the file descriptors for bar and bam will already be closed and set 273 // to -1 before they get a chance to be processed. 274 continue; 275 } 276 277 EventData* event_data = EventDataForKevent(*event); 278 279 // If the subdir is empty, this is the last item on the path and is the 280 // target file. 281 bool target_file_affected = event_data->subdir_.empty(); 282 if ((updates[i].fflags & NOTE_ATTRIB) && !target_file_affected) { 283 HandleAttributesChange(event, &target_file_affected, &update_watches); 284 } 285 if (updates[i].fflags & (NOTE_DELETE | NOTE_REVOKE | NOTE_RENAME)) { 286 HandleDeleteOrMoveChange(event, &target_file_affected, &update_watches); 287 } 288 if ((updates[i].fflags & NOTE_WRITE) && !target_file_affected) { 289 HandleCreateItemChange(event, &target_file_affected, &update_watches); 290 } 291 send_notification |= target_file_affected; 292 } 293 294 if (update_watches) { 295 if (!UpdateWatches(&send_notification)) { 296 callback_.Run(target_, true /* error */); 297 Cancel(); 298 } 299 } 300 301 if (send_notification) { 302 callback_.Run(target_, false); 303 } 304 } 305 306 void FilePathWatcherKQueue::OnFileCanWriteWithoutBlocking(int /* fd */) { 307 NOTREACHED(); 308 } 309 310 void FilePathWatcherKQueue::WillDestroyCurrentMessageLoop() { 311 CancelOnMessageLoopThread(); 312 } 313 314 bool FilePathWatcherKQueue::Watch(const FilePath& path, 315 bool recursive, 316 const FilePathWatcher::Callback& callback) { 317 DCHECK(MessageLoopForIO::current()); 318 DCHECK(target_.value().empty()); // Can only watch one path. 319 DCHECK(!callback.is_null()); 320 DCHECK_EQ(kqueue_, -1); 321 322 if (recursive) { 323 // Recursive watch is not supported using kqueue. 324 NOTIMPLEMENTED(); 325 return false; 326 } 327 328 callback_ = callback; 329 target_ = path; 330 331 MessageLoop::current()->AddDestructionObserver(this); 332 io_task_runner_ = ThreadTaskRunnerHandle::Get(); 333 334 kqueue_ = kqueue(); 335 if (kqueue_ == -1) { 336 DPLOG(ERROR) << "kqueue"; 337 return false; 338 } 339 340 int last_entry = EventsForPath(target_, &events_); 341 DCHECK_NE(last_entry, 0); 342 343 EventVector responses(last_entry); 344 345 int count = HANDLE_EINTR(kevent(kqueue_, &events_[0], last_entry, 346 &responses[0], last_entry, NULL)); 347 if (!AreKeventValuesValid(&responses[0], count)) { 348 // Calling Cancel() here to close any file descriptors that were opened. 349 // This would happen in the destructor anyways, but FilePathWatchers tend to 350 // be long lived, and if an error has occurred, there is no reason to waste 351 // the file descriptors. 352 Cancel(); 353 return false; 354 } 355 356 return MessageLoopForIO::current()->WatchFileDescriptor( 357 kqueue_, true, MessageLoopForIO::WATCH_READ, &kqueue_watcher_, this); 358 } 359 360 void FilePathWatcherKQueue::Cancel() { 361 SingleThreadTaskRunner* task_runner = io_task_runner_.get(); 362 if (!task_runner) { 363 set_cancelled(); 364 return; 365 } 366 if (!task_runner->BelongsToCurrentThread()) { 367 task_runner->PostTask(FROM_HERE, 368 base::Bind(&FilePathWatcherKQueue::Cancel, this)); 369 return; 370 } 371 CancelOnMessageLoopThread(); 372 } 373 374 void FilePathWatcherKQueue::CancelOnMessageLoopThread() { 375 DCHECK(MessageLoopForIO::current()); 376 if (!is_cancelled()) { 377 set_cancelled(); 378 kqueue_watcher_.StopWatchingFileDescriptor(); 379 if (IGNORE_EINTR(close(kqueue_)) != 0) { 380 DPLOG(ERROR) << "close kqueue"; 381 } 382 kqueue_ = -1; 383 std::for_each(events_.begin(), events_.end(), ReleaseEvent); 384 events_.clear(); 385 io_task_runner_ = NULL; 386 MessageLoop::current()->RemoveDestructionObserver(this); 387 callback_.Reset(); 388 } 389 } 390 391 } // namespace base 392