blob: 9efaf3f528601ffd430f61bffc59dcfe6aaafa9e [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/persistent_cache/sqlite/vfs/sqlite_sandboxed_vfs.h"
#include <mutex>
#include <optional>
#include <tuple>
#include <utility>
#include "base/check.h"
#include "base/check_op.h"
#include "base/files/file.h"
#include "base/files/file_util.h"
#include "base/metrics/histogram_functions.h"
#include "base/notreached.h"
#include "base/strings/strcat.h"
#include "base/synchronization/lock.h"
#include "components/persistent_cache/sqlite/vfs/sqlite_database_vfs_file_set.h"
#include "sql/sandboxed_vfs.h"
#include "sql/sandboxed_vfs_file.h"
#include "third_party/sqlite/sqlite3.h"
namespace persistent_cache {
namespace {
std::once_flag g_register_vfs_once_flag;
SqliteSandboxedVfsDelegate* g_instance = nullptr;
SandboxedFile::FileType GetFileType(int sqlite_requested_type) {
static constexpr int kTypeMask =
SQLITE_OPEN_MAIN_DB | SQLITE_OPEN_TEMP_DB | SQLITE_OPEN_TRANSIENT_DB |
SQLITE_OPEN_MAIN_JOURNAL | SQLITE_OPEN_TEMP_JOURNAL |
SQLITE_OPEN_SUBJOURNAL | SQLITE_OPEN_SUPER_JOURNAL | SQLITE_OPEN_WAL;
switch (sqlite_requested_type & kTypeMask) {
case SQLITE_OPEN_MAIN_DB:
return SandboxedFile::FileType::kMainDb;
case SQLITE_OPEN_TEMP_DB:
return SandboxedFile::FileType::kTempDb;
case SQLITE_OPEN_TRANSIENT_DB:
return SandboxedFile::FileType::kTransientDb;
case SQLITE_OPEN_MAIN_JOURNAL:
return SandboxedFile::FileType::kMainJournal;
case SQLITE_OPEN_TEMP_JOURNAL:
return SandboxedFile::FileType::kTempJournal;
case SQLITE_OPEN_SUBJOURNAL:
return SandboxedFile::FileType::kSubjournal;
case SQLITE_OPEN_SUPER_JOURNAL:
return SandboxedFile::FileType::kSuperJournal;
case SQLITE_OPEN_WAL:
return SandboxedFile::FileType::kWal;
}
NOTREACHED();
}
} // namespace
SqliteSandboxedVfsDelegate::SqliteSandboxedVfsDelegate() {
CHECK(!g_instance);
g_instance = this;
}
SqliteSandboxedVfsDelegate::~SqliteSandboxedVfsDelegate() {
CHECK_EQ(this, g_instance);
g_instance = nullptr;
}
// static
SqliteSandboxedVfsDelegate* SqliteSandboxedVfsDelegate::GetInstance() {
// When requesting the the global instance the first time make sure it exists
// and register it.
std::call_once(g_register_vfs_once_flag, []() {
sql::SandboxedVfs::Register(kSqliteVfsName,
std::make_unique<SqliteSandboxedVfsDelegate>(),
/*make_default=*/false);
});
return g_instance;
}
sql::SandboxedVfsFile* SqliteSandboxedVfsDelegate::RetrieveSandboxedVfsFile(
base::File file,
base::FilePath file_path,
sql::SandboxedVfsFileType file_type,
sql::SandboxedVfs* vfs) {
base::AutoLock lock(files_map_lock_);
auto it = sandboxed_files_map_.find(file_path);
if (it == sandboxed_files_map_.end()) {
return nullptr;
}
it->second->OnFileOpened(std::move(file));
return it->second;
}
base::File SqliteSandboxedVfsDelegate::OpenFile(const base::FilePath& file_path,
int sqlite_requested_flags) {
base::AutoLock lock(files_map_lock_);
// If `file_name` is missing in the mapping there is no file to return.
auto it = sandboxed_files_map_.find(file_path);
if (it == sandboxed_files_map_.end()) {
return base::File();
}
auto file_type = GetFileType(sqlite_requested_flags);
// Only the main database and its rollback journal and/or write-ahead log are
// supported.
CHECK(file_type == SandboxedFile::FileType::kMainDb ||
file_type == SandboxedFile::FileType::kMainJournal ||
file_type == SandboxedFile::FileType::kWal);
// If `file_name` is found in the mapping return the associated file.
return it->second->TakeUnderlyingFile(file_type);
}
int SqliteSandboxedVfsDelegate::DeleteFile(const base::FilePath& file_path,
bool /*sync_dir*/) {
base::AutoLock lock(files_map_lock_);
// Sandboxed processes are not capable of deleting files, so deletion is
// simulated by truncating the file to zero.
auto it = sandboxed_files_map_.find(file_path);
if (it != sandboxed_files_map_.end()) {
auto& file = it->second->GetFile();
const auto file_error = file.SetLength(0) ? base::File::FILE_OK
: base::File::GetLastFileError();
base::UmaHistogramExactLinear(
base::StrCat(
{"PersistentCache.Sqlite.",
SqliteVfsFileSet::GetVirtualFileHistogramVariant(file_path),
".SetLengthResult"}),
-file_error, -base::File::FILE_ERROR_MAX);
return file_error == base::File::FILE_OK ? SQLITE_OK : SQLITE_IOERR_DELETE;
}
return SQLITE_NOTFOUND;
}
std::optional<sql::SandboxedVfs::PathAccessInfo>
SqliteSandboxedVfsDelegate::GetPathAccess(const base::FilePath& file_path) {
base::AutoLock lock(files_map_lock_);
// If `file_name` is missing in the mapping there is no access to return.
auto it = sandboxed_files_map_.find(file_path);
if (it == sandboxed_files_map_.end()) {
return std::nullopt;
}
// The files will never be received without read access.
// Write access is conditional on the file being opened for write.
return sql::SandboxedVfs::PathAccessInfo{
.can_read = true,
.can_write = it->second->access_rights() ==
SandboxedFile::AccessRights::kReadWrite};
}
SqliteSandboxedVfsDelegate::UnregisterRunner::UnregisterRunner(
const SqliteVfsFileSet& vfs_file_set)
: vfs_file_set_(vfs_file_set) {}
SqliteSandboxedVfsDelegate::UnregisterRunner::~UnregisterRunner() {
SqliteSandboxedVfsDelegate::GetInstance()->UnregisterSandboxedFiles(
*vfs_file_set_);
}
// static
void SqliteSandboxedVfsDelegate::UnregisterSandboxedFiles(
const SqliteVfsFileSet& sqlite_vfs_file_set) {
base::AutoLock lock(files_map_lock_);
auto num_erased =
sandboxed_files_map_.erase(sqlite_vfs_file_set.GetDbVirtualFilePath());
CHECK_EQ(num_erased, 1U);
num_erased = sandboxed_files_map_.erase(
sqlite_vfs_file_set.GetJournalVirtualFilePath());
CHECK_EQ(num_erased, 1U);
if (sqlite_vfs_file_set.wal_journal_mode()) {
num_erased = sandboxed_files_map_.erase(
sqlite_vfs_file_set.GetWalJournalVirtualFilePath());
CHECK_EQ(num_erased, 1U);
}
}
// static
SqliteSandboxedVfsDelegate::UnregisterRunner
SqliteSandboxedVfsDelegate::RegisterSandboxedFiles(
const SqliteVfsFileSet& sqlite_vfs_file_set) {
base::AutoLock lock(files_map_lock_);
auto [it, inserted] =
sandboxed_files_map_.emplace(sqlite_vfs_file_set.GetDbVirtualFilePath(),
sqlite_vfs_file_set.GetSandboxedDbFile());
CHECK(inserted);
std::tie(it, inserted) = sandboxed_files_map_.emplace(
sqlite_vfs_file_set.GetJournalVirtualFilePath(),
sqlite_vfs_file_set.GetSandboxedJournalFile());
CHECK(inserted);
if (sqlite_vfs_file_set.wal_journal_mode()) {
std::tie(it, inserted) = sandboxed_files_map_.emplace(
sqlite_vfs_file_set.GetWalJournalVirtualFilePath(),
sqlite_vfs_file_set.GetSandboxedWalJournalFile());
CHECK(inserted);
}
return UnregisterRunner(sqlite_vfs_file_set);
}
} // namespace persistent_cache