1 // Copyright (c) 2017 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
5 #include "sql/vfs_wrapper.h"
6
7 #include <algorithm>
8 #include <string>
9 #include <vector>
10
11 #include "base/check_op.h"
12 #include "base/debug/leak_annotations.h"
13 #include "base/files/file_path.h"
14 #include "base/memory/ptr_util.h"
15 #include "base/metrics/histogram_macros.h"
16 #include "base/notreached.h"
17 #include "base/strings/string_piece.h"
18 #include "build/build_config.h"
19
20 #if defined(OS_MAC)
21 #include "base/mac/mac_util.h"
22 #endif
23
24 #if defined(OS_FUCHSIA)
25 #include "sql/vfs_wrapper_fuchsia.h"
26 #endif
27
28 namespace sql {
29 namespace {
30
31 // https://www.sqlite.org/vfs.html - documents the overall VFS system.
32 //
33 // https://www.sqlite.org/c3ref/vfs.html - VFS methods. This code tucks the
34 // wrapped VFS pointer into the wrapper's pAppData pointer.
35 //
36 // https://www.sqlite.org/c3ref/file.html - instance of an open file. This code
37 // allocates a VfsFile for this, which contains a pointer to the wrapped file.
38 // Idiomatic SQLite would take the wrapped VFS szOsFile and increase it to store
39 // additional data as a prefix.
40
GetWrappedVfs(sqlite3_vfs * wrapped_vfs)41 sqlite3_vfs* GetWrappedVfs(sqlite3_vfs* wrapped_vfs) {
42 return static_cast<sqlite3_vfs*>(wrapped_vfs->pAppData);
43 }
44
AsVfsFile(sqlite3_file * wrapper_file)45 VfsFile* AsVfsFile(sqlite3_file* wrapper_file) {
46 return reinterpret_cast<VfsFile*>(wrapper_file);
47 }
48
GetWrappedFile(sqlite3_file * wrapper_file)49 sqlite3_file* GetWrappedFile(sqlite3_file* wrapper_file) {
50 return AsVfsFile(wrapper_file)->wrapped_file;
51 }
52
Close(sqlite3_file * sqlite_file)53 int Close(sqlite3_file* sqlite_file)
54 {
55 VfsFile* file = AsVfsFile(sqlite_file);
56
57 #if defined(OS_FUCHSIA)
58 FuchsiaVfsUnlock(sqlite_file, SQLITE_LOCK_NONE);
59 #endif
60
61 int r = file->wrapped_file->pMethods->xClose(file->wrapped_file);
62 sqlite3_free(file->wrapped_file);
63
64 // Memory will be freed with sqlite3_free(), so the destructor needs to be
65 // called explicitly.
66 file->~VfsFile();
67 memset(file, '\0', sizeof(*file));
68 return r;
69 }
70
Read(sqlite3_file * sqlite_file,void * buf,int amt,sqlite3_int64 ofs)71 int Read(sqlite3_file* sqlite_file, void* buf, int amt, sqlite3_int64 ofs)
72 {
73 sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
74 return wrapped_file->pMethods->xRead(wrapped_file, buf, amt, ofs);
75 }
76
Write(sqlite3_file * sqlite_file,const void * buf,int amt,sqlite3_int64 ofs)77 int Write(sqlite3_file* sqlite_file, const void* buf, int amt,
78 sqlite3_int64 ofs)
79 {
80 sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
81 return wrapped_file->pMethods->xWrite(wrapped_file, buf, amt, ofs);
82 }
83
Truncate(sqlite3_file * sqlite_file,sqlite3_int64 size)84 int Truncate(sqlite3_file* sqlite_file, sqlite3_int64 size)
85 {
86 sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
87 return wrapped_file->pMethods->xTruncate(wrapped_file, size);
88 }
89
Sync(sqlite3_file * sqlite_file,int flags)90 int Sync(sqlite3_file* sqlite_file, int flags)
91 {
92 sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
93 return wrapped_file->pMethods->xSync(wrapped_file, flags);
94 }
95
FileSize(sqlite3_file * sqlite_file,sqlite3_int64 * size)96 int FileSize(sqlite3_file* sqlite_file, sqlite3_int64* size)
97 {
98 sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
99 return wrapped_file->pMethods->xFileSize(wrapped_file, size);
100 }
101
102 #if !defined(OS_FUCHSIA)
103
Lock(sqlite3_file * sqlite_file,int file_lock)104 int Lock(sqlite3_file* sqlite_file, int file_lock)
105 {
106 sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
107 return wrapped_file->pMethods->xLock(wrapped_file, file_lock);
108 }
109
Unlock(sqlite3_file * sqlite_file,int file_lock)110 int Unlock(sqlite3_file* sqlite_file, int file_lock)
111 {
112 sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
113 return wrapped_file->pMethods->xUnlock(wrapped_file, file_lock);
114 }
115
CheckReservedLock(sqlite3_file * sqlite_file,int * result)116 int CheckReservedLock(sqlite3_file* sqlite_file, int* result)
117 {
118 sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
119 return wrapped_file->pMethods->xCheckReservedLock(wrapped_file, result);
120 }
121
122 #endif // !defined(OS_FUCHSIA)
123
FileControl(sqlite3_file * sqlite_file,int op,void * arg)124 int FileControl(sqlite3_file* sqlite_file, int op, void* arg)
125 {
126 sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
127 return wrapped_file->pMethods->xFileControl(wrapped_file, op, arg);
128 }
129
SectorSize(sqlite3_file * sqlite_file)130 int SectorSize(sqlite3_file* sqlite_file)
131 {
132 sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
133 return wrapped_file->pMethods->xSectorSize(wrapped_file);
134 }
135
DeviceCharacteristics(sqlite3_file * sqlite_file)136 int DeviceCharacteristics(sqlite3_file* sqlite_file)
137 {
138 sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
139 return wrapped_file->pMethods->xDeviceCharacteristics(wrapped_file);
140 }
141
ShmMap(sqlite3_file * sqlite_file,int region,int size,int extend,void volatile ** pp)142 int ShmMap(sqlite3_file *sqlite_file, int region, int size,
143 int extend, void volatile **pp) {
144 sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
145 return wrapped_file->pMethods->xShmMap(
146 wrapped_file, region, size, extend, pp);
147 }
148
ShmLock(sqlite3_file * sqlite_file,int ofst,int n,int flags)149 int ShmLock(sqlite3_file *sqlite_file, int ofst, int n, int flags) {
150 sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
151 return wrapped_file->pMethods->xShmLock(wrapped_file, ofst, n, flags);
152 }
153
ShmBarrier(sqlite3_file * sqlite_file)154 void ShmBarrier(sqlite3_file *sqlite_file) {
155 sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
156 wrapped_file->pMethods->xShmBarrier(wrapped_file);
157 }
158
ShmUnmap(sqlite3_file * sqlite_file,int del)159 int ShmUnmap(sqlite3_file *sqlite_file, int del) {
160 sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
161 return wrapped_file->pMethods->xShmUnmap(wrapped_file, del);
162 }
163
Fetch(sqlite3_file * sqlite_file,sqlite3_int64 off,int amt,void ** pp)164 int Fetch(sqlite3_file *sqlite_file, sqlite3_int64 off, int amt, void **pp) {
165 sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
166 return wrapped_file->pMethods->xFetch(wrapped_file, off, amt, pp);
167 }
168
Unfetch(sqlite3_file * sqlite_file,sqlite3_int64 off,void * p)169 int Unfetch(sqlite3_file *sqlite_file, sqlite3_int64 off, void *p) {
170 sqlite3_file* wrapped_file = GetWrappedFile(sqlite_file);
171 return wrapped_file->pMethods->xUnfetch(wrapped_file, off, p);
172 }
173
Open(sqlite3_vfs * vfs,const char * file_name,sqlite3_file * wrapper_file,int desired_flags,int * used_flags)174 int Open(sqlite3_vfs* vfs, const char* file_name, sqlite3_file* wrapper_file,
175 int desired_flags, int* used_flags) {
176 sqlite3_vfs* wrapped_vfs = GetWrappedVfs(vfs);
177
178 sqlite3_file* wrapped_file = static_cast<sqlite3_file*>(
179 sqlite3_malloc(wrapped_vfs->szOsFile));
180 if (!wrapped_file)
181 return SQLITE_NOMEM;
182
183 // NOTE(shess): SQLite's unixOpen() makes assumptions about the structure of
184 // |file_name|. Do not pass a local copy, here, only the passed-in value.
185 int rc = wrapped_vfs->xOpen(wrapped_vfs,
186 file_name, wrapped_file,
187 desired_flags, used_flags);
188 if (rc != SQLITE_OK) {
189 sqlite3_free(wrapped_file);
190 return rc;
191 }
192 // NOTE(shess): Any early exit from here needs to call xClose() on
193 // |wrapped_file|.
194
195 #if defined(OS_MAC)
196 // When opening journal files, propagate time-machine exclusion from db.
197 static int kJournalFlags =
198 SQLITE_OPEN_MAIN_JOURNAL | SQLITE_OPEN_TEMP_JOURNAL |
199 SQLITE_OPEN_SUBJOURNAL | SQLITE_OPEN_MASTER_JOURNAL;
200 if (file_name && (desired_flags & kJournalFlags)) {
201 // https://www.sqlite.org/c3ref/vfs.html indicates that the journal path
202 // will have a suffix separated by "-" from the main database file name.
203 base::StringPiece file_name_string_piece(file_name);
204 size_t dash_index = file_name_string_piece.rfind('-');
205 if (dash_index != base::StringPiece::npos) {
206 base::StringPiece db_name(file_name, dash_index);
207 if (base::mac::GetFileBackupExclusion(base::FilePath(db_name))) {
208 base::mac::SetFileBackupExclusion(
209 base::FilePath(file_name_string_piece));
210 }
211 }
212 }
213 #endif
214
215 // |iVersion| determines what methods SQLite may call on the instance.
216 // Having the methods which can't be proxied return an error may cause SQLite
217 // to operate differently than if it didn't call those methods at all. To be
218 // on the safe side, the wrapper sqlite3_io_methods version perfectly matches
219 // the version of the wrapped files.
220 //
221 // At a first glance, it might be tempting to simplify the code by
222 // restricting wrapping support to VFS version 3. However, this might fail on
223 // Mac.
224 //
225 // On Mac, SQLite built with SQLITE_ENABLE_LOCKING_STYLE ends up using a VFS
226 // that dynamically dispatches between a few variants of sqlite3_io_methods,
227 // based on whether the opened database is on a local or on a remote (AFS,
228 // NFS) filesystem. Some variants return a VFS version 1 structure.
229 VfsFile* file = AsVfsFile(wrapper_file);
230
231 // Call constructor explicitly since the memory is already allocated.
232 new (file) VfsFile();
233
234 file->wrapped_file = wrapped_file;
235
236 #if defined(OS_FUCHSIA)
237 file->file_name = file_name;
238 file->lock_level = SQLITE_LOCK_NONE;
239 #endif
240
241 if (wrapped_file->pMethods->iVersion == 1) {
242 static const sqlite3_io_methods io_methods = {
243 1,
244 Close,
245 Read,
246 Write,
247 Truncate,
248 Sync,
249 FileSize,
250 #if !defined(OS_FUCHSIA)
251 Lock,
252 Unlock,
253 CheckReservedLock,
254 #else
255 FuchsiaVfsLock,
256 FuchsiaVfsUnlock,
257 FuchsiaVfsCheckReservedLock,
258 #endif
259 FileControl,
260 SectorSize,
261 DeviceCharacteristics,
262 };
263 file->methods = &io_methods;
264 } else if (wrapped_file->pMethods->iVersion == 2) {
265 static const sqlite3_io_methods io_methods = {
266 2,
267 Close,
268 Read,
269 Write,
270 Truncate,
271 Sync,
272 FileSize,
273 #if !defined(OS_FUCHSIA)
274 Lock,
275 Unlock,
276 CheckReservedLock,
277 #else
278 FuchsiaVfsLock,
279 FuchsiaVfsUnlock,
280 FuchsiaVfsCheckReservedLock,
281 #endif
282 FileControl,
283 SectorSize,
284 DeviceCharacteristics,
285 // Methods above are valid for version 1.
286 ShmMap,
287 ShmLock,
288 ShmBarrier,
289 ShmUnmap,
290 };
291 file->methods = &io_methods;
292 } else {
293 static const sqlite3_io_methods io_methods = {
294 3,
295 Close,
296 Read,
297 Write,
298 Truncate,
299 Sync,
300 FileSize,
301 #if !defined(OS_FUCHSIA)
302 Lock,
303 Unlock,
304 CheckReservedLock,
305 #else
306 FuchsiaVfsLock,
307 FuchsiaVfsUnlock,
308 FuchsiaVfsCheckReservedLock,
309 #endif
310 FileControl,
311 SectorSize,
312 DeviceCharacteristics,
313 // Methods above are valid for version 1.
314 ShmMap,
315 ShmLock,
316 ShmBarrier,
317 ShmUnmap,
318 // Methods above are valid for version 2.
319 Fetch,
320 Unfetch,
321 };
322 file->methods = &io_methods;
323 }
324 return SQLITE_OK;
325 }
326
Delete(sqlite3_vfs * vfs,const char * file_name,int sync_dir)327 int Delete(sqlite3_vfs* vfs, const char* file_name, int sync_dir) {
328 sqlite3_vfs* wrapped_vfs = GetWrappedVfs(vfs);
329 return wrapped_vfs->xDelete(wrapped_vfs, file_name, sync_dir);
330 }
331
Access(sqlite3_vfs * vfs,const char * file_name,int flag,int * res)332 int Access(sqlite3_vfs* vfs, const char* file_name, int flag, int* res) {
333 sqlite3_vfs* wrapped_vfs = GetWrappedVfs(vfs);
334 return wrapped_vfs->xAccess(wrapped_vfs, file_name, flag, res);
335 }
336
FullPathname(sqlite3_vfs * vfs,const char * relative_path,int buf_size,char * absolute_path)337 int FullPathname(sqlite3_vfs* vfs, const char* relative_path,
338 int buf_size, char* absolute_path) {
339 sqlite3_vfs* wrapped_vfs = GetWrappedVfs(vfs);
340 return wrapped_vfs->xFullPathname(
341 wrapped_vfs, relative_path, buf_size, absolute_path);
342 }
343
Randomness(sqlite3_vfs * vfs,int buf_size,char * buffer)344 int Randomness(sqlite3_vfs* vfs, int buf_size, char* buffer) {
345 sqlite3_vfs* wrapped_vfs = GetWrappedVfs(vfs);
346 return wrapped_vfs->xRandomness(wrapped_vfs, buf_size, buffer);
347 }
348
Sleep(sqlite3_vfs * vfs,int microseconds)349 int Sleep(sqlite3_vfs* vfs, int microseconds) {
350 sqlite3_vfs* wrapped_vfs = GetWrappedVfs(vfs);
351 return wrapped_vfs->xSleep(wrapped_vfs, microseconds);
352 }
353
GetLastError(sqlite3_vfs * vfs,int e,char * s)354 int GetLastError(sqlite3_vfs* vfs, int e, char* s) {
355 sqlite3_vfs* wrapped_vfs = GetWrappedVfs(vfs);
356 return wrapped_vfs->xGetLastError(wrapped_vfs, e, s);
357 }
358
CurrentTimeInt64(sqlite3_vfs * vfs,sqlite3_int64 * now)359 int CurrentTimeInt64(sqlite3_vfs* vfs, sqlite3_int64* now) {
360 sqlite3_vfs* wrapped_vfs = GetWrappedVfs(vfs);
361 return wrapped_vfs->xCurrentTimeInt64(wrapped_vfs, now);
362 }
363
364 } // namespace
365
VFSWrapper()366 sqlite3_vfs* VFSWrapper() {
367 const char* kVFSName = "VFSWrapper";
368
369 // Return existing version if already registered.
370 {
371 sqlite3_vfs* vfs = sqlite3_vfs_find(kVFSName);
372 if (vfs)
373 return vfs;
374 }
375
376 // Get the default VFS on all platforms except Fuchsia.
377 const char* base_vfs_name = nullptr;
378 #if defined(OS_FUCHSIA)
379 base_vfs_name = "unix-none";
380 #endif
381 sqlite3_vfs* wrapped_vfs = sqlite3_vfs_find(base_vfs_name);
382
383 // Give up if there is no VFS implementation for the current platform.
384 if (!wrapped_vfs) {
385 NOTREACHED();
386 return nullptr;
387 }
388
389 std::unique_ptr<sqlite3_vfs, std::function<void(sqlite3_vfs*)>> wrapper_vfs(
390 static_cast<sqlite3_vfs*>(sqlite3_malloc(sizeof(sqlite3_vfs))),
391 [](sqlite3_vfs* v) {
392 sqlite3_free(v);
393 });
394 memset(wrapper_vfs.get(), '\0', sizeof(sqlite3_vfs));
395
396 // VFS implementations should always work with a SQLite that only knows about
397 // earlier versions.
398 constexpr int kSqliteVfsApiVersion = 3;
399 wrapper_vfs->iVersion = kSqliteVfsApiVersion;
400
401 // All the SQLite VFS implementations used by Chrome should support the
402 // version proxied here.
403 DCHECK_GE(wrapped_vfs->iVersion, kSqliteVfsApiVersion);
404
405 // Caller of xOpen() allocates this much space.
406 wrapper_vfs->szOsFile = sizeof(VfsFile);
407
408 wrapper_vfs->mxPathname = wrapped_vfs->mxPathname;
409 wrapper_vfs->pNext = nullptr; // Field used by SQLite.
410 wrapper_vfs->zName = kVFSName;
411
412 // Keep a reference to the wrapped vfs for use in methods.
413 wrapper_vfs->pAppData = wrapped_vfs;
414
415 // VFS methods.
416 wrapper_vfs->xOpen = &Open;
417 wrapper_vfs->xDelete = &Delete;
418 wrapper_vfs->xAccess = &Access;
419 wrapper_vfs->xFullPathname = &FullPathname;
420
421 // SQLite's dynamic extension loading is disabled in Chrome. Not proxying
422 // these methods lets us ship less logic and provides a tiny bit of extra
423 // security, as we know for sure that SQLite will not dynamically load code.
424 wrapper_vfs->xDlOpen = nullptr;
425 wrapper_vfs->xDlError = nullptr;
426 wrapper_vfs->xDlSym = nullptr;
427 wrapper_vfs->xDlClose = nullptr;
428
429 wrapper_vfs->xRandomness = &Randomness;
430 wrapper_vfs->xSleep = &Sleep;
431
432 // |xCurrentTime| is null when SQLite is built with SQLITE_OMIT_DEPRECATED, so
433 // it does not need to be proxied.
434 wrapper_vfs->xCurrentTime = nullptr;
435
436 wrapper_vfs->xGetLastError = &GetLastError;
437
438 // The methods above are in version 1 of SQLite's VFS API.
439
440 DCHECK(wrapped_vfs->xCurrentTimeInt64 != nullptr);
441 wrapper_vfs->xCurrentTimeInt64 = &CurrentTimeInt64;
442
443 // The methods above are in version 2 of SQLite's VFS API.
444
445 // The VFS system call interception API is intended for very low-level SQLite
446 // testing and tweaks. Proxying these methods is not necessary because Chrome
447 // does not do very low-level SQLite testing, and the VFS wrapper supports all
448 // the needed tweaks.
449 wrapper_vfs->xSetSystemCall = nullptr;
450 wrapper_vfs->xGetSystemCall = nullptr;
451 wrapper_vfs->xNextSystemCall = nullptr;
452
453 // The methods above are in version 3 of sqlite_vfs.
454
455 if (SQLITE_OK == sqlite3_vfs_register(wrapper_vfs.get(), 0)) {
456 ANNOTATE_LEAKING_OBJECT_PTR(wrapper_vfs.get());
457 wrapper_vfs.release();
458 }
459
460 return sqlite3_vfs_find(kVFSName);
461 }
462
463 } // namespace sql
464