Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 1 | // Copyright 2019 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Ken Rockot | 7655fcb6 | 2020-08-12 19:22:40 | [diff] [blame] | 5 | #include "sql/sandboxed_vfs_file.h" |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 6 | |
| 7 | #include <atomic> |
| 8 | #include <cstring> |
| 9 | #include <type_traits> |
| 10 | #include <utility> |
| 11 | |
Hans Wennborg | fc109c9 | 2020-05-01 00:21:37 | [diff] [blame] | 12 | #include "base/check_op.h" |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 13 | #include "base/files/file.h" |
Hans Wennborg | fc109c9 | 2020-05-01 00:21:37 | [diff] [blame] | 14 | #include "base/notreached.h" |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 15 | #include "base/threading/platform_thread.h" |
| 16 | #include "build/build_config.h" |
| 17 | #include "sql/initialization.h" |
Ken Rockot | 7655fcb6 | 2020-08-12 19:22:40 | [diff] [blame] | 18 | #include "sql/sandboxed_vfs.h" |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 19 | #include "third_party/sqlite/sqlite3.h" |
| 20 | |
Ken Rockot | 7655fcb6 | 2020-08-12 19:22:40 | [diff] [blame] | 21 | namespace sql { |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 22 | |
| 23 | namespace { |
| 24 | |
| 25 | int SandboxedClose(sqlite3_file* file) { |
Ken Rockot | 7655fcb6 | 2020-08-12 19:22:40 | [diff] [blame] | 26 | return SandboxedVfsFile::FromSqliteFile(*file).Close(); |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 27 | } |
| 28 | int SandboxedRead(sqlite3_file* file, |
| 29 | void* buffer, |
| 30 | int size, |
| 31 | sqlite3_int64 offset) { |
Ken Rockot | 7655fcb6 | 2020-08-12 19:22:40 | [diff] [blame] | 32 | return SandboxedVfsFile::FromSqliteFile(*file).Read(buffer, size, offset); |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 33 | } |
| 34 | int SandboxedWrite(sqlite3_file* file, |
| 35 | const void* buffer, |
| 36 | int size, |
| 37 | sqlite3_int64 offset) { |
Ken Rockot | 7655fcb6 | 2020-08-12 19:22:40 | [diff] [blame] | 38 | return SandboxedVfsFile::FromSqliteFile(*file).Write(buffer, size, offset); |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 39 | } |
| 40 | int SandboxedTruncate(sqlite3_file* file, sqlite3_int64 size) { |
Ken Rockot | 7655fcb6 | 2020-08-12 19:22:40 | [diff] [blame] | 41 | return SandboxedVfsFile::FromSqliteFile(*file).Truncate(size); |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 42 | } |
| 43 | int SandboxedSync(sqlite3_file* file, int flags) { |
Ken Rockot | 7655fcb6 | 2020-08-12 19:22:40 | [diff] [blame] | 44 | return SandboxedVfsFile::FromSqliteFile(*file).Sync(flags); |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 45 | } |
| 46 | int SandboxedFileSize(sqlite3_file* file, sqlite3_int64* result_size) { |
Ken Rockot | 7655fcb6 | 2020-08-12 19:22:40 | [diff] [blame] | 47 | return SandboxedVfsFile::FromSqliteFile(*file).FileSize(result_size); |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 48 | } |
| 49 | int SandboxedLock(sqlite3_file* file, int mode) { |
Ken Rockot | 7655fcb6 | 2020-08-12 19:22:40 | [diff] [blame] | 50 | return SandboxedVfsFile::FromSqliteFile(*file).Lock(mode); |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 51 | } |
| 52 | int SandboxedUnlock(sqlite3_file* file, int mode) { |
Ken Rockot | 7655fcb6 | 2020-08-12 19:22:40 | [diff] [blame] | 53 | return SandboxedVfsFile::FromSqliteFile(*file).Unlock(mode); |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 54 | } |
| 55 | int SandboxedCheckReservedLock(sqlite3_file* file, int* has_reserved_lock) { |
Ken Rockot | 7655fcb6 | 2020-08-12 19:22:40 | [diff] [blame] | 56 | return SandboxedVfsFile::FromSqliteFile(*file).CheckReservedLock( |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 57 | has_reserved_lock); |
| 58 | } |
| 59 | int SandboxedFileControl(sqlite3_file* file, int opcode, void* data) { |
Ken Rockot | 7655fcb6 | 2020-08-12 19:22:40 | [diff] [blame] | 60 | return SandboxedVfsFile::FromSqliteFile(*file).FileControl(opcode, data); |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 61 | } |
| 62 | int SandboxedSectorSize(sqlite3_file* file) { |
Ken Rockot | 7655fcb6 | 2020-08-12 19:22:40 | [diff] [blame] | 63 | return SandboxedVfsFile::FromSqliteFile(*file).SectorSize(); |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 64 | } |
| 65 | int SandboxedDeviceCharacteristics(sqlite3_file* file) { |
Ken Rockot | 7655fcb6 | 2020-08-12 19:22:40 | [diff] [blame] | 66 | return SandboxedVfsFile::FromSqliteFile(*file).DeviceCharacteristics(); |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 67 | } |
| 68 | int SandboxedShmMap(sqlite3_file* file, |
| 69 | int page_index, |
| 70 | int page_size, |
| 71 | int extend_file_if_needed, |
| 72 | void volatile** result) { |
Ken Rockot | 7655fcb6 | 2020-08-12 19:22:40 | [diff] [blame] | 73 | return SandboxedVfsFile::FromSqliteFile(*file).ShmMap( |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 74 | page_index, page_size, extend_file_if_needed, result); |
| 75 | } |
| 76 | int SandboxedShmLock(sqlite3_file* file, int offset, int size, int flags) { |
Ken Rockot | 7655fcb6 | 2020-08-12 19:22:40 | [diff] [blame] | 77 | return SandboxedVfsFile::FromSqliteFile(*file).ShmLock(offset, size, flags); |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 78 | } |
| 79 | void SandboxedShmBarrier(sqlite3_file* file) { |
Ken Rockot | 7655fcb6 | 2020-08-12 19:22:40 | [diff] [blame] | 80 | SandboxedVfsFile::FromSqliteFile(*file).ShmBarrier(); |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 81 | } |
| 82 | int SandboxedShmUnmap(sqlite3_file* file, int also_delete_file) { |
Ken Rockot | 7655fcb6 | 2020-08-12 19:22:40 | [diff] [blame] | 83 | return SandboxedVfsFile::FromSqliteFile(*file).ShmUnmap(also_delete_file); |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 84 | } |
| 85 | int SandboxedFetch(sqlite3_file* file, |
| 86 | sqlite3_int64 offset, |
| 87 | int size, |
| 88 | void** result) { |
Ken Rockot | 7655fcb6 | 2020-08-12 19:22:40 | [diff] [blame] | 89 | return SandboxedVfsFile::FromSqliteFile(*file).Fetch(offset, size, result); |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 90 | } |
| 91 | int SandboxedUnfetch(sqlite3_file* file, |
| 92 | sqlite3_int64 offset, |
| 93 | void* fetch_result) { |
Ken Rockot | 7655fcb6 | 2020-08-12 19:22:40 | [diff] [blame] | 94 | return SandboxedVfsFile::FromSqliteFile(*file).Unfetch(offset, fetch_result); |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 95 | } |
| 96 | |
| 97 | const sqlite3_io_methods* GetSqliteIoMethods() { |
| 98 | // VFS IO API entry points are listed at |
| 99 | // https://www.sqlite.org/c3ref/io_methods.html |
| 100 | static constexpr int kSqliteVfsIoApiVersion = 3; |
| 101 | |
| 102 | static const sqlite3_io_methods kIoMethods = { |
| 103 | kSqliteVfsIoApiVersion, |
| 104 | SandboxedClose, |
| 105 | SandboxedRead, |
| 106 | SandboxedWrite, |
| 107 | SandboxedTruncate, |
| 108 | SandboxedSync, |
| 109 | SandboxedFileSize, |
| 110 | SandboxedLock, |
| 111 | SandboxedUnlock, |
| 112 | SandboxedCheckReservedLock, |
| 113 | SandboxedFileControl, |
| 114 | SandboxedSectorSize, |
| 115 | SandboxedDeviceCharacteristics, |
| 116 | SandboxedShmMap, |
| 117 | SandboxedShmLock, |
| 118 | SandboxedShmBarrier, |
| 119 | SandboxedShmUnmap, |
| 120 | SandboxedFetch, |
| 121 | SandboxedUnfetch, |
| 122 | }; |
| 123 | |
| 124 | return &kIoMethods; |
| 125 | } |
| 126 | |
| 127 | } // namespace |
| 128 | |
| 129 | // static |
| 130 | void SandboxedVfsFile::Create(base::File file, |
Ken Rockot | 7655fcb6 | 2020-08-12 19:22:40 | [diff] [blame] | 131 | base::FilePath file_path, |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 132 | SandboxedVfs* vfs, |
Ken Rockot | 7655fcb6 | 2020-08-12 19:22:40 | [diff] [blame] | 133 | sqlite3_file& buffer) { |
| 134 | SandboxedVfsFileSqliteBridge& bridge = |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 135 | SandboxedVfsFileSqliteBridge::FromSqliteFile(buffer); |
Ken Rockot | 7655fcb6 | 2020-08-12 19:22:40 | [diff] [blame] | 136 | bridge.sandboxed_vfs_file = |
| 137 | new SandboxedVfsFile(std::move(file), std::move(file_path), vfs); |
| 138 | bridge.sqlite_file.pMethods = GetSqliteIoMethods(); |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 139 | } |
| 140 | |
| 141 | // static |
Ken Rockot | 7655fcb6 | 2020-08-12 19:22:40 | [diff] [blame] | 142 | SandboxedVfsFile& SandboxedVfsFile::FromSqliteFile(sqlite3_file& sqlite_file) { |
| 143 | return *SandboxedVfsFileSqliteBridge::FromSqliteFile(sqlite_file) |
| 144 | .sandboxed_vfs_file; |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 145 | } |
| 146 | |
| 147 | int SandboxedVfsFile::Close() { |
| 148 | file_.Close(); |
Victor Costan | 99fe0c1 | 2019-10-07 21:06:12 | [diff] [blame] | 149 | delete this; |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 150 | return SQLITE_OK; |
| 151 | } |
| 152 | |
| 153 | int SandboxedVfsFile::Read(void* buffer, int size, sqlite3_int64 offset) { |
| 154 | DCHECK(buffer); |
| 155 | DCHECK_GE(size, 0); |
| 156 | DCHECK_GE(offset, 0); |
| 157 | char* data = reinterpret_cast<char*>(buffer); |
| 158 | |
| 159 | // If we supported mmap()ed files, we'd check for a memory mapping here, |
| 160 | // and try to fill as much of the request as possible from the mmap()ed |
| 161 | // region. |
| 162 | |
| 163 | int bytes_read = file_.Read(offset, data, size); |
| 164 | DCHECK_LE(bytes_read, size); |
| 165 | if (bytes_read == size) |
| 166 | return SQLITE_OK; |
| 167 | |
| 168 | // SQLite first reads the database header without locking the file. On |
| 169 | // Windows, this read will fail if there is an exclusive lock on the file, |
| 170 | // even if the current process owns that lock. |
| 171 | if (sqlite_lock_mode_ == SQLITE_LOCK_NONE) { |
| 172 | // The unlocked read is considered an optimization. SQLite can continue even |
| 173 | // if the read fails, as long as failure is communicated by zeroing out the |
| 174 | // output buffer. |
| 175 | std::memset(data, 0, size); |
| 176 | return SQLITE_OK; |
| 177 | } |
| 178 | |
| 179 | if (bytes_read < 0) { |
| 180 | vfs_->SetLastError(base::File::GetLastFileError()); |
| 181 | return SQLITE_IOERR_READ; |
| 182 | } |
| 183 | |
| 184 | // SQLite requires that we fill the unread bytes in the buffer with zeros. |
| 185 | std::memset(data + bytes_read, 0, size - bytes_read); |
| 186 | return SQLITE_IOERR_SHORT_READ; |
| 187 | } |
| 188 | |
| 189 | int SandboxedVfsFile::Write(const void* buffer, |
| 190 | int size, |
| 191 | sqlite3_int64 offset) { |
| 192 | DCHECK(buffer); |
| 193 | DCHECK_GE(size, 0); |
| 194 | DCHECK_GE(offset, 0); |
| 195 | const char* data = reinterpret_cast<const char*>(buffer); |
| 196 | |
| 197 | // If we supported mmap()ed files, we'd check for a memory mapping here, |
| 198 | // and try to fill as much of the request as possible by copying to the |
| 199 | // mmap()ed region. |
| 200 | |
| 201 | int bytes_written = file_.Write(offset, data, size); |
| 202 | DCHECK_LE(bytes_written, size); |
| 203 | if (bytes_written >= size) |
| 204 | return SQLITE_OK; |
| 205 | |
| 206 | base::File::Error last_error = base::File::GetLastFileError(); |
| 207 | vfs_->SetLastError(last_error); |
| 208 | if (last_error == base::File::Error::FILE_ERROR_NO_SPACE) |
| 209 | return SQLITE_FULL; |
| 210 | |
| 211 | return SQLITE_IOERR_WRITE; |
| 212 | } |
| 213 | |
| 214 | int SandboxedVfsFile::Truncate(sqlite3_int64 size) { |
| 215 | if (file_.SetLength(size)) |
| 216 | return SQLITE_OK; |
| 217 | |
Victor Costan | 959eab0 | 2020-08-17 18:33:56 | [diff] [blame] | 218 | // On macOS < 10.15, the default sandbox blocks ftruncate(), so we have to use |
| 219 | // a sync mojo IPC to ask the browser process to call ftruncate() for us. |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 220 | // |
Ken Rockot | 7655fcb6 | 2020-08-12 19:22:40 | [diff] [blame] | 221 | // TODO(crbug.com/1084565): Figure out if we can allow ftruncate() in renderer |
| 222 | // and utility processes. It would be useful for low-level storage APIs, like |
| 223 | // the upcoming filesystem API. |
| 224 | if (vfs_->delegate()->SetFileLength(file_path_, file_, |
| 225 | static_cast<size_t>(size))) { |
| 226 | return SQLITE_OK; |
| 227 | } |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 228 | |
Ken Rockot | 7655fcb6 | 2020-08-12 19:22:40 | [diff] [blame] | 229 | return SQLITE_IOERR_TRUNCATE; |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 230 | } |
| 231 | |
| 232 | int SandboxedVfsFile::Sync(int flags) { |
| 233 | // NOTE: SQLite passes in (SQLITE_SYNC_NORMAL or SQLITE_SYNC_FULL), |
| 234 | // potentially OR-ed with SQLITE_SYNC_DATAONLY. Implementing these could |
| 235 | // lead to better performance. |
| 236 | if (!file_.Flush()) { |
| 237 | vfs_->SetLastError(base::File::GetLastFileError()); |
| 238 | return SQLITE_IOERR_FSYNC; |
| 239 | } |
| 240 | |
| 241 | // The unix VFS also syncs the file's directory on the first xSync() call. |
| 242 | // Chrome's LevelDB Env implementation does the same for specific files |
| 243 | // (database manifests). |
| 244 | // |
| 245 | // For WebSQL, we would want to sync the directory at file open time, when the |
| 246 | // file is opened for writing. |
| 247 | |
| 248 | return SQLITE_OK; |
| 249 | } |
| 250 | |
| 251 | int SandboxedVfsFile::FileSize(sqlite3_int64* result_size) { |
| 252 | int64_t length = file_.GetLength(); |
| 253 | if (length < 0) { |
| 254 | vfs_->SetLastError(base::File::GetLastFileError()); |
| 255 | return SQLITE_IOERR_FSTAT; |
| 256 | } |
| 257 | |
| 258 | // SQLite's unix VFS reports 1-byte files as empty. This is documented as a |
| 259 | // workaround for a fairly obscure bug. See unixFileSize() in os_unix.c. |
| 260 | if (length == 1) |
| 261 | length = 0; |
| 262 | |
| 263 | *result_size = length; |
| 264 | return SQLITE_OK; |
| 265 | } |
| 266 | |
| 267 | namespace { |
| 268 | |
| 269 | // True if our simplified implementation uses an exclusive lock for a mode. |
| 270 | bool IsExclusiveLockMode(int sqlite_lock_mode) { |
| 271 | switch (sqlite_lock_mode) { |
| 272 | case SQLITE_LOCK_NONE: |
| 273 | case SQLITE_LOCK_SHARED: |
| 274 | return false; |
| 275 | |
| 276 | case SQLITE_LOCK_RESERVED: |
| 277 | case SQLITE_LOCK_PENDING: |
| 278 | case SQLITE_LOCK_EXCLUSIVE: |
| 279 | return true; |
| 280 | } |
| 281 | |
| 282 | NOTREACHED() << "Unsupported mode: " << sqlite_lock_mode; |
| 283 | return false; |
| 284 | } |
| 285 | |
| 286 | } // namespace |
| 287 | |
| 288 | int SandboxedVfsFile::Lock(int mode) { |
| 289 | #if defined(OS_FUCHSIA) |
| 290 | return SQLITE_IOERR_LOCK; |
| 291 | #else |
| 292 | base::File::LockMode file_lock_mode = base::File::LockMode::kExclusive; |
| 293 | |
| 294 | switch (mode) { |
| 295 | case SQLITE_LOCK_NONE: |
| 296 | return SQLITE_OK; |
| 297 | |
| 298 | case SQLITE_LOCK_SHARED: |
| 299 | if (sqlite_lock_mode_ != SQLITE_LOCK_NONE) |
| 300 | return SQLITE_OK; |
| 301 | |
| 302 | file_lock_mode = base::File::LockMode::kShared; |
| 303 | break; |
| 304 | |
| 305 | case SQLITE_LOCK_RESERVED: |
| 306 | // A SHARED lock is required before a RESERVED lock is acquired. |
| 307 | DCHECK_EQ(sqlite_lock_mode_, SQLITE_LOCK_SHARED); |
| 308 | file_lock_mode = base::File::LockMode::kExclusive; |
| 309 | break; |
| 310 | |
| 311 | case SQLITE_LOCK_PENDING: |
| 312 | // A SHARED lock is required before a PENDING lock is acquired. The caller |
| 313 | // may have a RESERVED lock. |
| 314 | if (sqlite_lock_mode_ == SQLITE_LOCK_RESERVED) { |
| 315 | sqlite_lock_mode_ = mode; |
| 316 | return SQLITE_OK; |
| 317 | } |
| 318 | |
| 319 | DCHECK_EQ(sqlite_lock_mode_, SQLITE_LOCK_SHARED); |
| 320 | file_lock_mode = base::File::LockMode::kExclusive; |
| 321 | break; |
| 322 | |
| 323 | case SQLITE_LOCK_EXCLUSIVE: |
| 324 | if (IsExclusiveLockMode(sqlite_lock_mode_)) { |
| 325 | sqlite_lock_mode_ = mode; |
| 326 | return SQLITE_OK; |
| 327 | } |
| 328 | file_lock_mode = base::File::LockMode::kExclusive; |
| 329 | break; |
| 330 | |
| 331 | default: |
| 332 | NOTREACHED() << "Unimplemented xLock() mode: " << mode; |
| 333 | } |
| 334 | |
| 335 | DCHECK_EQ(IsExclusiveLockMode(mode), |
| 336 | file_lock_mode == base::File::LockMode::kExclusive) |
| 337 | << "Incorrect file_lock_mode logic for SQLite mode: " << mode; |
| 338 | |
| 339 | // On POSIX, it would be possible to upgrade atomically from a shared lock to |
| 340 | // an exclusive lock. This implementation prioritizes the simplicity of no |
| 341 | // platform-specific code over being faster in high contention cases. |
| 342 | if (sqlite_lock_mode_ != SQLITE_LOCK_NONE) { |
| 343 | base::File::Error error = file_.Unlock(); |
| 344 | if (error != base::File::FILE_OK) { |
| 345 | vfs_->SetLastError(base::File::GetLastFileError()); |
| 346 | return SQLITE_IOERR_LOCK; |
| 347 | } |
| 348 | sqlite_lock_mode_ = SQLITE_LOCK_NONE; |
| 349 | } |
| 350 | |
| 351 | base::File::Error error = file_.Lock(file_lock_mode); |
| 352 | if (error != base::File::FILE_OK) { |
| 353 | vfs_->SetLastError(base::File::GetLastFileError()); |
| 354 | return SQLITE_IOERR_LOCK; |
| 355 | } |
| 356 | |
| 357 | sqlite_lock_mode_ = mode; |
| 358 | return SQLITE_OK; |
| 359 | #endif // defined(OS_FUCHSIA) |
| 360 | } |
| 361 | |
| 362 | int SandboxedVfsFile::Unlock(int mode) { |
| 363 | // No-op if we're already unlocked or at the requested mode. |
| 364 | if (sqlite_lock_mode_ == mode || sqlite_lock_mode_ == SQLITE_LOCK_NONE) |
| 365 | return SQLITE_OK; |
| 366 | |
| 367 | #if defined(OS_FUCHSIA) |
| 368 | return SQLITE_IOERR_UNLOCK; |
| 369 | #else |
| 370 | // On POSIX, it is possible to downgrade atomically from an exclusive lock to |
| 371 | // a shared lock. SQLite's unix VFS takes advantage of this. This |
| 372 | // implementation prioritizes the simplicity of no platform-specific code over |
| 373 | // being faster in high contention cases. |
| 374 | base::File::Error error = file_.Unlock(); |
| 375 | if (error != base::File::FILE_OK) { |
| 376 | vfs_->SetLastError(base::File::GetLastFileError()); |
| 377 | return SQLITE_IOERR_UNLOCK; |
| 378 | } |
| 379 | |
| 380 | if (mode == SQLITE_LOCK_NONE) { |
| 381 | sqlite_lock_mode_ = mode; |
| 382 | return SQLITE_OK; |
| 383 | } |
| 384 | |
| 385 | DCHECK_EQ(mode, SQLITE_LOCK_SHARED); |
| 386 | error = file_.Lock(base::File::LockMode::kShared); |
| 387 | if (error == base::File::FILE_OK) { |
| 388 | sqlite_lock_mode_ = mode; |
| 389 | return SQLITE_OK; |
| 390 | } |
| 391 | |
| 392 | // Gave up the exclusive lock, but failed to get a shared lock. |
| 393 | vfs_->SetLastError(base::File::GetLastFileError()); |
| 394 | sqlite_lock_mode_ = SQLITE_LOCK_NONE; |
| 395 | return SQLITE_IOERR_UNLOCK; |
| 396 | #endif // defined(OS_FUCHSIA) |
| 397 | } |
| 398 | |
| 399 | int SandboxedVfsFile::CheckReservedLock(int* has_reserved_lock) { |
| 400 | if (IsExclusiveLockMode(sqlite_lock_mode_)) { |
| 401 | *has_reserved_lock = 1; |
| 402 | return SQLITE_OK; |
| 403 | } |
| 404 | |
| 405 | if (sqlite_lock_mode_ == SQLITE_LOCK_SHARED) { |
| 406 | // Lock modes at or above RESERVED map to exclusive locks in our simplified |
| 407 | // implementation. If this process has a shared lock, no other process can |
| 408 | // have an exclusive lock. |
| 409 | *has_reserved_lock = 0; |
| 410 | return SQLITE_OK; |
| 411 | } |
| 412 | |
| 413 | #if defined(OS_FUCHSIA) |
| 414 | return SQLITE_IOERR_CHECKRESERVEDLOCK; |
| 415 | #else |
| 416 | // On POSIX, it's possible to query the existing lock state of a file. The |
| 417 | // SQLite unix VFS takes advantage of this. On Windows, this isn't the case. |
| 418 | // Follow the strategy of the Windows VFS, which checks by trying to get an |
| 419 | // exclusive lock on the file. |
| 420 | base::File::Error error = file_.Lock(base::File::LockMode::kShared); |
| 421 | if (error != base::File::FILE_OK) { |
| 422 | *has_reserved_lock = 1; |
| 423 | return SQLITE_OK; |
| 424 | } |
| 425 | |
| 426 | *has_reserved_lock = 0; |
| 427 | if (file_.Unlock() == base::File::FILE_OK) |
| 428 | return SQLITE_OK; |
| 429 | |
| 430 | // We acquired a shared lock that we can't get rid of. |
| 431 | sqlite_lock_mode_ = SQLITE_LOCK_SHARED; |
| 432 | return SQLITE_IOERR_CHECKRESERVEDLOCK; |
| 433 | #endif // defined(OS_FUCHSIA) |
| 434 | } |
| 435 | |
| 436 | int SandboxedVfsFile::FileControl(int opcode, void* data) { |
| 437 | switch (opcode) { |
| 438 | case SQLITE_FCNTL_MMAP_SIZE: |
| 439 | // Implementing memory-mapping will require handling this correctly. |
| 440 | return SQLITE_NOTFOUND; |
| 441 | default: |
| 442 | return SQLITE_NOTFOUND; |
| 443 | } |
| 444 | } |
| 445 | |
| 446 | int SandboxedVfsFile::SectorSize() { |
| 447 | return 0; |
| 448 | } |
| 449 | |
| 450 | int SandboxedVfsFile::DeviceCharacteristics() { |
| 451 | // TODO(pwnall): Figure out if we can get away with returning 0 on Windows. |
| 452 | #if defined(OS_WIN) |
| 453 | return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN; |
| 454 | #else |
| 455 | // NOTE: SQLite's unix VFS attempts to detect the underlying filesystem and |
| 456 | // sets some flags based on the result. |
| 457 | return 0; |
| 458 | #endif // OS_WIN |
| 459 | } |
| 460 | |
| 461 | int SandboxedVfsFile::ShmMap(int page_index, |
| 462 | int page_size, |
| 463 | int extend_file_if_needed, |
| 464 | void volatile** result) { |
| 465 | DCHECK_GE(page_index, 0); |
| 466 | DCHECK_GE(page_size, 0); |
| 467 | DCHECK(result); |
| 468 | |
Victor Costan | 959eab0 | 2020-08-17 18:33:56 | [diff] [blame] | 469 | // https://www.sqlite.org/wal.html#use_of_wal_without_shared_memory states |
| 470 | // that SQLite only attempts to use shared memory "-shm" files for databases |
| 471 | // in WAL mode that may be accessed by multiple processes (are not EXCLUSIVE). |
| 472 | // |
| 473 | // Chrome will not only use WAL mode on EXCLUSIVE databases. |
| 474 | NOTREACHED() << "SQLite should not attempt to use shared memory"; |
| 475 | |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 476 | *result = nullptr; |
| 477 | return SQLITE_IOERR; |
| 478 | } |
| 479 | |
| 480 | int SandboxedVfsFile::ShmLock(int offset, int size, int flags) { |
| 481 | DCHECK_GE(offset, 0); |
| 482 | DCHECK_GE(size, 0); |
| 483 | |
Victor Costan | 959eab0 | 2020-08-17 18:33:56 | [diff] [blame] | 484 | // https://www.sqlite.org/wal.html#use_of_wal_without_shared_memory states |
| 485 | // that SQLite only attempts to use shared memory "-shm" files for databases |
| 486 | // in WAL mode that may be accessed by multiple processes (are not EXCLUSIVE). |
| 487 | // |
| 488 | // Chrome will not only use WAL mode on EXCLUSIVE databases. |
| 489 | NOTREACHED() << "SQLite should not attempt to use shared memory"; |
| 490 | |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 491 | return SQLITE_IOERR; |
| 492 | } |
| 493 | |
| 494 | void SandboxedVfsFile::ShmBarrier() { |
Victor Costan | 959eab0 | 2020-08-17 18:33:56 | [diff] [blame] | 495 | // https://www.sqlite.org/wal.html#use_of_wal_without_shared_memory states |
| 496 | // that SQLite only attempts to use shared memory "-shm" files for databases |
| 497 | // in WAL mode that may be accessed by multiple processes (are not EXCLUSIVE). |
| 498 | // |
| 499 | // Chrome will not only use WAL mode on EXCLUSIVE databases. |
| 500 | NOTREACHED() << "SQLite should not attempt to use shared memory"; |
| 501 | |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 502 | // All writes to shared memory that have already been issued before this |
| 503 | // function is called must complete before the function returns. |
| 504 | std::atomic_thread_fence(std::memory_order_acq_rel); |
| 505 | } |
| 506 | |
| 507 | int SandboxedVfsFile::ShmUnmap(int also_delete_file) { |
Victor Costan | 959eab0 | 2020-08-17 18:33:56 | [diff] [blame] | 508 | // https://www.sqlite.org/wal.html#use_of_wal_without_shared_memory states |
| 509 | // that SQLite only attempts to use shared memory "-shm" files for databases |
| 510 | // in WAL mode that may be accessed by multiple processes (are not EXCLUSIVE). |
| 511 | // |
| 512 | // Chrome will not only use WAL mode on EXCLUSIVE databases. |
| 513 | NOTREACHED() << "SQLite should not attempt to use shared memory"; |
| 514 | |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 515 | return SQLITE_IOERR; |
| 516 | } |
| 517 | |
| 518 | int SandboxedVfsFile::Fetch(sqlite3_int64 offset, int size, void** result) { |
| 519 | DCHECK_GE(offset, 0); |
| 520 | DCHECK_GE(size, 0); |
| 521 | DCHECK(result); |
| 522 | |
| 523 | // NOTE: This would be needed for mmap()ed file support. |
| 524 | *result = nullptr; |
| 525 | return SQLITE_IOERR; |
| 526 | } |
| 527 | |
| 528 | int SandboxedVfsFile::Unfetch(sqlite3_int64 offset, void* fetch_result) { |
| 529 | DCHECK_GE(offset, 0); |
| 530 | DCHECK(fetch_result); |
| 531 | |
| 532 | // NOTE: This would be needed for mmap()ed file support. |
| 533 | return SQLITE_IOERR; |
| 534 | } |
| 535 | |
| 536 | SandboxedVfsFile::SandboxedVfsFile(base::File file, |
Ken Rockot | 7655fcb6 | 2020-08-12 19:22:40 | [diff] [blame] | 537 | base::FilePath file_path, |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 538 | SandboxedVfs* vfs) |
| 539 | : file_(std::move(file)), |
| 540 | sqlite_lock_mode_(SQLITE_LOCK_NONE), |
| 541 | vfs_(vfs), |
Ken Rockot | 7655fcb6 | 2020-08-12 19:22:40 | [diff] [blame] | 542 | file_path_(std::move(file_path)) {} |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 543 | |
| 544 | SandboxedVfsFile::~SandboxedVfsFile() = default; |
| 545 | |
| 546 | // static |
Ken Rockot | 7655fcb6 | 2020-08-12 19:22:40 | [diff] [blame] | 547 | SandboxedVfsFileSqliteBridge& SandboxedVfsFileSqliteBridge::FromSqliteFile( |
| 548 | sqlite3_file& sqlite_file) { |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 549 | static_assert(std::is_standard_layout<SandboxedVfsFileSqliteBridge>::value, |
| 550 | "needed for the reinterpret_cast below"); |
| 551 | static_assert(offsetof(SandboxedVfsFileSqliteBridge, sqlite_file) == 0, |
| 552 | "sqlite_file must be the first member of the struct."); |
| 553 | |
Ken Rockot | 7655fcb6 | 2020-08-12 19:22:40 | [diff] [blame] | 554 | SandboxedVfsFileSqliteBridge& bridge = |
| 555 | reinterpret_cast<SandboxedVfsFileSqliteBridge&>(sqlite_file); |
| 556 | DCHECK_EQ(&sqlite_file, &bridge.sqlite_file) |
Victor Costan | 451bb9cf | 2019-03-16 04:48:07 | [diff] [blame] | 557 | << "assumed by the reinterpret_casts in the implementation"; |
| 558 | return bridge; |
| 559 | } |
| 560 | |
Ken Rockot | 7655fcb6 | 2020-08-12 19:22:40 | [diff] [blame] | 561 | } // namespace sql |