#include "sql/vfs_wrapper.h"
#include <cstring>
#include <functional>
#include <memory>
#include <string_view>
#include "base/check.h"
#include "base/check_op.h"
#include "base/compiler_specific.h"
#include "base/debug/leak_annotations.h"
#include "base/metrics/histogram_macros.h"
#include "build/build_config.h"
#include "third_party/sqlite/sqlite3.h"
#if BUILDFLAG(IS_APPLE)
#include "base/apple/backup_util.h"
#include "base/files/file_util.h"
#endif
#if BUILDFLAG(IS_FUCHSIA)
#include "sql/vfs_wrapper_fuchsia.h"
#endif
namespace sql {
namespace {
#if !BUILDFLAG(IS_FUCHSIA)
int Unlock(sqlite3_file* sqlite_file, int file_lock);
#endif
sqlite3_vfs* GetWrappedVfs(sqlite3_vfs* wrapped_vfs) {
return static_cast<sqlite3_vfs*>(wrapped_vfs->pAppData);
}
VfsFile* AsVfsFile(sqlite3_file* wrapper_file) {
return reinterpret_cast<VfsFile*>(wrapper_file);
}
sqlite3_file* GetWrappedFile(sqlite3_file* wrapper_file) {
return AsVfsFile(wrapper_file)->wrapped_file;
}
int Close(sqlite3_file* sqlite_file) {
Unlock(sqlite_file, SQLITE_LOCK_NONE);
VfsFile* file = AsVfsFile(sqlite_file);
int r = file->wrapped_file->pMethods->xClose(file->wrapped_file);
sqlite3_free(file->wrapped_file);
file->~VfsFile();
UNSAFE_TODO(memset(file, '\0', sizeof(*file)));
return r;
}
int Read(sqlite3_file* sqlite_file, void* buf, int amt, sqlite3_int64 ofs)
{
sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
return wrapped_file->pMethods->xRead(wrapped_file, buf, amt, ofs);
}
int Write(sqlite3_file* sqlite_file, const void* buf, int amt,
sqlite3_int64 ofs)
{
sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
return wrapped_file->pMethods->xWrite(wrapped_file, buf, amt, ofs);
}
int Truncate(sqlite3_file* sqlite_file, sqlite3_int64 size)
{
sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
return wrapped_file->pMethods->xTruncate(wrapped_file, size);
}
int Sync(sqlite3_file* sqlite_file, int flags)
{
SCOPED_UMA_HISTOGRAM_TIMER("Sql.vfs.SyncTime");
sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
return wrapped_file->pMethods->xSync(wrapped_file, flags);
}
int FileSize(sqlite3_file* sqlite_file, sqlite3_int64* size)
{
sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
return wrapped_file->pMethods->xFileSize(wrapped_file, size);
}
#if !BUILDFLAG(IS_FUCHSIA)
int Lock(sqlite3_file* sqlite_file, int file_lock)
{
sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
return wrapped_file->pMethods->xLock(wrapped_file, file_lock);
}
int Unlock(sqlite3_file* sqlite_file, int file_lock)
{
sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
return wrapped_file->pMethods->xUnlock(wrapped_file, file_lock);
}
int CheckReservedLock(sqlite3_file* sqlite_file, int* result)
{
sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
return wrapped_file->pMethods->xCheckReservedLock(wrapped_file, result);
}
#endif
int FileControl(sqlite3_file* sqlite_file, int op, void* arg)
{
sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
return wrapped_file->pMethods->xFileControl(wrapped_file, op, arg);
}
int SectorSize(sqlite3_file* sqlite_file)
{
sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
return wrapped_file->pMethods->xSectorSize(wrapped_file);
}
int DeviceCharacteristics(sqlite3_file* sqlite_file)
{
sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
return wrapped_file->pMethods->xDeviceCharacteristics(wrapped_file);
}
int ShmMap(sqlite3_file *sqlite_file, int region, int size,
int extend, void volatile **pp) {
sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
return wrapped_file->pMethods->xShmMap(
wrapped_file, region, size, extend, pp);
}
int ShmLock(sqlite3_file *sqlite_file, int ofst, int n, int flags) {
sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
return wrapped_file->pMethods->xShmLock(wrapped_file, ofst, n, flags);
}
void ShmBarrier(sqlite3_file *sqlite_file) {
sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
wrapped_file->pMethods->xShmBarrier(wrapped_file);
}
int ShmUnmap(sqlite3_file *sqlite_file, int del) {
sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
return wrapped_file->pMethods->xShmUnmap(wrapped_file, del);
}
int Fetch(sqlite3_file *sqlite_file, sqlite3_int64 off, int amt, void **pp) {
sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
return wrapped_file->pMethods->xFetch(wrapped_file, off, amt, pp);
}
int Unfetch(sqlite3_file *sqlite_file, sqlite3_int64 off, void *p) {
sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
return wrapped_file->pMethods->xUnfetch(wrapped_file, off, p);
}
int Open(sqlite3_vfs* vfs, const char* file_name, sqlite3_file* wrapper_file,
int desired_flags, int* used_flags) {
sqlite3_vfs* wrapped_vfs = GetWrappedVfs(vfs);
sqlite3_file* wrapped_file = static_cast<sqlite3_file*>(
sqlite3_malloc(wrapped_vfs->szOsFile));
if (!wrapped_file)
return SQLITE_NOMEM;
int rc = wrapped_vfs->xOpen(wrapped_vfs,
file_name, wrapped_file,
desired_flags, used_flags);
if (rc != SQLITE_OK) {
sqlite3_free(wrapped_file);
return rc;
}
#if BUILDFLAG(IS_APPLE)
static int kJournalFlags =
SQLITE_OPEN_MAIN_JOURNAL | SQLITE_OPEN_TEMP_JOURNAL |
SQLITE_OPEN_SUBJOURNAL | SQLITE_OPEN_MASTER_JOURNAL;
if (file_name && (desired_flags & kJournalFlags)) {
std::string_view file_name_string_piece(file_name);
size_t dash_index = file_name_string_piece.rfind('-');
if (dash_index != std::string_view::npos) {
base::FilePath database_file_path(
std::string_view(file_name, dash_index));
if (base::PathExists(database_file_path) &&
base::apple::GetBackupExclusion(database_file_path)) {
base::apple::SetBackupExclusion(base::FilePath(file_name_string_piece));
}
}
}
#endif
VfsFile* file = AsVfsFile(wrapper_file);
new (file) VfsFile();
file->wrapped_file = wrapped_file;
#if BUILDFLAG(IS_FUCHSIA)
file->file_name = file_name;
#endif
if (wrapped_file->pMethods->iVersion == 1) {
static const sqlite3_io_methods io_methods = {
1,
Close,
Read,
Write,
Truncate,
Sync,
FileSize,
Lock,
Unlock,
CheckReservedLock,
FileControl,
SectorSize,
DeviceCharacteristics,
};
file->methods = &io_methods;
} else if (wrapped_file->pMethods->iVersion == 2) {
static const sqlite3_io_methods io_methods = {
2,
Close,
Read,
Write,
Truncate,
Sync,
FileSize,
Lock,
Unlock,
CheckReservedLock,
FileControl,
SectorSize,
DeviceCharacteristics,
ShmMap,
ShmLock,
ShmBarrier,
ShmUnmap,
};
file->methods = &io_methods;
} else {
static const sqlite3_io_methods io_methods = {
3,
Close,
Read,
Write,
Truncate,
Sync,
FileSize,
Lock,
Unlock,
CheckReservedLock,
FileControl,
SectorSize,
DeviceCharacteristics,
ShmMap,
ShmLock,
ShmBarrier,
ShmUnmap,
Fetch,
Unfetch,
};
file->methods = &io_methods;
}
return SQLITE_OK;
}
int Delete(sqlite3_vfs* vfs, const char* file_name, int sync_dir) {
sqlite3_vfs* wrapped_vfs = GetWrappedVfs(vfs);
return wrapped_vfs->xDelete(wrapped_vfs, file_name, sync_dir);
}
int Access(sqlite3_vfs* vfs, const char* file_name, int flag, int* res) {
sqlite3_vfs* wrapped_vfs = GetWrappedVfs(vfs);
return wrapped_vfs->xAccess(wrapped_vfs, file_name, flag, res);
}
int FullPathname(sqlite3_vfs* vfs, const char* relative_path,
int buf_size, char* absolute_path) {
sqlite3_vfs* wrapped_vfs = GetWrappedVfs(vfs);
return wrapped_vfs->xFullPathname(
wrapped_vfs, relative_path, buf_size, absolute_path);
}
int Randomness(sqlite3_vfs* vfs, int buf_size, char* buffer) {
sqlite3_vfs* wrapped_vfs = GetWrappedVfs(vfs);
return wrapped_vfs->xRandomness(wrapped_vfs, buf_size, buffer);
}
int Sleep(sqlite3_vfs* vfs, int microseconds) {
sqlite3_vfs* wrapped_vfs = GetWrappedVfs(vfs);
return wrapped_vfs->xSleep(wrapped_vfs, microseconds);
}
int GetLastError(sqlite3_vfs* vfs, int e, char* s) {
sqlite3_vfs* wrapped_vfs = GetWrappedVfs(vfs);
return wrapped_vfs->xGetLastError(wrapped_vfs, e, s);
}
int CurrentTimeInt64(sqlite3_vfs* vfs, sqlite3_int64* now) {
sqlite3_vfs* wrapped_vfs = GetWrappedVfs(vfs);
return wrapped_vfs->xCurrentTimeInt64(wrapped_vfs, now);
}
}
void EnsureVfsWrapper() {
if (sqlite3_vfs_find(kVfsWrapperName)) {
return;
}
static constexpr const char* kBaseVfsName =
#if BUILDFLAG(IS_FUCHSIA)
"unix-none";
#else
nullptr;
#endif
sqlite3_vfs* wrapped_vfs = sqlite3_vfs_find(kBaseVfsName);
CHECK(wrapped_vfs);
std::string_view vfs_name(wrapped_vfs->zName);
CHECK(vfs_name == "unix" || vfs_name == "win32" || vfs_name == "unix-none" ||
vfs_name == "storage_service")
<< "Wrapping unexpected VFS " << vfs_name;
std::unique_ptr<sqlite3_vfs, std::function<void(sqlite3_vfs*)>> wrapper_vfs(
static_cast<sqlite3_vfs*>(sqlite3_malloc(sizeof(sqlite3_vfs))),
[](sqlite3_vfs* v) {
sqlite3_free(v);
});
UNSAFE_TODO(memset(wrapper_vfs.get(), '\0', sizeof(sqlite3_vfs)));
constexpr int kSqliteVfsApiVersion = 3;
wrapper_vfs->iVersion = kSqliteVfsApiVersion;
DCHECK_GE(wrapped_vfs->iVersion, kSqliteVfsApiVersion);
wrapper_vfs->szOsFile = sizeof(VfsFile);
wrapper_vfs->mxPathname = wrapped_vfs->mxPathname;
wrapper_vfs->pNext = nullptr;
wrapper_vfs->zName = kVfsWrapperName;
wrapper_vfs->pAppData = wrapped_vfs;
wrapper_vfs->xOpen = &Open;
wrapper_vfs->xDelete = &Delete;
wrapper_vfs->xAccess = &Access;
wrapper_vfs->xFullPathname = &FullPathname;
wrapper_vfs->xDlOpen = nullptr;
wrapper_vfs->xDlError = nullptr;
wrapper_vfs->xDlSym = nullptr;
wrapper_vfs->xDlClose = nullptr;
wrapper_vfs->xRandomness = &Randomness;
wrapper_vfs->xSleep = &Sleep;
wrapper_vfs->xCurrentTime = nullptr;
wrapper_vfs->xGetLastError = &GetLastError;
DCHECK(wrapped_vfs->xCurrentTimeInt64 != nullptr);
wrapper_vfs->xCurrentTimeInt64 = &CurrentTimeInt64;
wrapper_vfs->xSetSystemCall = nullptr;
wrapper_vfs->xGetSystemCall = nullptr;
wrapper_vfs->xNextSystemCall = nullptr;
if (SQLITE_OK == sqlite3_vfs_register(wrapper_vfs.get(), 1)) {
ANNOTATE_LEAKING_OBJECT_PTR(wrapper_vfs.get());
wrapper_vfs.release();
}
}
}