1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "sql/sandboxed_vfs_file.h"
12 #include "base/check_op.h"
13 #include "base/files/file.h"
14 #include "base/files/file_path.h"
15 #include "base/logging.h"
16 #include "base/notreached.h"
17 #include "base/threading/platform_thread.h"
18 #include "build/build_config.h"
19 #include "sql/initialization.h"
20 #include "sql/sandboxed_vfs.h"
21 #include "third_party/sqlite/sqlite3.h"
27 int SandboxedClose(sqlite3_file* file) {
28 return SandboxedVfsFile::FromSqliteFile(*file).Close();
30 int SandboxedRead(sqlite3_file* file,
33 sqlite3_int64 offset) {
34 return SandboxedVfsFile::FromSqliteFile(*file).Read(buffer, size, offset);
36 int SandboxedWrite(sqlite3_file* file,
39 sqlite3_int64 offset) {
40 return SandboxedVfsFile::FromSqliteFile(*file).Write(buffer, size, offset);
42 int SandboxedTruncate(sqlite3_file* file, sqlite3_int64 size) {
43 return SandboxedVfsFile::FromSqliteFile(*file).Truncate(size);
45 int SandboxedSync(sqlite3_file* file, int flags) {
46 return SandboxedVfsFile::FromSqliteFile(*file).Sync(flags);
48 int SandboxedFileSize(sqlite3_file* file, sqlite3_int64* result_size) {
49 return SandboxedVfsFile::FromSqliteFile(*file).FileSize(result_size);
51 int SandboxedLock(sqlite3_file* file, int mode) {
52 return SandboxedVfsFile::FromSqliteFile(*file).Lock(mode);
54 int SandboxedUnlock(sqlite3_file* file, int mode) {
55 return SandboxedVfsFile::FromSqliteFile(*file).Unlock(mode);
57 int SandboxedCheckReservedLock(sqlite3_file* file, int* has_reserved_lock) {
58 return SandboxedVfsFile::FromSqliteFile(*file).CheckReservedLock(
61 int SandboxedFileControl(sqlite3_file* file, int opcode, void* data) {
62 return SandboxedVfsFile::FromSqliteFile(*file).FileControl(opcode, data);
64 int SandboxedSectorSize(sqlite3_file* file) {
65 return SandboxedVfsFile::FromSqliteFile(*file).SectorSize();
67 int SandboxedDeviceCharacteristics(sqlite3_file* file) {
68 return SandboxedVfsFile::FromSqliteFile(*file).DeviceCharacteristics();
70 int SandboxedShmMap(sqlite3_file* file,
73 int extend_file_if_needed,
74 void volatile** result) {
75 return SandboxedVfsFile::FromSqliteFile(*file).ShmMap(
76 page_index, page_size, extend_file_if_needed, result);
78 int SandboxedShmLock(sqlite3_file* file, int offset, int size, int flags) {
79 return SandboxedVfsFile::FromSqliteFile(*file).ShmLock(offset, size, flags);
81 void SandboxedShmBarrier(sqlite3_file* file) {
82 SandboxedVfsFile::FromSqliteFile(*file).ShmBarrier();
84 int SandboxedShmUnmap(sqlite3_file* file, int also_delete_file) {
85 return SandboxedVfsFile::FromSqliteFile(*file).ShmUnmap(also_delete_file);
87 int SandboxedFetch(sqlite3_file* file,
91 return SandboxedVfsFile::FromSqliteFile(*file).Fetch(offset, size, result);
93 int SandboxedUnfetch(sqlite3_file* file,
96 return SandboxedVfsFile::FromSqliteFile(*file).Unfetch(offset, fetch_result);
99 const sqlite3_io_methods* GetSqliteIoMethods() {
100 // VFS IO API entry points are listed at
101 // https://www.sqlite.org/c3ref/io_methods.html
102 static constexpr int kSqliteVfsIoApiVersion = 3;
104 static const sqlite3_io_methods kIoMethods = {
105 kSqliteVfsIoApiVersion,
114 SandboxedCheckReservedLock,
115 SandboxedFileControl,
117 SandboxedDeviceCharacteristics,
132 void SandboxedVfsFile::Create(base::File file,
133 base::FilePath file_path,
135 SandboxedVfsFileType file_type,
136 #endif // DCHECK_IS_ON()
138 sqlite3_file& buffer) {
139 SandboxedVfsFileSqliteBridge& bridge =
140 SandboxedVfsFileSqliteBridge::FromSqliteFile(buffer);
141 bridge.sandboxed_vfs_file =
142 new SandboxedVfsFile(std::move(file), std::move(file_path),
145 #endif // DCHECK_IS_ON()
147 bridge.sqlite_file.pMethods = GetSqliteIoMethods();
151 SandboxedVfsFile& SandboxedVfsFile::FromSqliteFile(sqlite3_file& sqlite_file) {
152 return *SandboxedVfsFileSqliteBridge::FromSqliteFile(sqlite_file)
156 int SandboxedVfsFile::Close() {
162 int SandboxedVfsFile::Read(void* buffer, int size, sqlite3_int64 offset) {
165 DCHECK_GE(offset, 0);
168 // See http://www.sqlite.org/fileformat2.html#database_header
169 constexpr int kSqliteDatabaseHeaderOffset = 0;
170 constexpr int kSqliteDatabaseHeaderSize = 100;
171 // SQLite's locking protocol only acquires locks on the database file. The
172 // journal and the WAL file are always unlocked. Also, as an optimization,
173 // SQLite first reads the database header without locking the file.
174 DCHECK(sqlite_lock_mode_ > SQLITE_LOCK_NONE ||
175 file_type_ != SandboxedVfsFileType::kDatabase ||
176 (offset == kSqliteDatabaseHeaderOffset &&
177 size == kSqliteDatabaseHeaderSize))
178 << "Read from database file with lock mode " << sqlite_lock_mode_
179 << "of size" << size << " at offset " << offset;
180 #endif // DCHECK_IS_ON()
182 char* data = reinterpret_cast<char*>(buffer);
184 // If we supported mmap()ed files, we'd check for a memory mapping here,
185 // and try to fill as much of the request as possible from the mmap()ed
188 int bytes_read = file_.Read(offset, data, size);
189 DCHECK_LE(bytes_read, size);
190 if (bytes_read == size)
193 if (bytes_read < 0) {
194 // SQLite first reads the database header without locking the file. On
195 // Windows, this read will fail if there is an exclusive lock on the file,
196 // even if the current process owns that lock.
197 if (sqlite_lock_mode_ == SQLITE_LOCK_NONE) {
198 // The unlocked read is considered an optimization. SQLite can continue
199 // even if the read fails, as long as failure is communicated by zeroing
200 // out the output buffer.
201 std::memset(data, 0, size);
205 vfs_->SetLastError(base::File::GetLastFileError());
206 return SQLITE_IOERR_READ;
209 // SQLite requires that we fill the unread bytes in the buffer with zeros.
210 std::memset(data + bytes_read, 0, size - bytes_read);
211 return SQLITE_IOERR_SHORT_READ;
214 int SandboxedVfsFile::Write(const void* buffer,
216 sqlite3_int64 offset) {
219 DCHECK_GE(offset, 0);
222 // SQLite's locking protocol only acquires locks on the database file. The
223 // journal and the WAL file are always unlocked.
224 DCHECK(sqlite_lock_mode_ == SQLITE_LOCK_EXCLUSIVE ||
225 file_type_ != SandboxedVfsFileType::kDatabase)
226 << "Write to database file with lock mode " << sqlite_lock_mode_;
227 #endif // DCHECK_IS_ON()
229 const char* data = reinterpret_cast<const char*>(buffer);
231 // If we supported mmap()ed files, we'd check for a memory mapping here,
232 // and try to fill as much of the request as possible by copying to the
235 int bytes_written = file_.Write(offset, data, size);
236 DCHECK_LE(bytes_written, size);
237 if (bytes_written >= size)
240 base::File::Error last_error = base::File::GetLastFileError();
241 vfs_->SetLastError(last_error);
242 if (last_error == base::File::Error::FILE_ERROR_NO_SPACE)
245 return SQLITE_IOERR_WRITE;
248 int SandboxedVfsFile::Truncate(sqlite3_int64 size) {
249 if (file_.SetLength(size))
252 // On macOS < 10.15, the default sandbox blocks ftruncate(), so we have to use
253 // a sync mojo IPC to ask the browser process to call ftruncate() for us.
255 // TODO(crbug.com/1084565): Figure out if we can allow ftruncate() in renderer
256 // and utility processes. It would be useful for low-level storage APIs, like
257 // the upcoming filesystem API.
258 if (vfs_->delegate()->SetFileLength(file_path_, file_,
259 static_cast<size_t>(size))) {
263 return SQLITE_IOERR_TRUNCATE;
266 int SandboxedVfsFile::Sync(int flags) {
267 // NOTE: SQLite passes in (SQLITE_SYNC_NORMAL or SQLITE_SYNC_FULL),
268 // potentially OR-ed with SQLITE_SYNC_DATAONLY. Implementing these could
269 // lead to better performance.
270 if (!file_.Flush()) {
271 vfs_->SetLastError(base::File::GetLastFileError());
272 return SQLITE_IOERR_FSYNC;
275 // The unix VFS also syncs the file's directory on the first xSync() call.
276 // Chrome's LevelDB Env implementation does the same for specific files
277 // (database manifests).
279 // For WebSQL, we would want to sync the directory at file open time, when the
280 // file is opened for writing.
285 int SandboxedVfsFile::FileSize(sqlite3_int64* result_size) {
286 int64_t length = file_.GetLength();
288 vfs_->SetLastError(base::File::GetLastFileError());
289 return SQLITE_IOERR_FSTAT;
292 // SQLite's unix VFS reports 1-byte files as empty. This is documented as a
293 // workaround for a fairly obscure bug. See unixFileSize() in os_unix.c.
297 *result_size = length;
303 // True if our simplified implementation uses an exclusive lock for a mode.
304 bool IsExclusiveLockMode(int sqlite_lock_mode) {
305 switch (sqlite_lock_mode) {
306 case SQLITE_LOCK_NONE:
307 case SQLITE_LOCK_SHARED:
310 case SQLITE_LOCK_RESERVED:
311 case SQLITE_LOCK_PENDING:
312 case SQLITE_LOCK_EXCLUSIVE:
316 NOTREACHED() << "Unsupported mode: " << sqlite_lock_mode;
322 int SandboxedVfsFile::Lock(int mode) {
323 DCHECK_GT(mode, sqlite_lock_mode_)
324 << "SQLite asked the VFS to lock the file up to mode " << mode
325 << " but the file is already locked at mode " << sqlite_lock_mode_;
327 #if BUILDFLAG(IS_FUCHSIA)
328 return SQLITE_IOERR_LOCK;
330 base::File::LockMode file_lock_mode = base::File::LockMode::kExclusive;
333 case SQLITE_LOCK_NONE:
336 case SQLITE_LOCK_SHARED:
337 if (sqlite_lock_mode_ != SQLITE_LOCK_NONE)
340 file_lock_mode = base::File::LockMode::kShared;
343 case SQLITE_LOCK_RESERVED:
344 // A SHARED lock is required before a RESERVED lock is acquired.
345 DCHECK_EQ(sqlite_lock_mode_, SQLITE_LOCK_SHARED);
346 file_lock_mode = base::File::LockMode::kExclusive;
349 case SQLITE_LOCK_PENDING:
350 NOTREACHED() << "SQLite never directly asks for PENDING locks";
352 // Should we ever receive PENDING lock requests, the handler for
353 // EXCLUSIVE lock requests below happens to work perfectly.
356 case SQLITE_LOCK_EXCLUSIVE:
357 // A SHARED lock is required before an EXCLUSIVE lock is acquired.
359 // No higher level is required. In fact, SQLite upgrades the lock directly
360 // from SHARED to EXCLUSIVE when rolling back a transaction, to avoid
361 // having other readers queue up in the RESERVED state.
362 DCHECK_GE(sqlite_lock_mode_, SQLITE_LOCK_SHARED);
364 if (IsExclusiveLockMode(sqlite_lock_mode_)) {
365 sqlite_lock_mode_ = mode;
368 file_lock_mode = base::File::LockMode::kExclusive;
372 NOTREACHED() << "Unimplemented xLock() mode: " << mode;
375 DCHECK_EQ(IsExclusiveLockMode(mode),
376 file_lock_mode == base::File::LockMode::kExclusive)
377 << "Incorrect file_lock_mode logic for SQLite mode: " << mode;
379 // On POSIX, it would be possible to upgrade atomically from a shared lock to
380 // an exclusive lock. This implementation prioritizes the simplicity of no
381 // platform-specific code over being faster in high contention cases.
382 if (sqlite_lock_mode_ != SQLITE_LOCK_NONE) {
383 base::File::Error error = file_.Unlock();
384 if (error != base::File::FILE_OK) {
385 vfs_->SetLastError(base::File::GetLastFileError());
386 return SQLITE_IOERR_LOCK;
388 sqlite_lock_mode_ = SQLITE_LOCK_NONE;
391 base::File::Error error = file_.Lock(file_lock_mode);
392 if (error != base::File::FILE_OK) {
393 vfs_->SetLastError(base::File::GetLastFileError());
394 return SQLITE_IOERR_LOCK;
397 sqlite_lock_mode_ = mode;
399 #endif // BUILDFLAG(IS_FUCHSIA)
402 int SandboxedVfsFile::Unlock(int mode) {
403 // The 2nd term in the DCHECK predicate is there because SQLite occasionally
404 // attempts to unlock (to SQLITE_LOCK_NONE) a file that was already unlocked.
405 // We're not aware of any other case of no-op VFS unlock calls.
406 DCHECK(mode < sqlite_lock_mode_ ||
407 (mode == sqlite_lock_mode_ && mode == SQLITE_LOCK_NONE))
408 << "SQLite asked the VFS to unlock the file down to mode " << mode
409 << " but the file is already at mode " << sqlite_lock_mode_;
411 // No-op if we're already unlocked or at the requested mode.
412 if (sqlite_lock_mode_ == mode || sqlite_lock_mode_ == SQLITE_LOCK_NONE)
415 #if BUILDFLAG(IS_FUCHSIA)
416 return SQLITE_IOERR_UNLOCK;
418 // On POSIX, it is possible to downgrade atomically from an exclusive lock to
419 // a shared lock. SQLite's unix VFS takes advantage of this. This
420 // implementation prioritizes the simplicity of no platform-specific code over
421 // being faster in high contention cases.
422 base::File::Error error = file_.Unlock();
423 if (error != base::File::FILE_OK) {
424 vfs_->SetLastError(base::File::GetLastFileError());
425 return SQLITE_IOERR_UNLOCK;
428 if (mode == SQLITE_LOCK_NONE) {
429 sqlite_lock_mode_ = mode;
433 DCHECK_EQ(mode, SQLITE_LOCK_SHARED);
434 error = file_.Lock(base::File::LockMode::kShared);
435 if (error == base::File::FILE_OK) {
436 sqlite_lock_mode_ = mode;
440 // Gave up the exclusive lock, but failed to get a shared lock.
441 vfs_->SetLastError(base::File::GetLastFileError());
442 sqlite_lock_mode_ = SQLITE_LOCK_NONE;
443 return SQLITE_IOERR_UNLOCK;
444 #endif // BUILDFLAG(IS_FUCHSIA)
447 int SandboxedVfsFile::CheckReservedLock(int* has_reserved_lock) {
448 if (IsExclusiveLockMode(sqlite_lock_mode_)) {
449 *has_reserved_lock = 1;
453 if (sqlite_lock_mode_ == SQLITE_LOCK_SHARED) {
454 // Lock modes at or above RESERVED map to exclusive locks in our simplified
455 // implementation. If this process has a shared lock, no other process can
456 // have an exclusive lock.
457 *has_reserved_lock = 0;
461 #if BUILDFLAG(IS_FUCHSIA)
462 return SQLITE_IOERR_CHECKRESERVEDLOCK;
464 // On POSIX, it's possible to query the existing lock state of a file. The
465 // SQLite unix VFS takes advantage of this. On Windows, this isn't the case.
466 // Follow the strategy of the Windows VFS, which checks by trying to get an
467 // exclusive lock on the file.
468 base::File::Error error = file_.Lock(base::File::LockMode::kShared);
469 if (error != base::File::FILE_OK) {
470 *has_reserved_lock = 1;
474 *has_reserved_lock = 0;
475 if (file_.Unlock() == base::File::FILE_OK)
478 // We acquired a shared lock that we can't get rid of.
479 sqlite_lock_mode_ = SQLITE_LOCK_SHARED;
480 return SQLITE_IOERR_CHECKRESERVEDLOCK;
481 #endif // BUILDFLAG(IS_FUCHSIA)
484 int SandboxedVfsFile::FileControl(int opcode, void* data) {
486 case SQLITE_FCNTL_MMAP_SIZE:
487 // Implementing memory-mapping will require handling this correctly.
488 return SQLITE_NOTFOUND;
490 return SQLITE_NOTFOUND;
494 int SandboxedVfsFile::SectorSize() {
498 int SandboxedVfsFile::DeviceCharacteristics() {
499 // TODO(pwnall): Figure out if we can get away with returning 0 on Windows.
500 #if BUILDFLAG(IS_WIN)
501 return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN;
503 // NOTE: SQLite's unix VFS attempts to detect the underlying filesystem and
504 // sets some flags based on the result.
506 #endif // BUILDFLAG(IS_WIN)
509 int SandboxedVfsFile::ShmMap(int page_index,
511 int extend_file_if_needed,
512 void volatile** result) {
513 DCHECK_GE(page_index, 0);
514 DCHECK_GE(page_size, 0);
517 // https://www.sqlite.org/wal.html#use_of_wal_without_shared_memory states
518 // that SQLite only attempts to use shared memory "-shm" files for databases
519 // in WAL mode that may be accessed by multiple processes (are not EXCLUSIVE).
521 // Chrome will not only use WAL mode on EXCLUSIVE databases.
522 NOTREACHED() << "SQLite should not attempt to use shared memory";
528 int SandboxedVfsFile::ShmLock(int offset, int size, int flags) {
529 DCHECK_GE(offset, 0);
532 // https://www.sqlite.org/wal.html#use_of_wal_without_shared_memory states
533 // that SQLite only attempts to use shared memory "-shm" files for databases
534 // in WAL mode that may be accessed by multiple processes (are not EXCLUSIVE).
536 // Chrome will not only use WAL mode on EXCLUSIVE databases.
537 NOTREACHED() << "SQLite should not attempt to use shared memory";
542 void SandboxedVfsFile::ShmBarrier() {
543 // https://www.sqlite.org/wal.html#use_of_wal_without_shared_memory states
544 // that SQLite only attempts to use shared memory "-shm" files for databases
545 // in WAL mode that may be accessed by multiple processes (are not EXCLUSIVE).
547 // Chrome will not only use WAL mode on EXCLUSIVE databases.
548 NOTREACHED() << "SQLite should not attempt to use shared memory";
550 // All writes to shared memory that have already been issued before this
551 // function is called must complete before the function returns.
552 std::atomic_thread_fence(std::memory_order_acq_rel);
555 int SandboxedVfsFile::ShmUnmap(int also_delete_file) {
556 // https://www.sqlite.org/wal.html#use_of_wal_without_shared_memory states
557 // that SQLite only attempts to use shared memory "-shm" files for databases
558 // in WAL mode that may be accessed by multiple processes (are not EXCLUSIVE).
560 // Chrome will not only use WAL mode on EXCLUSIVE databases.
561 NOTREACHED() << "SQLite should not attempt to use shared memory";
566 int SandboxedVfsFile::Fetch(sqlite3_int64 offset, int size, void** result) {
567 DCHECK_GE(offset, 0);
571 // NOTE: This would be needed for mmap()ed file support.
576 int SandboxedVfsFile::Unfetch(sqlite3_int64 offset, void* fetch_result) {
577 DCHECK_GE(offset, 0);
578 DCHECK(fetch_result);
580 // NOTE: This would be needed for mmap()ed file support.
584 SandboxedVfsFile::SandboxedVfsFile(base::File file,
585 base::FilePath file_path,
587 SandboxedVfsFileType file_type,
588 #endif // DCHECK_IS_ON()
590 : file_(std::move(file)),
591 sqlite_lock_mode_(SQLITE_LOCK_NONE),
594 file_type_(file_type),
595 #endif // DCHECK_IS_ON()
596 file_path_(std::move(file_path)) {
599 SandboxedVfsFile::~SandboxedVfsFile() = default;
602 SandboxedVfsFileSqliteBridge& SandboxedVfsFileSqliteBridge::FromSqliteFile(
603 sqlite3_file& sqlite_file) {
604 static_assert(std::is_standard_layout<SandboxedVfsFileSqliteBridge>::value,
605 "needed for the reinterpret_cast below");
606 static_assert(offsetof(SandboxedVfsFileSqliteBridge, sqlite_file) == 0,
607 "sqlite_file must be the first member of the struct.");
609 SandboxedVfsFileSqliteBridge& bridge =
610 reinterpret_cast<SandboxedVfsFileSqliteBridge&>(sqlite_file);
611 DCHECK_EQ(&sqlite_file, &bridge.sqlite_file)
612 << "assumed by the reinterpret_casts in the implementation";