Backed out 9 changesets (bug 1837168, bug 1835920, bug 1848783) for causing storage...
[gecko.git] / storage / ObfuscatingVFS.cpp
blobbba04f560f29b65be5591fba9bfe3e7750508e28
1 /*
2 ** 2020-04-20
3 **
4 ** The author disclaims copyright to this source code. In place of
5 ** a legal notice, here is a blessing:
6 **
7 ** May you do good and not evil.
8 ** May you find forgiveness for yourself and forgive others.
9 ** May you share freely, never taking more than you give.
11 ******************************************************************************
13 ** This file implements a VFS shim that obfuscates database content
14 ** written to disk by applying a CipherStrategy.
16 ** COMPILING
18 ** This extension requires SQLite 3.32.0 or later.
21 ** LOADING
23 ** Initialize it using a single API call as follows:
25 ** sqlite3_obfsvfs_init();
27 ** Obfsvfs is a VFS Shim. When loaded, "obfsvfs" becomes the new
28 ** default VFS and it uses the prior default VFS as the next VFS
29 ** down in the stack. This is normally what you want. However, it
30 ** complex situations where multiple VFS shims are being loaded,
31 ** it might be important to ensure that obfsvfs is loaded in the
32 ** correct order so that it sequences itself into the default VFS
33 ** Shim stack in the right order.
35 ** USING
37 ** Open database connections using the sqlite3_open_v2() with
38 ** the SQLITE_OPEN_URI flag and using a URI filename that includes
39 ** the query parameter "key=XXXXXXXXXXX..." where the XXXX... consists
40 ** of 64 hexadecimal digits (32 bytes of content).
42 ** Create a new encrypted database by opening a file that does not
43 ** yet exist using the key= query parameter.
45 ** LIMITATIONS:
47 ** * An obfuscated database must be created as such. There is
48 ** no way to convert an existing database file into an
49 ** obfuscated database file other than to run ".dump" on the
50 ** older database and reimport the SQL text into a new
51 ** obfuscated database.
53 ** * There is no way to change the key value, other than to
54 ** ".dump" and restore the database
56 ** * The database page size must be exactly 8192 bytes. No other
57 ** database page sizes are currently supported.
59 ** * Memory-mapped I/O does not work for obfuscated databases.
60 ** If you think about it, memory-mapped I/O doesn't make any
61 ** sense for obfuscated databases since you have to make a
62 ** copy of the content to deobfuscate anyhow - you might as
63 ** well use normal read()/write().
65 ** * Only the main database, the rollback journal, and WAL file
66 ** are obfuscated. Other temporary files used for things like
67 ** SAVEPOINTs or as part of a large external sort remain
68 ** unobfuscated.
70 ** * Requires SQLite 3.32.0 or later.
72 #include "sqlite3.h"
73 #include <string.h>
74 #include <ctype.h>
75 #include <stdio.h> /* For debugging only */
77 #include "mozilla/dom/quota/IPCStreamCipherStrategy.h"
78 #include "mozilla/ScopeExit.h"
79 #include "nsPrintfCString.h"
82 ** Forward declaration of objects used by this utility
84 using ObfsVfs = sqlite3_vfs;
87 ** Useful datatype abbreviations
89 #if !defined(SQLITE_CORE)
90 using u8 = unsigned char;
91 #endif
93 /* Access to a lower-level VFS that (might) implement dynamic loading,
94 ** access to randomness, etc.
96 #define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData))
97 #define ORIGFILE(p) ((sqlite3_file*)(((ObfsFile*)(p)) + 1))
100 ** Database page size for obfuscated databases
102 #define OBFS_PGSZ 8192
104 #define WAL_FRAMEHDRSIZE 24
106 using namespace mozilla;
107 using namespace mozilla::dom::quota;
109 /* An open file */
110 struct ObfsFile {
111 sqlite3_file base; /* IO methods */
112 const char* zFName; /* Original name of the file */
113 bool inCkpt; /* Currently doing a checkpoint */
114 ObfsFile* pPartner; /* Ptr from WAL to main-db, or from main-db to WAL */
115 void* pTemp; /* Temporary storage for encoded pages */
116 IPCStreamCipherStrategy*
117 encryptCipherStrategy; /* CipherStrategy for encryption */
118 IPCStreamCipherStrategy*
119 decryptCipherStrategy; /* CipherStrategy for decryption */
123 ** Methods for ObfsFile
125 static int obfsClose(sqlite3_file*);
126 static int obfsRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
127 static int obfsWrite(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst);
128 static int obfsTruncate(sqlite3_file*, sqlite3_int64 size);
129 static int obfsSync(sqlite3_file*, int flags);
130 static int obfsFileSize(sqlite3_file*, sqlite3_int64* pSize);
131 static int obfsLock(sqlite3_file*, int);
132 static int obfsUnlock(sqlite3_file*, int);
133 static int obfsCheckReservedLock(sqlite3_file*, int* pResOut);
134 static int obfsFileControl(sqlite3_file*, int op, void* pArg);
135 static int obfsSectorSize(sqlite3_file*);
136 static int obfsDeviceCharacteristics(sqlite3_file*);
137 static int obfsShmMap(sqlite3_file*, int iPg, int pgsz, int, void volatile**);
138 static int obfsShmLock(sqlite3_file*, int offset, int n, int flags);
139 static void obfsShmBarrier(sqlite3_file*);
140 static int obfsShmUnmap(sqlite3_file*, int deleteFlag);
141 static int obfsFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void** pp);
142 static int obfsUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void* p);
145 ** Methods for ObfsVfs
147 static int obfsOpen(sqlite3_vfs*, const char*, sqlite3_file*, int, int*);
148 static int obfsDelete(sqlite3_vfs*, const char* zPath, int syncDir);
149 static int obfsAccess(sqlite3_vfs*, const char* zPath, int flags, int*);
150 static int obfsFullPathname(sqlite3_vfs*, const char* zPath, int, char* zOut);
151 static void* obfsDlOpen(sqlite3_vfs*, const char* zPath);
152 static void obfsDlError(sqlite3_vfs*, int nByte, char* zErrMsg);
153 static void (*obfsDlSym(sqlite3_vfs* pVfs, void* p, const char* zSym))(void);
154 static void obfsDlClose(sqlite3_vfs*, void*);
155 static int obfsRandomness(sqlite3_vfs*, int nByte, char* zBufOut);
156 static int obfsSleep(sqlite3_vfs*, int nMicroseconds);
157 static int obfsCurrentTime(sqlite3_vfs*, double*);
158 static int obfsGetLastError(sqlite3_vfs*, int, char*);
159 static int obfsCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
160 static int obfsSetSystemCall(sqlite3_vfs*, const char*, sqlite3_syscall_ptr);
161 static sqlite3_syscall_ptr obfsGetSystemCall(sqlite3_vfs*, const char* z);
162 static const char* obfsNextSystemCall(sqlite3_vfs*, const char* zName);
164 static const sqlite3_io_methods obfs_io_methods = {
165 3, /* iVersion */
166 obfsClose, /* xClose */
167 obfsRead, /* xRead */
168 obfsWrite, /* xWrite */
169 obfsTruncate, /* xTruncate */
170 obfsSync, /* xSync */
171 obfsFileSize, /* xFileSize */
172 obfsLock, /* xLock */
173 obfsUnlock, /* xUnlock */
174 obfsCheckReservedLock, /* xCheckReservedLock */
175 obfsFileControl, /* xFileControl */
176 obfsSectorSize, /* xSectorSize */
177 obfsDeviceCharacteristics, /* xDeviceCharacteristics */
178 obfsShmMap, /* xShmMap */
179 obfsShmLock, /* xShmLock */
180 obfsShmBarrier, /* xShmBarrier */
181 obfsShmUnmap, /* xShmUnmap */
182 obfsFetch, /* xFetch */
183 obfsUnfetch /* xUnfetch */
186 static constexpr int kKeyBytes = 32;
187 static constexpr int kIvBytes = IPCStreamCipherStrategy::BlockPrefixLength;
188 static constexpr int kClearTextPrefixBytesOnFirstPage = 32;
189 static constexpr int kReservedBytes = 32;
190 static constexpr int kBasicBlockSize = IPCStreamCipherStrategy::BasicBlockSize;
191 static_assert(kClearTextPrefixBytesOnFirstPage % kBasicBlockSize == 0);
192 static_assert(kReservedBytes % kBasicBlockSize == 0);
194 /* Obfuscate a page using p->encryptCipherStrategy.
196 ** A new random nonce is created and stored in the last 32 bytes
197 ** of the page. All other bytes of the page are obfuscasted using the
198 ** CipherStrategy. Except, for page-1 (including the SQLite
199 ** database header) the first 32 bytes are not obfuscated
201 ** Return a pointer to the obfuscated content, which is held in the
202 ** p->pTemp buffer. Or return a NULL pointer if something goes wrong.
203 ** Errors are reported using NS_WARNING().
205 static void* obfsEncode(ObfsFile* p, /* File containing page to be obfuscated */
206 u8* a, /* database page to be obfuscated */
207 int nByte /* Bytes of content in a[]. Must be a multiple
208 of kBasicBlockSize. */
210 u8 aIv[kIvBytes];
211 u8* pOut;
212 int i;
214 static_assert((kIvBytes & (kIvBytes - 1)) == 0);
215 sqlite3_randomness(kIvBytes, aIv);
216 pOut = (u8*)p->pTemp;
217 if (pOut == nullptr) {
218 pOut = static_cast<u8*>(sqlite3_malloc64(nByte));
219 if (pOut == nullptr) {
220 NS_WARNING(nsPrintfCString("unable to allocate a buffer in which to"
221 " write obfuscated database content for %s",
222 p->zFName)
223 .get());
224 return nullptr;
226 p->pTemp = pOut;
228 if (memcmp(a, "SQLite format 3", 16) == 0) {
229 i = kClearTextPrefixBytesOnFirstPage;
230 if (a[20] != kReservedBytes) {
231 NS_WARNING(nsPrintfCString("obfuscated database must have reserved-bytes"
232 " set to %d",
233 kReservedBytes)
234 .get());
235 return nullptr;
237 memcpy(pOut, a, kClearTextPrefixBytesOnFirstPage);
238 } else {
239 i = 0;
241 const int payloadLength = nByte - kReservedBytes - i;
242 MOZ_ASSERT(payloadLength > 0);
243 // XXX I guess this can be done in-place as well, then we don't need the
244 // temporary page at all, I guess?
245 p->encryptCipherStrategy->Cipher(
246 Span{aIv}, Span{a + i, static_cast<unsigned>(payloadLength)},
247 Span{pOut + i, static_cast<unsigned>(payloadLength)});
248 memcpy(pOut + nByte - kReservedBytes, aIv, kIvBytes);
250 return pOut;
253 /* De-obfuscate a page using p->decryptCipherStrategy.
255 ** The deobfuscation is done in-place.
257 ** For pages that begin with the SQLite header text, the first
258 ** 32 bytes are not deobfuscated.
260 static void obfsDecode(ObfsFile* p, /* File containing page to be obfuscated */
261 u8* a, /* database page to be obfuscated */
262 int nByte /* Bytes of content in a[]. Must be a multiple
263 of kBasicBlockSize. */
265 int i;
267 if (memcmp(a, "SQLite format 3", 16) == 0) {
268 i = kClearTextPrefixBytesOnFirstPage;
269 } else {
270 i = 0;
272 const int payloadLength = nByte - kReservedBytes - i;
273 MOZ_ASSERT(payloadLength > 0);
274 p->decryptCipherStrategy->Cipher(
275 Span{a + nByte - kReservedBytes, kIvBytes},
276 Span{a + i, static_cast<unsigned>(payloadLength)},
277 Span{a + i, static_cast<unsigned>(payloadLength)});
278 memset(a + nByte - kReservedBytes, 0, kIvBytes);
282 ** Close an obfsucated file.
284 static int obfsClose(sqlite3_file* pFile) {
285 ObfsFile* p = (ObfsFile*)pFile;
286 if (p->pPartner) {
287 MOZ_ASSERT(p->pPartner->pPartner == p);
288 p->pPartner->pPartner = nullptr;
289 p->pPartner = nullptr;
291 sqlite3_free(p->pTemp);
293 delete p->decryptCipherStrategy;
294 delete p->encryptCipherStrategy;
296 pFile = ORIGFILE(pFile);
297 return pFile->pMethods->xClose(pFile);
301 ** Read data from an obfuscated file.
303 ** If the file is less than one full page in length, then return
304 ** a substitute "prototype" page-1. This prototype page one
305 ** specifies a database in WAL mode with an 8192-byte page size
306 ** and a 32-byte reserved-bytes value. Those settings are necessary
307 ** for obfuscation to function correctly.
309 static int obfsRead(sqlite3_file* pFile, void* zBuf, int iAmt,
310 sqlite_int64 iOfst) {
311 int rc;
312 ObfsFile* p = (ObfsFile*)pFile;
313 pFile = ORIGFILE(pFile);
314 rc = pFile->pMethods->xRead(pFile, zBuf, iAmt, iOfst);
315 if (rc == SQLITE_OK) {
316 if ((iAmt == OBFS_PGSZ || iAmt == OBFS_PGSZ + WAL_FRAMEHDRSIZE) &&
317 !p->inCkpt) {
318 obfsDecode(p, ((u8*)zBuf) + iAmt - OBFS_PGSZ, OBFS_PGSZ);
320 } else if (rc == SQLITE_IOERR_SHORT_READ && iOfst == 0 && iAmt >= 100) {
321 static const unsigned char aEmptyDb[] = {
322 // Offset 0, Size 16, The header string: "SQLite format 3\000"
323 0x53, 0x51, 0x4c, 0x69, 0x74, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61,
324 0x74, 0x20, 0x33, 0x00,
325 // XXX Add description for other fields
326 0x20, 0x00, 0x02, 0x02, kReservedBytes, 0x40, 0x20, 0x20, 0x00, 0x00,
327 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
328 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
329 0x00, 0x00,
330 // Offset 52, Size 4, The page number of the largest root b-tree page
331 // when in auto-vacuum or incremental-vacuum modes, or zero otherwise.
332 0x00, 0x00, 0x00, 0x01};
334 memcpy(zBuf, aEmptyDb, sizeof(aEmptyDb));
335 memset(((u8*)zBuf) + sizeof(aEmptyDb), 0, iAmt - sizeof(aEmptyDb));
336 rc = SQLITE_OK;
338 return rc;
342 ** Write data to an obfuscated file or journal.
344 static int obfsWrite(sqlite3_file* pFile, const void* zBuf, int iAmt,
345 sqlite_int64 iOfst) {
346 ObfsFile* p = (ObfsFile*)pFile;
347 pFile = ORIGFILE(pFile);
348 if (iAmt == OBFS_PGSZ && !p->inCkpt) {
349 zBuf = obfsEncode(p, (u8*)zBuf, iAmt);
350 if (zBuf == nullptr) {
351 return SQLITE_IOERR;
354 return pFile->pMethods->xWrite(pFile, zBuf, iAmt, iOfst);
358 ** Truncate an obfuscated file.
360 static int obfsTruncate(sqlite3_file* pFile, sqlite_int64 size) {
361 pFile = ORIGFILE(pFile);
362 return pFile->pMethods->xTruncate(pFile, size);
366 ** Sync an obfuscated file.
368 static int obfsSync(sqlite3_file* pFile, int flags) {
369 pFile = ORIGFILE(pFile);
370 return pFile->pMethods->xSync(pFile, flags);
374 ** Return the current file-size of an obfuscated file.
376 static int obfsFileSize(sqlite3_file* pFile, sqlite_int64* pSize) {
377 ObfsFile* p = (ObfsFile*)pFile;
378 pFile = ORIGFILE(p);
379 return pFile->pMethods->xFileSize(pFile, pSize);
383 ** Lock an obfuscated file.
385 static int obfsLock(sqlite3_file* pFile, int eLock) {
386 pFile = ORIGFILE(pFile);
387 return pFile->pMethods->xLock(pFile, eLock);
391 ** Unlock an obfuscated file.
393 static int obfsUnlock(sqlite3_file* pFile, int eLock) {
394 pFile = ORIGFILE(pFile);
395 return pFile->pMethods->xUnlock(pFile, eLock);
399 ** Check if another file-handle holds a RESERVED lock on an obfuscated file.
401 static int obfsCheckReservedLock(sqlite3_file* pFile, int* pResOut) {
402 pFile = ORIGFILE(pFile);
403 return pFile->pMethods->xCheckReservedLock(pFile, pResOut);
407 ** File control method. For custom operations on an obfuscated file.
409 static int obfsFileControl(sqlite3_file* pFile, int op, void* pArg) {
410 int rc;
411 ObfsFile* p = (ObfsFile*)pFile;
412 pFile = ORIGFILE(pFile);
413 if (op == SQLITE_FCNTL_PRAGMA) {
414 char** azArg = (char**)pArg;
415 MOZ_ASSERT(azArg[1] != nullptr);
416 if (azArg[2] != nullptr && sqlite3_stricmp(azArg[1], "page_size") == 0) {
417 /* Do not allow page size changes on an obfuscated database */
418 return SQLITE_OK;
420 } else if (op == SQLITE_FCNTL_CKPT_START || op == SQLITE_FCNTL_CKPT_DONE) {
421 p->inCkpt = op == SQLITE_FCNTL_CKPT_START;
422 if (p->pPartner) {
423 p->pPartner->inCkpt = p->inCkpt;
426 rc = pFile->pMethods->xFileControl(pFile, op, pArg);
427 if (rc == SQLITE_OK && op == SQLITE_FCNTL_VFSNAME) {
428 *(char**)pArg = sqlite3_mprintf("obfs/%z", *(char**)pArg);
430 return rc;
434 ** Return the sector-size in bytes for an obfuscated file.
436 static int obfsSectorSize(sqlite3_file* pFile) {
437 pFile = ORIGFILE(pFile);
438 return pFile->pMethods->xSectorSize(pFile);
442 ** Return the device characteristic flags supported by an obfuscated file.
444 static int obfsDeviceCharacteristics(sqlite3_file* pFile) {
445 pFile = ORIGFILE(pFile);
446 return pFile->pMethods->xDeviceCharacteristics(pFile);
449 /* Create a shared memory file mapping */
450 static int obfsShmMap(sqlite3_file* pFile, int iPg, int pgsz, int bExtend,
451 void volatile** pp) {
452 pFile = ORIGFILE(pFile);
453 return pFile->pMethods->xShmMap(pFile, iPg, pgsz, bExtend, pp);
456 /* Perform locking on a shared-memory segment */
457 static int obfsShmLock(sqlite3_file* pFile, int offset, int n, int flags) {
458 pFile = ORIGFILE(pFile);
459 return pFile->pMethods->xShmLock(pFile, offset, n, flags);
462 /* Memory barrier operation on shared memory */
463 static void obfsShmBarrier(sqlite3_file* pFile) {
464 pFile = ORIGFILE(pFile);
465 pFile->pMethods->xShmBarrier(pFile);
468 /* Unmap a shared memory segment */
469 static int obfsShmUnmap(sqlite3_file* pFile, int deleteFlag) {
470 pFile = ORIGFILE(pFile);
471 return pFile->pMethods->xShmUnmap(pFile, deleteFlag);
474 /* Fetch a page of a memory-mapped file */
475 static int obfsFetch(sqlite3_file* pFile, sqlite3_int64 iOfst, int iAmt,
476 void** pp) {
477 *pp = nullptr;
478 return SQLITE_OK;
481 /* Release a memory-mapped page */
482 static int obfsUnfetch(sqlite3_file* pFile, sqlite3_int64 iOfst, void* pPage) {
483 pFile = ORIGFILE(pFile);
484 return pFile->pMethods->xUnfetch(pFile, iOfst, pPage);
488 ** Translate a single byte of Hex into an integer.
489 ** This routine only works if h really is a valid hexadecimal
490 ** character: 0..9a..fA..F
492 static u8 obfsHexToInt(int h) {
493 MOZ_ASSERT((h >= '0' && h <= '9') || (h >= 'a' && h <= 'f') ||
494 (h >= 'A' && h <= 'F'));
495 #if 1 /* ASCII */
496 h += 9 * (1 & (h >> 6));
497 #else /* EBCDIC */
498 h += 9 * (1 & ~(h >> 4));
499 #endif
500 return (u8)(h & 0xf);
504 ** Open a new file.
506 ** If the file is an ordinary database file, or a rollback or WAL journal
507 ** file, and if the key=XXXX parameter exists, then try to open the file
508 ** as an obfuscated database. All other open attempts fall through into
509 ** the lower-level VFS shim.
511 ** If the key=XXXX parameter exists but is not 64-bytes of hex key, then
512 ** put an error message in NS_WARNING() and return SQLITE_CANTOPEN.
514 static int obfsOpen(sqlite3_vfs* pVfs, const char* zName, sqlite3_file* pFile,
515 int flags, int* pOutFlags) {
516 ObfsFile* p;
517 sqlite3_file* pSubFile;
518 sqlite3_vfs* pSubVfs;
519 int rc, i;
520 const char* zKey;
521 u8 aKey[kKeyBytes];
522 pSubVfs = ORIGVFS(pVfs);
523 if (flags &
524 (SQLITE_OPEN_MAIN_DB | SQLITE_OPEN_WAL | SQLITE_OPEN_MAIN_JOURNAL)) {
525 zKey = sqlite3_uri_parameter(zName, "key");
526 } else {
527 zKey = nullptr;
529 if (zKey == nullptr) {
530 return pSubVfs->xOpen(pSubVfs, zName, pFile, flags, pOutFlags);
532 for (i = 0;
533 i < kKeyBytes && isxdigit(zKey[i * 2]) && isxdigit(zKey[i * 2 + 1]);
534 i++) {
535 aKey[i] = (obfsHexToInt(zKey[i * 2]) << 4) | obfsHexToInt(zKey[i * 2 + 1]);
537 if (i != kKeyBytes) {
538 NS_WARNING(
539 nsPrintfCString("invalid query parameter on %s: key=%s", zName, zKey)
540 .get());
541 return SQLITE_CANTOPEN;
543 p = (ObfsFile*)pFile;
544 memset(p, 0, sizeof(*p));
546 auto encryptCipherStrategy = MakeUnique<IPCStreamCipherStrategy>();
547 auto decryptCipherStrategy = MakeUnique<IPCStreamCipherStrategy>();
549 auto resetMethods = MakeScopeExit([pFile] { pFile->pMethods = nullptr; });
551 if (NS_WARN_IF(NS_FAILED(encryptCipherStrategy->Init(
552 CipherMode::Encrypt, Span{aKey, sizeof(aKey)},
553 IPCStreamCipherStrategy::MakeBlockPrefix())))) {
554 return SQLITE_ERROR;
557 if (NS_WARN_IF(NS_FAILED(decryptCipherStrategy->Init(
558 CipherMode::Decrypt, Span{aKey, sizeof(aKey)})))) {
559 return SQLITE_ERROR;
562 pSubFile = ORIGFILE(pFile);
563 p->base.pMethods = &obfs_io_methods;
564 rc = pSubVfs->xOpen(pSubVfs, zName, pSubFile, flags, pOutFlags);
565 if (rc) {
566 return rc;
569 resetMethods.release();
571 if (flags & (SQLITE_OPEN_WAL | SQLITE_OPEN_MAIN_JOURNAL)) {
572 sqlite3_file* pDb = sqlite3_database_file_object(zName);
573 p->pPartner = (ObfsFile*)pDb;
574 MOZ_ASSERT(p->pPartner->pPartner == nullptr);
575 p->pPartner->pPartner = p;
577 p->zFName = zName;
579 p->encryptCipherStrategy = encryptCipherStrategy.release();
580 p->decryptCipherStrategy = decryptCipherStrategy.release();
582 return SQLITE_OK;
586 ** All other VFS methods are pass-thrus.
588 static int obfsDelete(sqlite3_vfs* pVfs, const char* zPath, int syncDir) {
589 return ORIGVFS(pVfs)->xDelete(ORIGVFS(pVfs), zPath, syncDir);
591 static int obfsAccess(sqlite3_vfs* pVfs, const char* zPath, int flags,
592 int* pResOut) {
593 return ORIGVFS(pVfs)->xAccess(ORIGVFS(pVfs), zPath, flags, pResOut);
595 static int obfsFullPathname(sqlite3_vfs* pVfs, const char* zPath, int nOut,
596 char* zOut) {
597 return ORIGVFS(pVfs)->xFullPathname(ORIGVFS(pVfs), zPath, nOut, zOut);
599 static void* obfsDlOpen(sqlite3_vfs* pVfs, const char* zPath) {
600 return ORIGVFS(pVfs)->xDlOpen(ORIGVFS(pVfs), zPath);
602 static void obfsDlError(sqlite3_vfs* pVfs, int nByte, char* zErrMsg) {
603 ORIGVFS(pVfs)->xDlError(ORIGVFS(pVfs), nByte, zErrMsg);
605 static void (*obfsDlSym(sqlite3_vfs* pVfs, void* p, const char* zSym))(void) {
606 return ORIGVFS(pVfs)->xDlSym(ORIGVFS(pVfs), p, zSym);
608 static void obfsDlClose(sqlite3_vfs* pVfs, void* pHandle) {
609 ORIGVFS(pVfs)->xDlClose(ORIGVFS(pVfs), pHandle);
611 static int obfsRandomness(sqlite3_vfs* pVfs, int nByte, char* zBufOut) {
612 return ORIGVFS(pVfs)->xRandomness(ORIGVFS(pVfs), nByte, zBufOut);
614 static int obfsSleep(sqlite3_vfs* pVfs, int nMicroseconds) {
615 return ORIGVFS(pVfs)->xSleep(ORIGVFS(pVfs), nMicroseconds);
617 static int obfsCurrentTime(sqlite3_vfs* pVfs, double* pTimeOut) {
618 return ORIGVFS(pVfs)->xCurrentTime(ORIGVFS(pVfs), pTimeOut);
620 static int obfsGetLastError(sqlite3_vfs* pVfs, int a, char* b) {
621 return ORIGVFS(pVfs)->xGetLastError(ORIGVFS(pVfs), a, b);
623 static int obfsCurrentTimeInt64(sqlite3_vfs* pVfs, sqlite3_int64* p) {
624 return ORIGVFS(pVfs)->xCurrentTimeInt64(ORIGVFS(pVfs), p);
626 static int obfsSetSystemCall(sqlite3_vfs* pVfs, const char* zName,
627 sqlite3_syscall_ptr pCall) {
628 return ORIGVFS(pVfs)->xSetSystemCall(ORIGVFS(pVfs), zName, pCall);
630 static sqlite3_syscall_ptr obfsGetSystemCall(sqlite3_vfs* pVfs,
631 const char* zName) {
632 return ORIGVFS(pVfs)->xGetSystemCall(ORIGVFS(pVfs), zName);
634 static const char* obfsNextSystemCall(sqlite3_vfs* pVfs, const char* zName) {
635 return ORIGVFS(pVfs)->xNextSystemCall(ORIGVFS(pVfs), zName);
638 namespace mozilla {
639 namespace storage {
641 const char* GetObfuscatingVFSName() { return "obfsvfs"; }
643 UniquePtr<sqlite3_vfs> ConstructObfuscatingVFS(const char* aBaseVFSName) {
644 MOZ_ASSERT(aBaseVFSName);
646 if (sqlite3_vfs_find(GetObfuscatingVFSName()) != nullptr) {
647 return nullptr;
649 sqlite3_vfs* const pOrig = sqlite3_vfs_find(aBaseVFSName);
650 if (pOrig == nullptr) {
651 return nullptr;
654 #ifdef DEBUG
655 // If the VFS version is higher than the last known one, you should update
656 // this VFS adding appropriate methods for any methods added in the version
657 // change.
658 static constexpr int kLastKnownVfsVersion = 3;
659 MOZ_ASSERT(pOrig->iVersion <= kLastKnownVfsVersion);
660 #endif
662 const sqlite3_vfs obfs_vfs = {
663 pOrig->iVersion, /* iVersion */
664 static_cast<int>(pOrig->szOsFile + sizeof(ObfsFile)), /* szOsFile */
665 pOrig->mxPathname, /* mxPathname */
666 nullptr, /* pNext */
667 GetObfuscatingVFSName(), /* zName */
668 pOrig, /* pAppData */
669 obfsOpen, /* xOpen */
670 obfsDelete, /* xDelete */
671 obfsAccess, /* xAccess */
672 obfsFullPathname, /* xFullPathname */
673 obfsDlOpen, /* xDlOpen */
674 obfsDlError, /* xDlError */
675 obfsDlSym, /* xDlSym */
676 obfsDlClose, /* xDlClose */
677 obfsRandomness, /* xRandomness */
678 obfsSleep, /* xSleep */
679 obfsCurrentTime, /* xCurrentTime */
680 obfsGetLastError, /* xGetLastError */
681 obfsCurrentTimeInt64, /* xCurrentTimeInt64 */
682 obfsSetSystemCall, /* xSetSystemCall */
683 obfsGetSystemCall, /* xGetSystemCall */
684 obfsNextSystemCall /* xNextSystemCall */
687 return MakeUnique<sqlite3_vfs>(obfs_vfs);
690 } // namespace storage
691 } // namespace mozilla