123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616 |
- // Copyright 2019 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 "sql/sandboxed_vfs_file.h"
- #include <atomic>
- #include <cstring>
- #include <type_traits>
- #include <utility>
- #include "base/check_op.h"
- #include "base/files/file.h"
- #include "base/files/file_path.h"
- #include "base/logging.h"
- #include "base/notreached.h"
- #include "base/threading/platform_thread.h"
- #include "build/build_config.h"
- #include "sql/initialization.h"
- #include "sql/sandboxed_vfs.h"
- #include "third_party/sqlite/sqlite3.h"
- namespace sql {
- namespace {
- int SandboxedClose(sqlite3_file* file) {
- return SandboxedVfsFile::FromSqliteFile(*file).Close();
- }
- int SandboxedRead(sqlite3_file* file,
- void* buffer,
- int size,
- sqlite3_int64 offset) {
- return SandboxedVfsFile::FromSqliteFile(*file).Read(buffer, size, offset);
- }
- int SandboxedWrite(sqlite3_file* file,
- const void* buffer,
- int size,
- sqlite3_int64 offset) {
- return SandboxedVfsFile::FromSqliteFile(*file).Write(buffer, size, offset);
- }
- int SandboxedTruncate(sqlite3_file* file, sqlite3_int64 size) {
- return SandboxedVfsFile::FromSqliteFile(*file).Truncate(size);
- }
- int SandboxedSync(sqlite3_file* file, int flags) {
- return SandboxedVfsFile::FromSqliteFile(*file).Sync(flags);
- }
- int SandboxedFileSize(sqlite3_file* file, sqlite3_int64* result_size) {
- return SandboxedVfsFile::FromSqliteFile(*file).FileSize(result_size);
- }
- int SandboxedLock(sqlite3_file* file, int mode) {
- return SandboxedVfsFile::FromSqliteFile(*file).Lock(mode);
- }
- int SandboxedUnlock(sqlite3_file* file, int mode) {
- return SandboxedVfsFile::FromSqliteFile(*file).Unlock(mode);
- }
- int SandboxedCheckReservedLock(sqlite3_file* file, int* has_reserved_lock) {
- return SandboxedVfsFile::FromSqliteFile(*file).CheckReservedLock(
- has_reserved_lock);
- }
- int SandboxedFileControl(sqlite3_file* file, int opcode, void* data) {
- return SandboxedVfsFile::FromSqliteFile(*file).FileControl(opcode, data);
- }
- int SandboxedSectorSize(sqlite3_file* file) {
- return SandboxedVfsFile::FromSqliteFile(*file).SectorSize();
- }
- int SandboxedDeviceCharacteristics(sqlite3_file* file) {
- return SandboxedVfsFile::FromSqliteFile(*file).DeviceCharacteristics();
- }
- int SandboxedShmMap(sqlite3_file* file,
- int page_index,
- int page_size,
- int extend_file_if_needed,
- void volatile** result) {
- return SandboxedVfsFile::FromSqliteFile(*file).ShmMap(
- page_index, page_size, extend_file_if_needed, result);
- }
- int SandboxedShmLock(sqlite3_file* file, int offset, int size, int flags) {
- return SandboxedVfsFile::FromSqliteFile(*file).ShmLock(offset, size, flags);
- }
- void SandboxedShmBarrier(sqlite3_file* file) {
- SandboxedVfsFile::FromSqliteFile(*file).ShmBarrier();
- }
- int SandboxedShmUnmap(sqlite3_file* file, int also_delete_file) {
- return SandboxedVfsFile::FromSqliteFile(*file).ShmUnmap(also_delete_file);
- }
- int SandboxedFetch(sqlite3_file* file,
- sqlite3_int64 offset,
- int size,
- void** result) {
- return SandboxedVfsFile::FromSqliteFile(*file).Fetch(offset, size, result);
- }
- int SandboxedUnfetch(sqlite3_file* file,
- sqlite3_int64 offset,
- void* fetch_result) {
- return SandboxedVfsFile::FromSqliteFile(*file).Unfetch(offset, fetch_result);
- }
- const sqlite3_io_methods* GetSqliteIoMethods() {
- // VFS IO API entry points are listed at
- // https://www.sqlite.org/c3ref/io_methods.html
- static constexpr int kSqliteVfsIoApiVersion = 3;
- static const sqlite3_io_methods kIoMethods = {
- kSqliteVfsIoApiVersion,
- SandboxedClose,
- SandboxedRead,
- SandboxedWrite,
- SandboxedTruncate,
- SandboxedSync,
- SandboxedFileSize,
- SandboxedLock,
- SandboxedUnlock,
- SandboxedCheckReservedLock,
- SandboxedFileControl,
- SandboxedSectorSize,
- SandboxedDeviceCharacteristics,
- SandboxedShmMap,
- SandboxedShmLock,
- SandboxedShmBarrier,
- SandboxedShmUnmap,
- SandboxedFetch,
- SandboxedUnfetch,
- };
- return &kIoMethods;
- }
- } // namespace
- // static
- void SandboxedVfsFile::Create(base::File file,
- base::FilePath file_path,
- #if DCHECK_IS_ON()
- SandboxedVfsFileType file_type,
- #endif // DCHECK_IS_ON()
- SandboxedVfs* vfs,
- sqlite3_file& buffer) {
- SandboxedVfsFileSqliteBridge& bridge =
- SandboxedVfsFileSqliteBridge::FromSqliteFile(buffer);
- bridge.sandboxed_vfs_file =
- new SandboxedVfsFile(std::move(file), std::move(file_path),
- #if DCHECK_IS_ON()
- file_type,
- #endif // DCHECK_IS_ON()
- vfs);
- bridge.sqlite_file.pMethods = GetSqliteIoMethods();
- }
- // static
- SandboxedVfsFile& SandboxedVfsFile::FromSqliteFile(sqlite3_file& sqlite_file) {
- return *SandboxedVfsFileSqliteBridge::FromSqliteFile(sqlite_file)
- .sandboxed_vfs_file;
- }
- int SandboxedVfsFile::Close() {
- file_.Close();
- delete this;
- return SQLITE_OK;
- }
- int SandboxedVfsFile::Read(void* buffer, int size, sqlite3_int64 offset) {
- DCHECK(buffer);
- DCHECK_GE(size, 0);
- DCHECK_GE(offset, 0);
- #if DCHECK_IS_ON()
- // See http://www.sqlite.org/fileformat2.html#database_header
- constexpr int kSqliteDatabaseHeaderOffset = 0;
- constexpr int kSqliteDatabaseHeaderSize = 100;
- // SQLite's locking protocol only acquires locks on the database file. The
- // journal and the WAL file are always unlocked. Also, as an optimization,
- // SQLite first reads the database header without locking the file.
- DCHECK(sqlite_lock_mode_ > SQLITE_LOCK_NONE ||
- file_type_ != SandboxedVfsFileType::kDatabase ||
- (offset == kSqliteDatabaseHeaderOffset &&
- size == kSqliteDatabaseHeaderSize))
- << "Read from database file with lock mode " << sqlite_lock_mode_
- << "of size" << size << " at offset " << offset;
- #endif // DCHECK_IS_ON()
- char* data = reinterpret_cast<char*>(buffer);
- // If we supported mmap()ed files, we'd check for a memory mapping here,
- // and try to fill as much of the request as possible from the mmap()ed
- // region.
- int bytes_read = file_.Read(offset, data, size);
- DCHECK_LE(bytes_read, size);
- if (bytes_read == size)
- return SQLITE_OK;
- if (bytes_read < 0) {
- // SQLite first reads the database header without locking the file. On
- // Windows, this read will fail if there is an exclusive lock on the file,
- // even if the current process owns that lock.
- if (sqlite_lock_mode_ == SQLITE_LOCK_NONE) {
- // The unlocked read is considered an optimization. SQLite can continue
- // even if the read fails, as long as failure is communicated by zeroing
- // out the output buffer.
- std::memset(data, 0, size);
- return SQLITE_OK;
- }
- vfs_->SetLastError(base::File::GetLastFileError());
- return SQLITE_IOERR_READ;
- }
- // SQLite requires that we fill the unread bytes in the buffer with zeros.
- std::memset(data + bytes_read, 0, size - bytes_read);
- return SQLITE_IOERR_SHORT_READ;
- }
- int SandboxedVfsFile::Write(const void* buffer,
- int size,
- sqlite3_int64 offset) {
- DCHECK(buffer);
- DCHECK_GE(size, 0);
- DCHECK_GE(offset, 0);
- #if DCHECK_IS_ON()
- // SQLite's locking protocol only acquires locks on the database file. The
- // journal and the WAL file are always unlocked.
- DCHECK(sqlite_lock_mode_ == SQLITE_LOCK_EXCLUSIVE ||
- file_type_ != SandboxedVfsFileType::kDatabase)
- << "Write to database file with lock mode " << sqlite_lock_mode_;
- #endif // DCHECK_IS_ON()
- const char* data = reinterpret_cast<const char*>(buffer);
- // If we supported mmap()ed files, we'd check for a memory mapping here,
- // and try to fill as much of the request as possible by copying to the
- // mmap()ed region.
- int bytes_written = file_.Write(offset, data, size);
- DCHECK_LE(bytes_written, size);
- if (bytes_written >= size)
- return SQLITE_OK;
- base::File::Error last_error = base::File::GetLastFileError();
- vfs_->SetLastError(last_error);
- if (last_error == base::File::Error::FILE_ERROR_NO_SPACE)
- return SQLITE_FULL;
- return SQLITE_IOERR_WRITE;
- }
- int SandboxedVfsFile::Truncate(sqlite3_int64 size) {
- if (file_.SetLength(size))
- return SQLITE_OK;
- // On macOS < 10.15, the default sandbox blocks ftruncate(), so we have to use
- // a sync mojo IPC to ask the browser process to call ftruncate() for us.
- //
- // TODO(crbug.com/1084565): Figure out if we can allow ftruncate() in renderer
- // and utility processes. It would be useful for low-level storage APIs, like
- // the upcoming filesystem API.
- if (vfs_->delegate()->SetFileLength(file_path_, file_,
- static_cast<size_t>(size))) {
- return SQLITE_OK;
- }
- return SQLITE_IOERR_TRUNCATE;
- }
- int SandboxedVfsFile::Sync(int flags) {
- // NOTE: SQLite passes in (SQLITE_SYNC_NORMAL or SQLITE_SYNC_FULL),
- // potentially OR-ed with SQLITE_SYNC_DATAONLY. Implementing these could
- // lead to better performance.
- if (!file_.Flush()) {
- vfs_->SetLastError(base::File::GetLastFileError());
- return SQLITE_IOERR_FSYNC;
- }
- // The unix VFS also syncs the file's directory on the first xSync() call.
- // Chrome's LevelDB Env implementation does the same for specific files
- // (database manifests).
- //
- // For WebSQL, we would want to sync the directory at file open time, when the
- // file is opened for writing.
- return SQLITE_OK;
- }
- int SandboxedVfsFile::FileSize(sqlite3_int64* result_size) {
- int64_t length = file_.GetLength();
- if (length < 0) {
- vfs_->SetLastError(base::File::GetLastFileError());
- return SQLITE_IOERR_FSTAT;
- }
- // SQLite's unix VFS reports 1-byte files as empty. This is documented as a
- // workaround for a fairly obscure bug. See unixFileSize() in os_unix.c.
- if (length == 1)
- length = 0;
- *result_size = length;
- return SQLITE_OK;
- }
- namespace {
- // True if our simplified implementation uses an exclusive lock for a mode.
- bool IsExclusiveLockMode(int sqlite_lock_mode) {
- switch (sqlite_lock_mode) {
- case SQLITE_LOCK_NONE:
- case SQLITE_LOCK_SHARED:
- return false;
- case SQLITE_LOCK_RESERVED:
- case SQLITE_LOCK_PENDING:
- case SQLITE_LOCK_EXCLUSIVE:
- return true;
- }
- NOTREACHED() << "Unsupported mode: " << sqlite_lock_mode;
- return false;
- }
- } // namespace
- int SandboxedVfsFile::Lock(int mode) {
- DCHECK_GT(mode, sqlite_lock_mode_)
- << "SQLite asked the VFS to lock the file up to mode " << mode
- << " but the file is already locked at mode " << sqlite_lock_mode_;
- #if BUILDFLAG(IS_FUCHSIA)
- return SQLITE_IOERR_LOCK;
- #else
- base::File::LockMode file_lock_mode = base::File::LockMode::kExclusive;
- switch (mode) {
- case SQLITE_LOCK_NONE:
- return SQLITE_OK;
- case SQLITE_LOCK_SHARED:
- if (sqlite_lock_mode_ != SQLITE_LOCK_NONE)
- return SQLITE_OK;
- file_lock_mode = base::File::LockMode::kShared;
- break;
- case SQLITE_LOCK_RESERVED:
- // A SHARED lock is required before a RESERVED lock is acquired.
- DCHECK_EQ(sqlite_lock_mode_, SQLITE_LOCK_SHARED);
- file_lock_mode = base::File::LockMode::kExclusive;
- break;
- case SQLITE_LOCK_PENDING:
- NOTREACHED() << "SQLite never directly asks for PENDING locks";
- // Should we ever receive PENDING lock requests, the handler for
- // EXCLUSIVE lock requests below happens to work perfectly.
- [[fallthrough]];
- case SQLITE_LOCK_EXCLUSIVE:
- // A SHARED lock is required before an EXCLUSIVE lock is acquired.
- //
- // No higher level is required. In fact, SQLite upgrades the lock directly
- // from SHARED to EXCLUSIVE when rolling back a transaction, to avoid
- // having other readers queue up in the RESERVED state.
- DCHECK_GE(sqlite_lock_mode_, SQLITE_LOCK_SHARED);
- if (IsExclusiveLockMode(sqlite_lock_mode_)) {
- sqlite_lock_mode_ = mode;
- return SQLITE_OK;
- }
- file_lock_mode = base::File::LockMode::kExclusive;
- break;
- default:
- NOTREACHED() << "Unimplemented xLock() mode: " << mode;
- }
- DCHECK_EQ(IsExclusiveLockMode(mode),
- file_lock_mode == base::File::LockMode::kExclusive)
- << "Incorrect file_lock_mode logic for SQLite mode: " << mode;
- // On POSIX, it would be possible to upgrade atomically from a shared lock to
- // an exclusive lock. This implementation prioritizes the simplicity of no
- // platform-specific code over being faster in high contention cases.
- if (sqlite_lock_mode_ != SQLITE_LOCK_NONE) {
- base::File::Error error = file_.Unlock();
- if (error != base::File::FILE_OK) {
- vfs_->SetLastError(base::File::GetLastFileError());
- return SQLITE_IOERR_LOCK;
- }
- sqlite_lock_mode_ = SQLITE_LOCK_NONE;
- }
- base::File::Error error = file_.Lock(file_lock_mode);
- if (error != base::File::FILE_OK) {
- vfs_->SetLastError(base::File::GetLastFileError());
- return SQLITE_IOERR_LOCK;
- }
- sqlite_lock_mode_ = mode;
- return SQLITE_OK;
- #endif // BUILDFLAG(IS_FUCHSIA)
- }
- int SandboxedVfsFile::Unlock(int mode) {
- // The 2nd term in the DCHECK predicate is there because SQLite occasionally
- // attempts to unlock (to SQLITE_LOCK_NONE) a file that was already unlocked.
- // We're not aware of any other case of no-op VFS unlock calls.
- DCHECK(mode < sqlite_lock_mode_ ||
- (mode == sqlite_lock_mode_ && mode == SQLITE_LOCK_NONE))
- << "SQLite asked the VFS to unlock the file down to mode " << mode
- << " but the file is already at mode " << sqlite_lock_mode_;
- // No-op if we're already unlocked or at the requested mode.
- if (sqlite_lock_mode_ == mode || sqlite_lock_mode_ == SQLITE_LOCK_NONE)
- return SQLITE_OK;
- #if BUILDFLAG(IS_FUCHSIA)
- return SQLITE_IOERR_UNLOCK;
- #else
- // On POSIX, it is possible to downgrade atomically from an exclusive lock to
- // a shared lock. SQLite's unix VFS takes advantage of this. This
- // implementation prioritizes the simplicity of no platform-specific code over
- // being faster in high contention cases.
- base::File::Error error = file_.Unlock();
- if (error != base::File::FILE_OK) {
- vfs_->SetLastError(base::File::GetLastFileError());
- return SQLITE_IOERR_UNLOCK;
- }
- if (mode == SQLITE_LOCK_NONE) {
- sqlite_lock_mode_ = mode;
- return SQLITE_OK;
- }
- DCHECK_EQ(mode, SQLITE_LOCK_SHARED);
- error = file_.Lock(base::File::LockMode::kShared);
- if (error == base::File::FILE_OK) {
- sqlite_lock_mode_ = mode;
- return SQLITE_OK;
- }
- // Gave up the exclusive lock, but failed to get a shared lock.
- vfs_->SetLastError(base::File::GetLastFileError());
- sqlite_lock_mode_ = SQLITE_LOCK_NONE;
- return SQLITE_IOERR_UNLOCK;
- #endif // BUILDFLAG(IS_FUCHSIA)
- }
- int SandboxedVfsFile::CheckReservedLock(int* has_reserved_lock) {
- if (IsExclusiveLockMode(sqlite_lock_mode_)) {
- *has_reserved_lock = 1;
- return SQLITE_OK;
- }
- if (sqlite_lock_mode_ == SQLITE_LOCK_SHARED) {
- // Lock modes at or above RESERVED map to exclusive locks in our simplified
- // implementation. If this process has a shared lock, no other process can
- // have an exclusive lock.
- *has_reserved_lock = 0;
- return SQLITE_OK;
- }
- #if BUILDFLAG(IS_FUCHSIA)
- return SQLITE_IOERR_CHECKRESERVEDLOCK;
- #else
- // On POSIX, it's possible to query the existing lock state of a file. The
- // SQLite unix VFS takes advantage of this. On Windows, this isn't the case.
- // Follow the strategy of the Windows VFS, which checks by trying to get an
- // exclusive lock on the file.
- base::File::Error error = file_.Lock(base::File::LockMode::kShared);
- if (error != base::File::FILE_OK) {
- *has_reserved_lock = 1;
- return SQLITE_OK;
- }
- *has_reserved_lock = 0;
- if (file_.Unlock() == base::File::FILE_OK)
- return SQLITE_OK;
- // We acquired a shared lock that we can't get rid of.
- sqlite_lock_mode_ = SQLITE_LOCK_SHARED;
- return SQLITE_IOERR_CHECKRESERVEDLOCK;
- #endif // BUILDFLAG(IS_FUCHSIA)
- }
- int SandboxedVfsFile::FileControl(int opcode, void* data) {
- switch (opcode) {
- case SQLITE_FCNTL_MMAP_SIZE:
- // Implementing memory-mapping will require handling this correctly.
- return SQLITE_NOTFOUND;
- default:
- return SQLITE_NOTFOUND;
- }
- }
- int SandboxedVfsFile::SectorSize() {
- return 0;
- }
- int SandboxedVfsFile::DeviceCharacteristics() {
- // TODO(pwnall): Figure out if we can get away with returning 0 on Windows.
- #if BUILDFLAG(IS_WIN)
- return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN;
- #else
- // NOTE: SQLite's unix VFS attempts to detect the underlying filesystem and
- // sets some flags based on the result.
- return 0;
- #endif // BUILDFLAG(IS_WIN)
- }
- int SandboxedVfsFile::ShmMap(int page_index,
- int page_size,
- int extend_file_if_needed,
- void volatile** result) {
- DCHECK_GE(page_index, 0);
- DCHECK_GE(page_size, 0);
- DCHECK(result);
- // https://www.sqlite.org/wal.html#use_of_wal_without_shared_memory states
- // that SQLite only attempts to use shared memory "-shm" files for databases
- // in WAL mode that may be accessed by multiple processes (are not EXCLUSIVE).
- //
- // Chrome will not only use WAL mode on EXCLUSIVE databases.
- NOTREACHED() << "SQLite should not attempt to use shared memory";
- *result = nullptr;
- return SQLITE_IOERR;
- }
- int SandboxedVfsFile::ShmLock(int offset, int size, int flags) {
- DCHECK_GE(offset, 0);
- DCHECK_GE(size, 0);
- // https://www.sqlite.org/wal.html#use_of_wal_without_shared_memory states
- // that SQLite only attempts to use shared memory "-shm" files for databases
- // in WAL mode that may be accessed by multiple processes (are not EXCLUSIVE).
- //
- // Chrome will not only use WAL mode on EXCLUSIVE databases.
- NOTREACHED() << "SQLite should not attempt to use shared memory";
- return SQLITE_IOERR;
- }
- void SandboxedVfsFile::ShmBarrier() {
- // https://www.sqlite.org/wal.html#use_of_wal_without_shared_memory states
- // that SQLite only attempts to use shared memory "-shm" files for databases
- // in WAL mode that may be accessed by multiple processes (are not EXCLUSIVE).
- //
- // Chrome will not only use WAL mode on EXCLUSIVE databases.
- NOTREACHED() << "SQLite should not attempt to use shared memory";
- // All writes to shared memory that have already been issued before this
- // function is called must complete before the function returns.
- std::atomic_thread_fence(std::memory_order_acq_rel);
- }
- int SandboxedVfsFile::ShmUnmap(int also_delete_file) {
- // https://www.sqlite.org/wal.html#use_of_wal_without_shared_memory states
- // that SQLite only attempts to use shared memory "-shm" files for databases
- // in WAL mode that may be accessed by multiple processes (are not EXCLUSIVE).
- //
- // Chrome will not only use WAL mode on EXCLUSIVE databases.
- NOTREACHED() << "SQLite should not attempt to use shared memory";
- return SQLITE_IOERR;
- }
- int SandboxedVfsFile::Fetch(sqlite3_int64 offset, int size, void** result) {
- DCHECK_GE(offset, 0);
- DCHECK_GE(size, 0);
- DCHECK(result);
- // NOTE: This would be needed for mmap()ed file support.
- *result = nullptr;
- return SQLITE_IOERR;
- }
- int SandboxedVfsFile::Unfetch(sqlite3_int64 offset, void* fetch_result) {
- DCHECK_GE(offset, 0);
- DCHECK(fetch_result);
- // NOTE: This would be needed for mmap()ed file support.
- return SQLITE_IOERR;
- }
- SandboxedVfsFile::SandboxedVfsFile(base::File file,
- base::FilePath file_path,
- #if DCHECK_IS_ON()
- SandboxedVfsFileType file_type,
- #endif // DCHECK_IS_ON()
- SandboxedVfs* vfs)
- : file_(std::move(file)),
- sqlite_lock_mode_(SQLITE_LOCK_NONE),
- vfs_(vfs),
- #if DCHECK_IS_ON()
- file_type_(file_type),
- #endif // DCHECK_IS_ON()
- file_path_(std::move(file_path)) {
- }
- SandboxedVfsFile::~SandboxedVfsFile() = default;
- // static
- SandboxedVfsFileSqliteBridge& SandboxedVfsFileSqliteBridge::FromSqliteFile(
- sqlite3_file& sqlite_file) {
- static_assert(std::is_standard_layout<SandboxedVfsFileSqliteBridge>::value,
- "needed for the reinterpret_cast below");
- static_assert(offsetof(SandboxedVfsFileSqliteBridge, sqlite_file) == 0,
- "sqlite_file must be the first member of the struct.");
- SandboxedVfsFileSqliteBridge& bridge =
- reinterpret_cast<SandboxedVfsFileSqliteBridge&>(sqlite_file);
- DCHECK_EQ(&sqlite_file, &bridge.sqlite_file)
- << "assumed by the reinterpret_casts in the implementation";
- return bridge;
- }
- } // namespace sql
|