Bug 1732032 [wpt PR 30868] - fix: canShare() return false if not allowed to use,...
[gecko.git] / storage / ObfuscatingVFS.cpp
blob58a7d9bf68e82d5f6f3ed0eb2b01a6266c5119bb
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 using namespace mozilla;
105 using namespace mozilla::dom::quota;
107 /* An open file */
108 struct ObfsFile {
109 sqlite3_file base; /* IO methods */
110 const char* zFName; /* Original name of the file */
111 bool inCkpt; /* Currently doing a checkpoint */
112 ObfsFile* pPartner; /* Ptr from WAL to main-db, or from main-db to WAL */
113 void* pTemp; /* Temporary storage for encoded pages */
114 IPCStreamCipherStrategy*
115 encryptCipherStrategy; /* CipherStrategy for encryption */
116 IPCStreamCipherStrategy*
117 decryptCipherStrategy; /* CipherStrategy for decryption */
121 ** Methods for ObfsFile
123 static int obfsClose(sqlite3_file*);
124 static int obfsRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
125 static int obfsWrite(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst);
126 static int obfsTruncate(sqlite3_file*, sqlite3_int64 size);
127 static int obfsSync(sqlite3_file*, int flags);
128 static int obfsFileSize(sqlite3_file*, sqlite3_int64* pSize);
129 static int obfsLock(sqlite3_file*, int);
130 static int obfsUnlock(sqlite3_file*, int);
131 static int obfsCheckReservedLock(sqlite3_file*, int* pResOut);
132 static int obfsFileControl(sqlite3_file*, int op, void* pArg);
133 static int obfsSectorSize(sqlite3_file*);
134 static int obfsDeviceCharacteristics(sqlite3_file*);
135 static int obfsShmMap(sqlite3_file*, int iPg, int pgsz, int, void volatile**);
136 static int obfsShmLock(sqlite3_file*, int offset, int n, int flags);
137 static void obfsShmBarrier(sqlite3_file*);
138 static int obfsShmUnmap(sqlite3_file*, int deleteFlag);
139 static int obfsFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void** pp);
140 static int obfsUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void* p);
143 ** Methods for ObfsVfs
145 static int obfsOpen(sqlite3_vfs*, const char*, sqlite3_file*, int, int*);
146 static int obfsDelete(sqlite3_vfs*, const char* zPath, int syncDir);
147 static int obfsAccess(sqlite3_vfs*, const char* zPath, int flags, int*);
148 static int obfsFullPathname(sqlite3_vfs*, const char* zPath, int, char* zOut);
149 static void* obfsDlOpen(sqlite3_vfs*, const char* zPath);
150 static void obfsDlError(sqlite3_vfs*, int nByte, char* zErrMsg);
151 static void (*obfsDlSym(sqlite3_vfs* pVfs, void* p, const char* zSym))(void);
152 static void obfsDlClose(sqlite3_vfs*, void*);
153 static int obfsRandomness(sqlite3_vfs*, int nByte, char* zBufOut);
154 static int obfsSleep(sqlite3_vfs*, int nMicroseconds);
155 static int obfsCurrentTime(sqlite3_vfs*, double*);
156 static int obfsGetLastError(sqlite3_vfs*, int, char*);
157 static int obfsCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
158 static int obfsSetSystemCall(sqlite3_vfs*, const char*, sqlite3_syscall_ptr);
159 static sqlite3_syscall_ptr obfsGetSystemCall(sqlite3_vfs*, const char* z);
160 static const char* obfsNextSystemCall(sqlite3_vfs*, const char* zName);
162 static const sqlite3_io_methods obfs_io_methods = {
163 3, /* iVersion */
164 obfsClose, /* xClose */
165 obfsRead, /* xRead */
166 obfsWrite, /* xWrite */
167 obfsTruncate, /* xTruncate */
168 obfsSync, /* xSync */
169 obfsFileSize, /* xFileSize */
170 obfsLock, /* xLock */
171 obfsUnlock, /* xUnlock */
172 obfsCheckReservedLock, /* xCheckReservedLock */
173 obfsFileControl, /* xFileControl */
174 obfsSectorSize, /* xSectorSize */
175 obfsDeviceCharacteristics, /* xDeviceCharacteristics */
176 obfsShmMap, /* xShmMap */
177 obfsShmLock, /* xShmLock */
178 obfsShmBarrier, /* xShmBarrier */
179 obfsShmUnmap, /* xShmUnmap */
180 obfsFetch, /* xFetch */
181 obfsUnfetch /* xUnfetch */
184 static constexpr int kKeyBytes = 32;
185 static constexpr int kIvBytes = IPCStreamCipherStrategy::BlockPrefixLength;
186 static constexpr int kClearTextPrefixBytesOnFirstPage = 32;
187 static constexpr int kReservedBytes = 32;
188 static constexpr int kBasicBlockSize = IPCStreamCipherStrategy::BasicBlockSize;
189 static_assert(kClearTextPrefixBytesOnFirstPage % kBasicBlockSize == 0);
190 static_assert(kReservedBytes % kBasicBlockSize == 0);
192 /* Obfuscate a page using p->encryptCipherStrategy.
194 ** A new random nonce is created and stored in the last 32 bytes
195 ** of the page. All other bytes of the page are obfuscasted using the
196 ** CipherStrategy. Except, for page-1 (including the SQLite
197 ** database header) the first 32 bytes are not obfuscated
199 ** Return a pointer to the obfuscated content, which is held in the
200 ** p->pTemp buffer. Or return a NULL pointer if something goes wrong.
201 ** Errors are reported using NS_WARNING().
203 static void* obfsEncode(ObfsFile* p, /* File containing page to be obfuscated */
204 u8* a, /* database page to be obfuscated */
205 int nByte /* Bytes of content in a[]. Must be a multiple
206 of kBasicBlockSize. */
208 u8 aIv[kIvBytes];
209 u8* pOut;
210 int i;
212 static_assert((kIvBytes & (kIvBytes - 1)) == 0);
213 sqlite3_randomness(kIvBytes, aIv);
214 pOut = (u8*)p->pTemp;
215 if (pOut == nullptr) {
216 pOut = static_cast<u8*>(sqlite3_malloc64(nByte));
217 if (pOut == nullptr) {
218 NS_WARNING(nsPrintfCString("unable to allocate a buffer in which to"
219 " write obfuscated database content for %s",
220 p->zFName)
221 .get());
222 return nullptr;
224 p->pTemp = pOut;
226 if (memcmp(a, "SQLite format 3", 16) == 0) {
227 i = kClearTextPrefixBytesOnFirstPage;
228 if (a[20] != kReservedBytes) {
229 NS_WARNING(nsPrintfCString("obfuscated database must have reserved-bytes"
230 " set to %d",
231 kReservedBytes)
232 .get());
233 return nullptr;
235 memcpy(pOut, a, kClearTextPrefixBytesOnFirstPage);
236 } else {
237 i = 0;
239 const int payloadLength = nByte - kReservedBytes - i;
240 MOZ_ASSERT(payloadLength > 0);
241 // XXX I guess this can be done in-place as well, then we don't need the
242 // temporary page at all, I guess?
243 p->encryptCipherStrategy->Cipher(
244 Span{aIv}, Span{a + i, static_cast<unsigned>(payloadLength)},
245 Span{pOut + i, static_cast<unsigned>(payloadLength)});
246 memcpy(pOut + nByte - kReservedBytes, aIv, kIvBytes);
248 return pOut;
251 /* De-obfuscate a page using p->decryptCipherStrategy.
253 ** The deobfuscation is done in-place.
255 ** For pages that begin with the SQLite header text, the first
256 ** 32 bytes are not deobfuscated.
258 static void obfsDecode(ObfsFile* p, /* File containing page to be obfuscated */
259 u8* a, /* database page to be obfuscated */
260 int nByte /* Bytes of content in a[]. Must be a multiple
261 of kBasicBlockSize. */
263 int i;
265 if (memcmp(a, "SQLite format 3", 16) == 0) {
266 i = kClearTextPrefixBytesOnFirstPage;
267 } else {
268 i = 0;
270 const int payloadLength = nByte - kReservedBytes - i;
271 MOZ_ASSERT(payloadLength > 0);
272 p->decryptCipherStrategy->Cipher(
273 Span{a + nByte - kReservedBytes, kIvBytes},
274 Span{a + i, static_cast<unsigned>(payloadLength)},
275 Span{a + i, static_cast<unsigned>(payloadLength)});
279 ** Close an obfsucated file.
281 static int obfsClose(sqlite3_file* pFile) {
282 ObfsFile* p = (ObfsFile*)pFile;
283 if (p->pPartner) {
284 MOZ_ASSERT(p->pPartner->pPartner == p);
285 p->pPartner->pPartner = nullptr;
286 p->pPartner = nullptr;
288 sqlite3_free(p->pTemp);
290 delete p->decryptCipherStrategy;
291 delete p->encryptCipherStrategy;
293 pFile = ORIGFILE(pFile);
294 return pFile->pMethods->xClose(pFile);
298 ** Read data from an obfuscated file.
300 ** If the file is less than one full page in length, then return
301 ** a substitute "prototype" page-1. This prototype page one
302 ** specifies a database in WAL mode with an 8192-byte page size
303 ** and a 32-byte reserved-bytes value. Those settings are necessary
304 ** for obfuscation to function correctly.
306 static int obfsRead(sqlite3_file* pFile, void* zBuf, int iAmt,
307 sqlite_int64 iOfst) {
308 int rc;
309 ObfsFile* p = (ObfsFile*)pFile;
310 pFile = ORIGFILE(pFile);
311 rc = pFile->pMethods->xRead(pFile, zBuf, iAmt, iOfst);
312 if (rc == SQLITE_OK) {
313 if (iAmt == OBFS_PGSZ && !p->inCkpt) {
314 obfsDecode(p, (u8*)zBuf, iAmt);
316 } else if (SQLITE_IOERR_SHORT_READ && iOfst == 0 && iAmt >= 100) {
317 static const unsigned char aEmptyDb[] = {
318 0x53, 0x51, 0x4c, 0x69, 0x74, 0x65, 0x20, 0x66,
319 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x20, 0x33, 0x00,
320 0x20, 0x00, 0x02, 0x02, kReservedBytes, 0x40, 0x20, 0x20,
321 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01};
322 memcpy(zBuf, aEmptyDb, sizeof(aEmptyDb));
323 memset(((u8*)zBuf) + sizeof(aEmptyDb), 0, iAmt - sizeof(aEmptyDb));
324 rc = SQLITE_OK;
326 return rc;
330 ** Write data to an obfuscated file or journal.
332 static int obfsWrite(sqlite3_file* pFile, const void* zBuf, int iAmt,
333 sqlite_int64 iOfst) {
334 ObfsFile* p = (ObfsFile*)pFile;
335 pFile = ORIGFILE(pFile);
336 if (iAmt == OBFS_PGSZ && !p->inCkpt) {
337 zBuf = obfsEncode(p, (u8*)zBuf, iAmt);
338 if (zBuf == nullptr) {
339 return SQLITE_IOERR;
342 return pFile->pMethods->xWrite(pFile, zBuf, iAmt, iOfst);
346 ** Truncate an obfuscated file.
348 static int obfsTruncate(sqlite3_file* pFile, sqlite_int64 size) {
349 pFile = ORIGFILE(pFile);
350 return pFile->pMethods->xTruncate(pFile, size);
354 ** Sync an obfuscated file.
356 static int obfsSync(sqlite3_file* pFile, int flags) {
357 pFile = ORIGFILE(pFile);
358 return pFile->pMethods->xSync(pFile, flags);
362 ** Return the current file-size of an obfuscated file.
364 static int obfsFileSize(sqlite3_file* pFile, sqlite_int64* pSize) {
365 ObfsFile* p = (ObfsFile*)pFile;
366 pFile = ORIGFILE(p);
367 return pFile->pMethods->xFileSize(pFile, pSize);
371 ** Lock an obfuscated file.
373 static int obfsLock(sqlite3_file* pFile, int eLock) {
374 pFile = ORIGFILE(pFile);
375 return pFile->pMethods->xLock(pFile, eLock);
379 ** Unlock an obfuscated file.
381 static int obfsUnlock(sqlite3_file* pFile, int eLock) {
382 pFile = ORIGFILE(pFile);
383 return pFile->pMethods->xUnlock(pFile, eLock);
387 ** Check if another file-handle holds a RESERVED lock on an obfuscated file.
389 static int obfsCheckReservedLock(sqlite3_file* pFile, int* pResOut) {
390 pFile = ORIGFILE(pFile);
391 return pFile->pMethods->xCheckReservedLock(pFile, pResOut);
395 ** File control method. For custom operations on an obfuscated file.
397 static int obfsFileControl(sqlite3_file* pFile, int op, void* pArg) {
398 int rc;
399 ObfsFile* p = (ObfsFile*)pFile;
400 pFile = ORIGFILE(pFile);
401 if (op == SQLITE_FCNTL_PRAGMA) {
402 char** azArg = (char**)pArg;
403 MOZ_ASSERT(azArg[1] != nullptr);
404 if (azArg[2] != nullptr && sqlite3_stricmp(azArg[1], "page_size") == 0) {
405 /* Do not allow page size changes on an obfuscated database */
406 return SQLITE_OK;
408 } else if (op == SQLITE_FCNTL_CKPT_START || op == SQLITE_FCNTL_CKPT_DONE) {
409 p->inCkpt = op == SQLITE_FCNTL_CKPT_START;
410 if (p->pPartner) {
411 p->pPartner->inCkpt = p->inCkpt;
414 rc = pFile->pMethods->xFileControl(pFile, op, pArg);
415 if (rc == SQLITE_OK && op == SQLITE_FCNTL_VFSNAME) {
416 *(char**)pArg = sqlite3_mprintf("obfs/%z", *(char**)pArg);
418 return rc;
422 ** Return the sector-size in bytes for an obfuscated file.
424 static int obfsSectorSize(sqlite3_file* pFile) {
425 pFile = ORIGFILE(pFile);
426 return pFile->pMethods->xSectorSize(pFile);
430 ** Return the device characteristic flags supported by an obfuscated file.
432 static int obfsDeviceCharacteristics(sqlite3_file* pFile) {
433 pFile = ORIGFILE(pFile);
434 return pFile->pMethods->xDeviceCharacteristics(pFile);
437 /* Create a shared memory file mapping */
438 static int obfsShmMap(sqlite3_file* pFile, int iPg, int pgsz, int bExtend,
439 void volatile** pp) {
440 pFile = ORIGFILE(pFile);
441 return pFile->pMethods->xShmMap(pFile, iPg, pgsz, bExtend, pp);
444 /* Perform locking on a shared-memory segment */
445 static int obfsShmLock(sqlite3_file* pFile, int offset, int n, int flags) {
446 pFile = ORIGFILE(pFile);
447 return pFile->pMethods->xShmLock(pFile, offset, n, flags);
450 /* Memory barrier operation on shared memory */
451 static void obfsShmBarrier(sqlite3_file* pFile) {
452 pFile = ORIGFILE(pFile);
453 pFile->pMethods->xShmBarrier(pFile);
456 /* Unmap a shared memory segment */
457 static int obfsShmUnmap(sqlite3_file* pFile, int deleteFlag) {
458 pFile = ORIGFILE(pFile);
459 return pFile->pMethods->xShmUnmap(pFile, deleteFlag);
462 /* Fetch a page of a memory-mapped file */
463 static int obfsFetch(sqlite3_file* pFile, sqlite3_int64 iOfst, int iAmt,
464 void** pp) {
465 *pp = nullptr;
466 return SQLITE_OK;
469 /* Release a memory-mapped page */
470 static int obfsUnfetch(sqlite3_file* pFile, sqlite3_int64 iOfst, void* pPage) {
471 pFile = ORIGFILE(pFile);
472 return pFile->pMethods->xUnfetch(pFile, iOfst, pPage);
476 ** Translate a single byte of Hex into an integer.
477 ** This routine only works if h really is a valid hexadecimal
478 ** character: 0..9a..fA..F
480 static u8 obfsHexToInt(int h) {
481 MOZ_ASSERT((h >= '0' && h <= '9') || (h >= 'a' && h <= 'f') ||
482 (h >= 'A' && h <= 'F'));
483 #if 1 /* ASCII */
484 h += 9 * (1 & (h >> 6));
485 #else /* EBCDIC */
486 h += 9 * (1 & ~(h >> 4));
487 #endif
488 return (u8)(h & 0xf);
492 ** Open a new file.
494 ** If the file is an ordinary database file, or a rollback or WAL journal
495 ** file, and if the key=XXXX parameter exists, then try to open the file
496 ** as an obfuscated database. All other open attempts fall through into
497 ** the lower-level VFS shim.
499 ** If the key=XXXX parameter exists but is not 64-bytes of hex key, then
500 ** put an error message in NS_WARNING() and return SQLITE_CANTOPEN.
502 static int obfsOpen(sqlite3_vfs* pVfs, const char* zName, sqlite3_file* pFile,
503 int flags, int* pOutFlags) {
504 ObfsFile* p;
505 sqlite3_file* pSubFile;
506 sqlite3_vfs* pSubVfs;
507 int rc, i;
508 const char* zKey;
509 u8 aKey[kKeyBytes];
510 pSubVfs = ORIGVFS(pVfs);
511 if (flags &
512 (SQLITE_OPEN_MAIN_DB | SQLITE_OPEN_WAL | SQLITE_OPEN_MAIN_JOURNAL)) {
513 zKey = sqlite3_uri_parameter(zName, "key");
514 } else {
515 zKey = nullptr;
517 if (zKey == nullptr) {
518 return pSubVfs->xOpen(pSubVfs, zName, pFile, flags, pOutFlags);
520 for (i = 0;
521 i < kKeyBytes && isxdigit(zKey[i * 2]) && isxdigit(zKey[i * 2 + 1]);
522 i++) {
523 aKey[i] = (obfsHexToInt(zKey[i * 2]) << 4) | obfsHexToInt(zKey[i * 2 + 1]);
525 if (i != kKeyBytes) {
526 NS_WARNING(
527 nsPrintfCString("invalid query parameter on %s: key=%s", zName, zKey)
528 .get());
529 return SQLITE_CANTOPEN;
531 p = (ObfsFile*)pFile;
532 memset(p, 0, sizeof(*p));
534 auto encryptCipherStrategy = MakeUnique<IPCStreamCipherStrategy>();
535 auto decryptCipherStrategy = MakeUnique<IPCStreamCipherStrategy>();
537 auto resetMethods = MakeScopeExit([pFile] { pFile->pMethods = nullptr; });
539 if (NS_WARN_IF(NS_FAILED(encryptCipherStrategy->Init(
540 CipherMode::Encrypt, Span{aKey, sizeof(aKey)},
541 IPCStreamCipherStrategy::MakeBlockPrefix())))) {
542 return SQLITE_ERROR;
545 if (NS_WARN_IF(NS_FAILED(decryptCipherStrategy->Init(
546 CipherMode::Decrypt, Span{aKey, sizeof(aKey)})))) {
547 return SQLITE_ERROR;
550 pSubFile = ORIGFILE(pFile);
551 p->base.pMethods = &obfs_io_methods;
552 rc = pSubVfs->xOpen(pSubVfs, zName, pSubFile, flags, pOutFlags);
553 if (rc) {
554 return rc;
557 resetMethods.release();
559 if (flags & (SQLITE_OPEN_WAL | SQLITE_OPEN_MAIN_JOURNAL)) {
560 sqlite3_file* pDb = sqlite3_database_file_object(zName);
561 p->pPartner = (ObfsFile*)pDb;
562 MOZ_ASSERT(p->pPartner->pPartner == nullptr);
563 p->pPartner->pPartner = p;
565 p->zFName = zName;
567 p->encryptCipherStrategy = encryptCipherStrategy.release();
568 p->decryptCipherStrategy = decryptCipherStrategy.release();
570 return SQLITE_OK;
574 ** All other VFS methods are pass-thrus.
576 static int obfsDelete(sqlite3_vfs* pVfs, const char* zPath, int syncDir) {
577 return ORIGVFS(pVfs)->xDelete(ORIGVFS(pVfs), zPath, syncDir);
579 static int obfsAccess(sqlite3_vfs* pVfs, const char* zPath, int flags,
580 int* pResOut) {
581 return ORIGVFS(pVfs)->xAccess(ORIGVFS(pVfs), zPath, flags, pResOut);
583 static int obfsFullPathname(sqlite3_vfs* pVfs, const char* zPath, int nOut,
584 char* zOut) {
585 return ORIGVFS(pVfs)->xFullPathname(ORIGVFS(pVfs), zPath, nOut, zOut);
587 static void* obfsDlOpen(sqlite3_vfs* pVfs, const char* zPath) {
588 return ORIGVFS(pVfs)->xDlOpen(ORIGVFS(pVfs), zPath);
590 static void obfsDlError(sqlite3_vfs* pVfs, int nByte, char* zErrMsg) {
591 ORIGVFS(pVfs)->xDlError(ORIGVFS(pVfs), nByte, zErrMsg);
593 static void (*obfsDlSym(sqlite3_vfs* pVfs, void* p, const char* zSym))(void) {
594 return ORIGVFS(pVfs)->xDlSym(ORIGVFS(pVfs), p, zSym);
596 static void obfsDlClose(sqlite3_vfs* pVfs, void* pHandle) {
597 ORIGVFS(pVfs)->xDlClose(ORIGVFS(pVfs), pHandle);
599 static int obfsRandomness(sqlite3_vfs* pVfs, int nByte, char* zBufOut) {
600 return ORIGVFS(pVfs)->xRandomness(ORIGVFS(pVfs), nByte, zBufOut);
602 static int obfsSleep(sqlite3_vfs* pVfs, int nMicroseconds) {
603 return ORIGVFS(pVfs)->xSleep(ORIGVFS(pVfs), nMicroseconds);
605 static int obfsCurrentTime(sqlite3_vfs* pVfs, double* pTimeOut) {
606 return ORIGVFS(pVfs)->xCurrentTime(ORIGVFS(pVfs), pTimeOut);
608 static int obfsGetLastError(sqlite3_vfs* pVfs, int a, char* b) {
609 return ORIGVFS(pVfs)->xGetLastError(ORIGVFS(pVfs), a, b);
611 static int obfsCurrentTimeInt64(sqlite3_vfs* pVfs, sqlite3_int64* p) {
612 return ORIGVFS(pVfs)->xCurrentTimeInt64(ORIGVFS(pVfs), p);
614 static int obfsSetSystemCall(sqlite3_vfs* pVfs, const char* zName,
615 sqlite3_syscall_ptr pCall) {
616 return ORIGVFS(pVfs)->xSetSystemCall(ORIGVFS(pVfs), zName, pCall);
618 static sqlite3_syscall_ptr obfsGetSystemCall(sqlite3_vfs* pVfs,
619 const char* zName) {
620 return ORIGVFS(pVfs)->xGetSystemCall(ORIGVFS(pVfs), zName);
622 static const char* obfsNextSystemCall(sqlite3_vfs* pVfs, const char* zName) {
623 return ORIGVFS(pVfs)->xNextSystemCall(ORIGVFS(pVfs), zName);
626 namespace mozilla {
627 namespace storage {
629 const char* GetObfuscatingVFSName() { return "obfsvfs"; }
631 UniquePtr<sqlite3_vfs> ConstructObfuscatingVFS(const char* aBaseVFSName) {
632 MOZ_ASSERT(aBaseVFSName);
634 if (sqlite3_vfs_find(GetObfuscatingVFSName()) != nullptr) {
635 return nullptr;
637 sqlite3_vfs* const pOrig = sqlite3_vfs_find(aBaseVFSName);
638 if (pOrig == nullptr) {
639 return nullptr;
642 #ifdef DEBUG
643 // If the VFS version is higher than the last known one, you should update
644 // this VFS adding appropriate methods for any methods added in the version
645 // change.
646 static constexpr int kLastKnownVfsVersion = 3;
647 MOZ_ASSERT(pOrig->iVersion <= kLastKnownVfsVersion);
648 #endif
650 const sqlite3_vfs obfs_vfs = {
651 pOrig->iVersion, /* iVersion */
652 static_cast<int>(pOrig->szOsFile + sizeof(ObfsFile)), /* szOsFile */
653 1024, /* mxPathname */
654 nullptr, /* pNext */
655 GetObfuscatingVFSName(), /* zName */
656 pOrig, /* pAppData */
657 obfsOpen, /* xOpen */
658 obfsDelete, /* xDelete */
659 obfsAccess, /* xAccess */
660 obfsFullPathname, /* xFullPathname */
661 obfsDlOpen, /* xDlOpen */
662 obfsDlError, /* xDlError */
663 obfsDlSym, /* xDlSym */
664 obfsDlClose, /* xDlClose */
665 obfsRandomness, /* xRandomness */
666 obfsSleep, /* xSleep */
667 obfsCurrentTime, /* xCurrentTime */
668 obfsGetLastError, /* xGetLastError */
669 obfsCurrentTimeInt64, /* xCurrentTimeInt64 */
670 obfsSetSystemCall, /* xSetSystemCall */
671 obfsGetSystemCall, /* xGetSystemCall */
672 obfsNextSystemCall /* xNextSystemCall */
675 return MakeUnique<sqlite3_vfs>(obfs_vfs);
678 } // namespace storage
679 } // namespace mozilla