|  | // Copyright 2016 Google Inc. All Rights Reserved. | 
|  | // | 
|  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | // you may not use this file except in compliance with the License. | 
|  | // You may obtain a copy of the License at | 
|  | // | 
|  | //     http://www.apache.org/licenses/LICENSE-2.0 | 
|  | // | 
|  | // Unless required by applicable law or agreed to in writing, software | 
|  | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | // See the License for the specific language governing permissions and | 
|  | // limitations under the License. | 
|  |  | 
|  | // Adapted from cobalt/storage. | 
|  |  | 
|  | #include "sql/test_vfs.h" | 
|  |  | 
|  | #include <string.h> | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <map> | 
|  |  | 
|  | #include "base/compiler_specific.h" | 
|  | #include "base/lazy_instance.h" | 
|  | #include "base/logging.h" | 
|  | #include "base/rand_util.h" | 
|  | #include "base/string_util.h" | 
|  | #include "base/synchronization/lock.h" | 
|  | #include "third_party/sqlite/sqlite3.h" | 
|  |  | 
|  | namespace sql { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // A "subclass" of sqlite3_file with our required data structures added. | 
|  | struct virtual_file { | 
|  | sqlite3_file sql_internal_file; | 
|  | std::string* data; | 
|  | base::Lock* lock; | 
|  | int current_lock; | 
|  | int shared; | 
|  | }; | 
|  |  | 
|  | // A very simple in-memory virtual file system. | 
|  | class TestVfs { | 
|  | public: | 
|  | typedef std::map<std::string, std::string> FileMap; | 
|  |  | 
|  | public: | 
|  | TestVfs() {} | 
|  | ~TestVfs() {} | 
|  |  | 
|  | void Register(); | 
|  | void Unregister(); | 
|  |  | 
|  | std::string* Open(const char* path) { | 
|  | return &file_map_[path]; | 
|  | } | 
|  |  | 
|  | void Delete(const char* path) { | 
|  | file_map_.erase(path); | 
|  | } | 
|  |  | 
|  | private: | 
|  | sqlite3_vfs vfs_; | 
|  | FileMap file_map_; | 
|  | }; | 
|  |  | 
|  | base::LazyInstance<TestVfs> g_vfs = LAZY_INSTANCE_INITIALIZER; | 
|  |  | 
|  | int VfsClose(sqlite3_file* file) { | 
|  | virtual_file* vfile = reinterpret_cast<virtual_file*>(file); | 
|  | delete vfile->lock; | 
|  | return SQLITE_OK; | 
|  | } | 
|  |  | 
|  | int VfsRead(sqlite3_file* file, void* out, int bytes, sqlite_int64 offset) { | 
|  | virtual_file* vfile = reinterpret_cast<virtual_file*>(file); | 
|  | base::AutoLock lock(*vfile->lock); | 
|  | if (offset >= static_cast<sqlite_int64>(vfile->data->length())) { | 
|  | return SQLITE_OK; | 
|  | } | 
|  |  | 
|  | size_t available = | 
|  | std::max(static_cast<sqlite_int64>(vfile->data->length()) - offset, | 
|  | static_cast<sqlite_int64>(0)); | 
|  | size_t to_read = std::min(available, static_cast<size_t>(bytes)); | 
|  | if (to_read == 0) { | 
|  | return SQLITE_OK; | 
|  | } | 
|  |  | 
|  | memcpy(out, &(vfile->data->c_str()[offset]), to_read); | 
|  | return SQLITE_OK; | 
|  | } | 
|  |  | 
|  | int VfsWrite(sqlite3_file* file, | 
|  | const void* data, | 
|  | int bytes, | 
|  | sqlite3_int64 offset) { | 
|  | size_t max = offset + bytes; | 
|  |  | 
|  | virtual_file* vfile = reinterpret_cast<virtual_file*>(file); | 
|  | base::AutoLock lock(*vfile->lock); | 
|  | if (vfile->data->length() < max) { | 
|  | vfile->data->resize(max); | 
|  | } | 
|  |  | 
|  | vfile->data->replace(offset, bytes, reinterpret_cast<const char*>(data), | 
|  | bytes); | 
|  |  | 
|  | return SQLITE_OK; | 
|  | } | 
|  |  | 
|  | int VfsSync(sqlite3_file* pFile, int flags) { | 
|  | UNREFERENCED_PARAMETER(pFile); | 
|  | UNREFERENCED_PARAMETER(flags); | 
|  | return SQLITE_OK; | 
|  | } | 
|  |  | 
|  | int VfsFileControl(sqlite3_file* pFile, int op, void* pArg) { | 
|  | UNREFERENCED_PARAMETER(pFile); | 
|  | UNREFERENCED_PARAMETER(op); | 
|  | UNREFERENCED_PARAMETER(pArg); | 
|  | return SQLITE_OK; | 
|  | } | 
|  |  | 
|  | int VfsSectorSize(sqlite3_file* file) { | 
|  | // The number of bytes that can be read without disturbing other bytes in the | 
|  | // file. | 
|  | UNREFERENCED_PARAMETER(file); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | int VfsLock(sqlite3_file* file, const int mode) { | 
|  | virtual_file* vfile = reinterpret_cast<virtual_file*>(file); | 
|  | base::AutoLock lock(*vfile->lock); | 
|  |  | 
|  | // If there is already a lock of this type or more restrictive, do nothing | 
|  | if (vfile->current_lock >= mode) { | 
|  | return SQLITE_OK; | 
|  | } | 
|  |  | 
|  | if (mode == SQLITE_LOCK_SHARED) { | 
|  | DCHECK_EQ(vfile->current_lock, SQLITE_LOCK_NONE); | 
|  | vfile->shared++; | 
|  | vfile->current_lock = SQLITE_LOCK_SHARED; | 
|  | } | 
|  |  | 
|  | if (mode == SQLITE_LOCK_RESERVED) { | 
|  | DCHECK_EQ(vfile->current_lock, SQLITE_LOCK_SHARED); | 
|  | vfile->current_lock = SQLITE_LOCK_RESERVED; | 
|  | } | 
|  |  | 
|  | if (mode == SQLITE_LOCK_EXCLUSIVE) { | 
|  | if (vfile->current_lock >= SQLITE_LOCK_PENDING) { | 
|  | return SQLITE_BUSY; | 
|  | } | 
|  | vfile->current_lock = SQLITE_LOCK_PENDING; | 
|  | if (vfile->shared > 1) { | 
|  | // There are some outstanding shared locks (greater than one because the | 
|  | // pending lock is an "upgraded" shared lock) | 
|  | return SQLITE_BUSY; | 
|  | } | 
|  | // Acquire the exclusive lock | 
|  | vfile->current_lock = SQLITE_LOCK_EXCLUSIVE; | 
|  | } | 
|  |  | 
|  | return SQLITE_OK; | 
|  | } | 
|  |  | 
|  | int VfsUnlock(sqlite3_file* file, int mode) { | 
|  | DCHECK_LE(mode, SQLITE_LOCK_SHARED); | 
|  | virtual_file* vfile = reinterpret_cast<virtual_file*>(file); | 
|  | base::AutoLock lock(*vfile->lock); | 
|  |  | 
|  | COMPILE_ASSERT(SQLITE_LOCK_NONE < SQLITE_LOCK_SHARED, | 
|  | sqlite_lock_constants_order_has_changed); | 
|  | COMPILE_ASSERT(SQLITE_LOCK_SHARED < SQLITE_LOCK_RESERVED, | 
|  | sqlite_lock_constants_order_has_changed); | 
|  | COMPILE_ASSERT(SQLITE_LOCK_RESERVED < SQLITE_LOCK_PENDING, | 
|  | sqlite_lock_constants_order_has_changed); | 
|  | COMPILE_ASSERT(SQLITE_LOCK_PENDING < SQLITE_LOCK_EXCLUSIVE, | 
|  | sqlite_lock_constants_order_has_changed); | 
|  |  | 
|  | if (mode == SQLITE_LOCK_NONE && vfile->current_lock >= SQLITE_LOCK_SHARED) { | 
|  | vfile->shared--; | 
|  | } | 
|  |  | 
|  | vfile->current_lock = mode; | 
|  | return SQLITE_OK; | 
|  | } | 
|  |  | 
|  | int VfsCheckReservedLock(sqlite3_file* file, int* result) { | 
|  | virtual_file* vfile = reinterpret_cast<virtual_file*>(file); | 
|  | base::AutoLock lock(*vfile->lock); | 
|  |  | 
|  | // The function expects a result is 1 if the lock is reserved, pending, or | 
|  | // exclusive; 0 otherwise. | 
|  | *result = vfile->current_lock >= SQLITE_LOCK_RESERVED ? 1 : 0; | 
|  | return SQLITE_OK; | 
|  | } | 
|  |  | 
|  | int VfsFileSize(sqlite3_file* file, sqlite3_int64* out_size) { | 
|  | virtual_file* vfile = reinterpret_cast<virtual_file*>(file); | 
|  | *out_size = vfile->data->length(); | 
|  | return SQLITE_OK; | 
|  | } | 
|  |  | 
|  | int VfsTruncate(sqlite3_file* file, sqlite3_int64 size) { | 
|  | virtual_file* vfile = reinterpret_cast<virtual_file*>(file); | 
|  | base::AutoLock lock(*vfile->lock); | 
|  | vfile->data->resize(size); | 
|  | return SQLITE_OK; | 
|  | } | 
|  |  | 
|  | int VfsDeviceCharacteristics(sqlite3_file* file) { | 
|  | UNREFERENCED_PARAMETER(file); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | const sqlite3_io_methods s_cobalt_vfs_io = { | 
|  | 1,                        // Structure version number | 
|  | VfsClose,                 // xClose | 
|  | VfsRead,                  // xRead | 
|  | VfsWrite,                 // xWrite | 
|  | VfsTruncate,              // xTruncate | 
|  | VfsSync,                  // xSync | 
|  | VfsFileSize,              // xFileSize | 
|  | VfsLock,                  // xLock | 
|  | VfsUnlock,                // xUnlock | 
|  | VfsCheckReservedLock,     // xCheckReservedLock | 
|  | VfsFileControl,           // xFileControl | 
|  | VfsSectorSize,            // xSectorSize | 
|  | VfsDeviceCharacteristics  // xDeviceCharacteristics | 
|  | }; | 
|  |  | 
|  | int VfsOpen(sqlite3_vfs* sql_vfs, | 
|  | const char* path, | 
|  | sqlite3_file* file, | 
|  | int flags, | 
|  | int* out_flags) { | 
|  | UNREFERENCED_PARAMETER(flags); | 
|  | UNREFERENCED_PARAMETER(out_flags); | 
|  | DCHECK(path) << "NULL filename not supported."; | 
|  | virtual_file* vfile = reinterpret_cast<virtual_file*>(file); | 
|  | vfile->lock = new base::Lock; | 
|  | TestVfs* vfs = reinterpret_cast<TestVfs*>(sql_vfs->pAppData); | 
|  | vfile->data = vfs->Open(path); | 
|  | file->pMethods = &s_cobalt_vfs_io; | 
|  | return SQLITE_OK; | 
|  | } | 
|  |  | 
|  | int VfsDelete(sqlite3_vfs* sql_vfs, const char* path, int sync_dir) { | 
|  | UNREFERENCED_PARAMETER(sync_dir); | 
|  | TestVfs* vfs = reinterpret_cast<TestVfs*>(sql_vfs->pAppData); | 
|  | vfs->Delete(path); | 
|  | return SQLITE_OK; | 
|  | } | 
|  |  | 
|  | int VfsFullPathname(sqlite3_vfs* sql_vfs, | 
|  | const char* path, | 
|  | int out_size, | 
|  | char* out_path) { | 
|  | UNREFERENCED_PARAMETER(sql_vfs); | 
|  | size_t path_size = static_cast<size_t>(out_size); | 
|  | if (base::strlcpy(out_path, path, path_size) < path_size) { | 
|  | return SQLITE_OK; | 
|  | } | 
|  | return SQLITE_ERROR; | 
|  | } | 
|  |  | 
|  | int VfsAccess(sqlite3_vfs* sql_vfs, const char* name, int flags, int* result) { | 
|  | UNREFERENCED_PARAMETER(name); | 
|  | UNREFERENCED_PARAMETER(sql_vfs); | 
|  | UNREFERENCED_PARAMETER(flags); | 
|  | // We should always have a valid, readable/writable file. | 
|  | *result |= SQLITE_ACCESS_EXISTS | SQLITE_ACCESS_READWRITE; | 
|  | return SQLITE_OK; | 
|  | } | 
|  |  | 
|  | int VfsRandomness(sqlite3_vfs* sql_vfs, int bytes, char* out) { | 
|  | UNREFERENCED_PARAMETER(sql_vfs); | 
|  | base::RandBytes(out, static_cast<size_t>(bytes)); | 
|  | return SQLITE_OK; | 
|  | } | 
|  |  | 
|  | void TestVfs::Register() { | 
|  | memset(&vfs_, 0, sizeof(vfs_)); | 
|  | vfs_.iVersion = 1; | 
|  | vfs_.szOsFile = sizeof(virtual_file); | 
|  | vfs_.mxPathname = 512; | 
|  | vfs_.pNext = NULL; | 
|  | vfs_.zName = "test_vfs"; | 
|  | vfs_.pAppData = this; | 
|  | vfs_.xOpen = VfsOpen; | 
|  | vfs_.xDelete = VfsDelete; | 
|  | vfs_.xAccess = VfsAccess; | 
|  | vfs_.xFullPathname = VfsFullPathname; | 
|  | vfs_.xRandomness = VfsRandomness; | 
|  |  | 
|  | // Ensure we are not registering multiple of these with the same name. | 
|  | // Behavior is undefined in that case. | 
|  | DCHECK(sqlite3_vfs_find(vfs_.zName) == NULL); | 
|  |  | 
|  | int ret = sqlite3_vfs_register(&vfs_, 1 /* make_default */); | 
|  | DCHECK_EQ(ret, SQLITE_OK); | 
|  | } | 
|  |  | 
|  | void TestVfs::Unregister() { | 
|  | int ret = sqlite3_vfs_unregister(&vfs_); | 
|  | file_map_.clear(); | 
|  | DCHECK_EQ(ret, SQLITE_OK); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | void RegisterTestVfs() { | 
|  | g_vfs.Get().Register(); | 
|  | } | 
|  |  | 
|  | void UnregisterTestVfs() { | 
|  | g_vfs.Get().Unregister(); | 
|  | } | 
|  |  | 
|  | }  // namespace sql |