// Copyright 2016 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 "components/leveldb/leveldb_mojo_proxy.h" #include #include "base/bind.h" #include "base/callback.h" #include "base/single_thread_task_runner.h" #include "mojo/public/cpp/bindings/interface_request.h" #include "mojo/public/cpp/bindings/sync_call_restrictions.h" namespace leveldb { struct LevelDBMojoProxy::OpaqueLock { filesystem::mojom::FilePtr lock_file; }; struct LevelDBMojoProxy::OpaqueDir { explicit OpaqueDir( mojo::InterfacePtrInfo directory_info) { directory.Bind(std::move(directory_info)); } filesystem::mojom::DirectoryPtr directory; }; LevelDBMojoProxy::LevelDBMojoProxy( scoped_refptr task_runner) : task_runner_(std::move(task_runner)), outstanding_opaque_dirs_(0) { DCHECK(!task_runner_->BelongsToCurrentThread()); } LevelDBMojoProxy::OpaqueDir* LevelDBMojoProxy::RegisterDirectory( filesystem::mojom::DirectoryPtr directory) { OpaqueDir* out_dir = nullptr; RunInternal(base::Bind(&LevelDBMojoProxy::RegisterDirectoryImpl, this, base::Passed(directory.PassInterface()), &out_dir)); return out_dir; } void LevelDBMojoProxy::UnregisterDirectory(OpaqueDir* dir) { RunInternal(base::Bind(&LevelDBMojoProxy::UnregisterDirectoryImpl, this, dir)); } base::File LevelDBMojoProxy::OpenFileHandle(OpaqueDir* dir, const std::string& name, uint32_t open_flags) { base::File file; RunInternal(base::Bind(&LevelDBMojoProxy::OpenFileHandleImpl, this, dir, name, open_flags, &file)); return file; } filesystem::mojom::FileError LevelDBMojoProxy::SyncDirectory( OpaqueDir* dir, const std::string& name) { filesystem::mojom::FileError error = filesystem::mojom::FileError::FAILED; RunInternal(base::Bind(&LevelDBMojoProxy::SyncDirectoryImpl, this, dir, name, &error)); return error; } bool LevelDBMojoProxy::FileExists(OpaqueDir* dir, const std::string& name) { bool exists = false; RunInternal(base::Bind(&LevelDBMojoProxy::FileExistsImpl, this, dir, name, &exists)); return exists; } filesystem::mojom::FileError LevelDBMojoProxy::GetChildren( OpaqueDir* dir, const std::string& path, std::vector* result) { filesystem::mojom::FileError error = filesystem::mojom::FileError::FAILED; RunInternal(base::Bind(&LevelDBMojoProxy::GetChildrenImpl, this, dir, path, result, &error)); return error; } filesystem::mojom::FileError LevelDBMojoProxy::Delete(OpaqueDir* dir, const std::string& path, uint32_t delete_flags) { filesystem::mojom::FileError error = filesystem::mojom::FileError::FAILED; RunInternal(base::Bind(&LevelDBMojoProxy::DeleteImpl, this, dir, path, delete_flags, &error)); return error; } filesystem::mojom::FileError LevelDBMojoProxy::CreateDir( OpaqueDir* dir, const std::string& path) { filesystem::mojom::FileError error = filesystem::mojom::FileError::FAILED; RunInternal(base::Bind(&LevelDBMojoProxy::CreateDirImpl, this, dir, path, &error)); return error; } filesystem::mojom::FileError LevelDBMojoProxy::GetFileSize( OpaqueDir* dir, const std::string& path, uint64_t* file_size) { filesystem::mojom::FileError error = filesystem::mojom::FileError::FAILED; RunInternal(base::Bind(&LevelDBMojoProxy::GetFileSizeImpl, this, dir, path, file_size, &error)); return error; } filesystem::mojom::FileError LevelDBMojoProxy::RenameFile( OpaqueDir* dir, const std::string& old_path, const std::string& new_path) { filesystem::mojom::FileError error = filesystem::mojom::FileError::FAILED; RunInternal(base::Bind(&LevelDBMojoProxy::RenameFileImpl, this, dir, old_path, new_path, &error)); return error; } std::pair LevelDBMojoProxy::LockFile(OpaqueDir* dir, const std::string& path) { filesystem::mojom::FileError error = filesystem::mojom::FileError::FAILED; OpaqueLock* out_lock = nullptr; RunInternal(base::Bind(&LevelDBMojoProxy::LockFileImpl, this, dir, path, &error, &out_lock)); return std::make_pair(error, out_lock); } filesystem::mojom::FileError LevelDBMojoProxy::UnlockFile(OpaqueLock* lock) { // Take ownership of the incoming lock so it gets destroyed whatever happens. std::unique_ptr scoped_lock(lock); filesystem::mojom::FileError error = filesystem::mojom::FileError::FAILED; RunInternal(base::Bind(&LevelDBMojoProxy::UnlockFileImpl, this, base::Passed(&scoped_lock), &error)); return error; } LevelDBMojoProxy::~LevelDBMojoProxy() { DCHECK_EQ(0, outstanding_opaque_dirs_); } void LevelDBMojoProxy::RunInternal(const base::Closure& task) { if (task_runner_->BelongsToCurrentThread()) { task.Run(); } else { base::WaitableEvent done_event( base::WaitableEvent::ResetPolicy::AUTOMATIC, base::WaitableEvent::InitialState::NOT_SIGNALED); task_runner_->PostTask( FROM_HERE, base::Bind(&LevelDBMojoProxy::DoOnOtherThread, this, task, base::Unretained(&done_event))); done_event.Wait(); } } void LevelDBMojoProxy::DoOnOtherThread(const base::Closure& c, base::WaitableEvent* event) { c.Run(); event->Signal(); } void LevelDBMojoProxy::RegisterDirectoryImpl( mojo::InterfacePtrInfo directory_info, OpaqueDir** out_dir) { // Take the Directory pipe and bind it on this thread. *out_dir = new OpaqueDir(std::move(directory_info)); outstanding_opaque_dirs_++; } void LevelDBMojoProxy::UnregisterDirectoryImpl( OpaqueDir* dir) { // Only delete the directories on the thread that owns them. delete dir; outstanding_opaque_dirs_--; } void LevelDBMojoProxy::OpenFileHandleImpl(OpaqueDir* dir, std::string name, uint32_t open_flags, base::File* output_file) { mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync; base::File file; filesystem::mojom::FileError error = filesystem::mojom::FileError::FAILED; bool completed = dir->directory->OpenFileHandle(name, open_flags, &error, &file); DCHECK(completed); if (error != filesystem::mojom::FileError::OK) { *output_file = base::File(static_cast(error)); } else { *output_file = std::move(file); } } void LevelDBMojoProxy::SyncDirectoryImpl( OpaqueDir* dir, std::string name, filesystem::mojom::FileError* out_error) { mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync; filesystem::mojom::DirectoryPtr target; bool completed = dir->directory->OpenDirectory( name, MakeRequest(&target), filesystem::mojom::kFlagRead | filesystem::mojom::kFlagWrite, out_error); DCHECK(completed); if (*out_error != filesystem::mojom::FileError::OK) return; completed = target->Flush(out_error); DCHECK(completed); } void LevelDBMojoProxy::FileExistsImpl(OpaqueDir* dir, std::string name, bool* exists) { mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync; filesystem::mojom::FileError error = filesystem::mojom::FileError::FAILED; bool completed = dir->directory->Exists(name, &error, exists); DCHECK(completed); } void LevelDBMojoProxy::GetChildrenImpl( OpaqueDir* dir, std::string name, std::vector* out_contents, filesystem::mojom::FileError* out_error) { mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync; filesystem::mojom::DirectoryPtr target; bool completed = dir->directory->OpenDirectory( name, mojo::MakeRequest(&target), filesystem::mojom::kFlagRead | filesystem::mojom::kFlagWrite, out_error); DCHECK(completed); if (*out_error != filesystem::mojom::FileError::OK) return; base::Optional> directory_contents; completed = target->Read(out_error, &directory_contents); DCHECK(completed); if (directory_contents.has_value()) { for (size_t i = 0; i < directory_contents->size(); ++i) out_contents->push_back(directory_contents.value()[i]->name); } } void LevelDBMojoProxy::DeleteImpl(OpaqueDir* dir, std::string name, uint32_t delete_flags, filesystem::mojom::FileError* out_error) { mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync; bool completed = dir->directory->Delete(name, delete_flags, out_error); DCHECK(completed); } void LevelDBMojoProxy::CreateDirImpl(OpaqueDir* dir, std::string name, filesystem::mojom::FileError* out_error) { mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync; bool completed = dir->directory->OpenDirectory( name, nullptr, filesystem::mojom::kFlagRead | filesystem::mojom::kFlagWrite | filesystem::mojom::kFlagCreate, out_error); DCHECK(completed); } void LevelDBMojoProxy::GetFileSizeImpl( OpaqueDir* dir, const std::string& path, uint64_t* file_size, filesystem::mojom::FileError* out_error) { mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync; filesystem::mojom::FileInformationPtr info; bool completed = dir->directory->StatFile(path, out_error, &info); DCHECK(completed); if (info) *file_size = info->size; } void LevelDBMojoProxy::RenameFileImpl(OpaqueDir* dir, const std::string& old_path, const std::string& new_path, filesystem::mojom::FileError* out_error) { mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync; bool completed = dir->directory->Rename(old_path, new_path, out_error); DCHECK(completed); } void LevelDBMojoProxy::LockFileImpl(OpaqueDir* dir, const std::string& path, filesystem::mojom::FileError* out_error, OpaqueLock** out_lock) { mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync; // Since a lock is associated with a file descriptor, we need to open and // have a persistent file on the other side of the connection. filesystem::mojom::FilePtr target; bool completed = dir->directory->OpenFile(path, mojo::MakeRequest(&target), filesystem::mojom::kFlagOpenAlways | filesystem::mojom::kFlagRead | filesystem::mojom::kFlagWrite, out_error); DCHECK(completed); if (*out_error != filesystem::mojom::FileError::OK) return; completed = target->Lock(out_error); DCHECK(completed); if (*out_error == filesystem::mojom::FileError::OK) { OpaqueLock* l = new OpaqueLock; l->lock_file = std::move(target); *out_lock = l; } } void LevelDBMojoProxy::UnlockFileImpl(std::unique_ptr lock, filesystem::mojom::FileError* out_error) { mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync; lock->lock_file->Unlock(out_error); } } // namespace leveldb