| Index: base/file_path_component_watcher_mac.cc
|
| diff --git a/base/file_path_component_watcher_mac.cc b/base/file_path_component_watcher_mac.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..e6c91dd5515f180631e2e30641a4a477c4fd2fcf
|
| --- /dev/null
|
| +++ b/base/file_path_component_watcher_mac.cc
|
| @@ -0,0 +1,178 @@
|
| +// Copyright (c) 2010 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "base/file_path_component_watcher.h"
|
| +
|
| +#include <CoreFoundation/CoreFoundation.h>
|
| +#include <fcntl.h>
|
| +#include <sys/event.h>
|
| +#include <sys/param.h>
|
| +#include <vector>
|
| +
|
| +#include "base/file_util.h"
|
| +#include "base/mac/scoped_cftyperef.h"
|
| +
|
| +namespace base {
|
| +
|
| +namespace {
|
| +
|
| +// Mac-specific file watcher implementation based on kqueue.
|
| +class FilePathComponentWatcherImpl
|
| + : public base::FilePathComponentWatcher::PlatformDelegate {
|
| + public:
|
| + FilePathComponentWatcherImpl() {}
|
| + virtual ~FilePathComponentWatcherImpl() {}
|
| +
|
| + void OnFilePathChanged(CFFileDescriptorRef fd);
|
| +
|
| + // FilePathComponentWatcher::PlatformDelegate overrides.
|
| + virtual bool Watch(const FilePath& path,
|
| + FilePathComponentWatcher::Delegate* delegate);
|
| + virtual void Cancel();
|
| +
|
| + private:
|
| + base::mac::ScopedCFTypeRef<CFFileDescriptorRef> kqueue_fd_;
|
| + std::vector<int> file_descriptors_;
|
| + scoped_refptr<FilePathComponentWatcher::Delegate> delegate_;
|
| + FilePath path_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(FilePathComponentWatcherImpl);
|
| +};
|
| +
|
| +void CloseFile(int fd) {
|
| + if(close(fd) != 0) {
|
| + PLOG(ERROR) << "close";
|
| + }
|
| +}
|
| +
|
| +void CloseFileDescriptors(std::vector<int>& descriptors) {
|
| + std::for_each(descriptors.begin(), descriptors.end(), CloseFile);
|
| + descriptors.clear();
|
| +}
|
| +
|
| +bool OpenFileDescriptorsForPath(FilePath path, std::vector<int>* descriptors) {
|
| + FilePath normalized_path;
|
| + if (!file_util::NormalizeFilePath(path, &normalized_path)) {
|
| + return false;
|
| + }
|
| +
|
| + std::vector<FilePath::StringType> components;
|
| + normalized_path.GetComponents(&components);
|
| +
|
| + if (components.size() < 1) {
|
| + return false;
|
| + }
|
| +
|
| + FilePath built_path(components[0]);
|
| + std::vector<int> local_descriptors;
|
| + for(std::vector<FilePath::StringType>::iterator i = components.begin() + 1;
|
| + i != components.end(); ++i) {
|
| + built_path = built_path.Append(*i);
|
| + int fd = open(built_path.value().c_str(), O_RDONLY);
|
| + if (fd == -1) {
|
| + PLOG(ERROR) << "open " << built_path.value();
|
| + CloseFileDescriptors(*descriptors);
|
| + descriptors->clear();
|
| + return false;
|
| + } else {
|
| + descriptors->push_back(fd);
|
| + }
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +void PathComponentWatcherTrampoline(CFFileDescriptorRef fd,
|
| + CFOptionFlags callBackTypes,
|
| + void *info) {
|
| + FilePathComponentWatcherImpl *impl =
|
| + reinterpret_cast<FilePathComponentWatcherImpl*>(info);
|
| + impl->OnFilePathChanged(fd);
|
| +}
|
| +
|
| +// FilePathComponentWatcherImpl implementation:
|
| +
|
| +void FilePathComponentWatcherImpl::OnFilePathChanged(CFFileDescriptorRef fd) {
|
| + int native_fd = CFFileDescriptorGetNativeDescriptor(fd);
|
| + struct kevent kev;
|
| + struct timespec timeout = {0, 0};
|
| + int status = kevent(native_fd, NULL, 0, &kev, 1, &timeout);
|
| + if (status < 0) {
|
| + PLOG(ERROR) << "kevent";
|
| + if (delegate_) {
|
| + delegate_->OnError();
|
| + }
|
| + } else if (delegate_) {
|
| + char path[MAXPATHLEN];
|
| + FilePath new_path;
|
| + int path_fd = file_descriptors_[file_descriptors_.size() - 1];
|
| + if (fcntl(path_fd, F_GETPATH, path) != -1) {
|
| + new_path = FilePath(path);
|
| + }
|
| + delegate_->OnFilePathComponentsChanged(path_, new_path);
|
| + }
|
| + Cancel();
|
| +}
|
| +
|
| +bool FilePathComponentWatcherImpl::Watch(
|
| + const FilePath& path, FilePathComponentWatcher::Delegate* delegate) {
|
| + delegate_ = delegate;
|
| + path_ = path;
|
| + int kq = kqueue();
|
| + if (kq == -1) {
|
| + PLOG(ERROR) << "kqueue";
|
| + return false;
|
| + }
|
| + if (!OpenFileDescriptorsForPath(path, &file_descriptors_)) {
|
| + LOG(ERROR) << "FileDescriptorsForPath: " << path.value();
|
| + return false;
|
| + }
|
| +
|
| + size_t count = file_descriptors_.size();
|
| +
|
| + scoped_ptr<struct kevent> changes(reinterpret_cast<struct kevent*>(
|
| + calloc(count, sizeof(struct kevent))));
|
| +
|
| + for(size_t i = 0; i != count; ++i) {
|
| + EV_SET(&(changes.get())[i], file_descriptors_[i], EVFILT_VNODE,
|
| + (EV_ADD | EV_CLEAR | EV_ENABLE),
|
| + (NOTE_RENAME | NOTE_DELETE | NOTE_ATTRIB | NOTE_REVOKE), 0, NULL);
|
| + }
|
| +
|
| + if (HANDLE_EINTR(kevent(kq, changes.get(), count, NULL, 0, NULL)) == -1) {
|
| + PLOG(ERROR) << "kevent";
|
| + return false;
|
| + }
|
| +
|
| + CFFileDescriptorContext context;
|
| + memset(&context, 0, sizeof(context));
|
| + context.info = this;
|
| +
|
| + kqueue_fd_.reset(
|
| + CFFileDescriptorCreate(kCFAllocatorDefault, kq, true,
|
| + PathComponentWatcherTrampoline, &context));
|
| + CFFileDescriptorEnableCallBacks(kqueue_fd_, kCFFileDescriptorReadCallBack);
|
| + base::mac::ScopedCFTypeRef<CFRunLoopSourceRef> source(
|
| + CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, kqueue_fd_, 0));
|
| + CFRunLoopAddSource(CFRunLoopGetMain(),
|
| + source,
|
| + kCFRunLoopDefaultMode);
|
| +
|
| + return true;
|
| +}
|
| +
|
| +void FilePathComponentWatcherImpl::Cancel() {
|
| + if (kqueue_fd_.get()) {
|
| + CFFileDescriptorInvalidate(kqueue_fd_);
|
| + kqueue_fd_.reset();
|
| + }
|
| + CloseFileDescriptors(file_descriptors_);
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +FilePathComponentWatcher::FilePathComponentWatcher() {
|
| + impl_ = new FilePathComponentWatcherImpl();
|
| +}
|
| +
|
| +} // namespace base
|
|
|