Bug 1828719 - Remove omnijar Gradle project from srcdir r=geckoview-reviewers,nalexan...
[gecko.git] / storage / ObfuscatingVFS.cpp
blob29bdf5593f4ec5e1bc85036ded05348e81c0967c
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 0x53, 0x51, 0x4c, 0x69, 0x74, 0x65, 0x20, 0x66,
323 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x20, 0x33, 0x00,
324 0x20, 0x00, 0x02, 0x02, kReservedBytes, 0x40, 0x20, 0x20,
325 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01};
326 memcpy(zBuf, aEmptyDb, sizeof(aEmptyDb));
327 memset(((u8*)zBuf) + sizeof(aEmptyDb), 0, iAmt - sizeof(aEmptyDb));
328 rc = SQLITE_OK;
330 return rc;
334 ** Write data to an obfuscated file or journal.
336 static int obfsWrite(sqlite3_file* pFile, const void* zBuf, int iAmt,
337 sqlite_int64 iOfst) {
338 ObfsFile* p = (ObfsFile*)pFile;
339 pFile = ORIGFILE(pFile);
340 if (iAmt == OBFS_PGSZ && !p->inCkpt) {
341 zBuf = obfsEncode(p, (u8*)zBuf, iAmt);
342 if (zBuf == nullptr) {
343 return SQLITE_IOERR;
346 return pFile->pMethods->xWrite(pFile, zBuf, iAmt, iOfst);
350 ** Truncate an obfuscated file.
352 static int obfsTruncate(sqlite3_file* pFile, sqlite_int64 size) {
353 pFile = ORIGFILE(pFile);
354 return pFile->pMethods->xTruncate(pFile, size);
358 ** Sync an obfuscated file.
360 static int obfsSync(sqlite3_file* pFile, int flags) {
361 pFile = ORIGFILE(pFile);
362 return pFile->pMethods->xSync(pFile, flags);
366 ** Return the current file-size of an obfuscated file.
368 static int obfsFileSize(sqlite3_file* pFile, sqlite_int64* pSize) {
369 ObfsFile* p = (ObfsFile*)pFile;
370 pFile = ORIGFILE(p);
371 return pFile->pMethods->xFileSize(pFile, pSize);
375 ** Lock an obfuscated file.
377 static int obfsLock(sqlite3_file* pFile, int eLock) {
378 pFile = ORIGFILE(pFile);
379 return pFile->pMethods->xLock(pFile, eLock);
383 ** Unlock an obfuscated file.
385 static int obfsUnlock(sqlite3_file* pFile, int eLock) {
386 pFile = ORIGFILE(pFile);
387 return pFile->pMethods->xUnlock(pFile, eLock);
391 ** Check if another file-handle holds a RESERVED lock on an obfuscated file.
393 static int obfsCheckReservedLock(sqlite3_file* pFile, int* pResOut) {
394 pFile = ORIGFILE(pFile);
395 return pFile->pMethods->xCheckReservedLock(pFile, pResOut);
399 ** File control method. For custom operations on an obfuscated file.
401 static int obfsFileControl(sqlite3_file* pFile, int op, void* pArg) {
402 int rc;
403 ObfsFile* p = (ObfsFile*)pFile;
404 pFile = ORIGFILE(pFile);
405 if (op == SQLITE_FCNTL_PRAGMA) {
406 char** azArg = (char**)pArg;
407 MOZ_ASSERT(azArg[1] != nullptr);
408 if (azArg[2] != nullptr && sqlite3_stricmp(azArg[1], "page_size") == 0) {
409 /* Do not allow page size changes on an obfuscated database */
410 return SQLITE_OK;
412 } else if (op == SQLITE_FCNTL_CKPT_START || op == SQLITE_FCNTL_CKPT_DONE) {
413 p->inCkpt = op == SQLITE_FCNTL_CKPT_START;
414 if (p->pPartner) {
415 p->pPartner->inCkpt = p->inCkpt;
418 rc = pFile->pMethods->xFileControl(pFile, op, pArg);
419 if (rc == SQLITE_OK && op == SQLITE_FCNTL_VFSNAME) {
420 *(char**)pArg = sqlite3_mprintf("obfs/%z", *(char**)pArg);
422 return rc;
426 ** Return the sector-size in bytes for an obfuscated file.
428 static int obfsSectorSize(sqlite3_file* pFile) {
429 pFile = ORIGFILE(pFile);
430 return pFile->pMethods->xSectorSize(pFile);
434 ** Return the device characteristic flags supported by an obfuscated file.
436 static int obfsDeviceCharacteristics(sqlite3_file* pFile) {
437 pFile = ORIGFILE(pFile);
438 return pFile->pMethods->xDeviceCharacteristics(pFile);
441 /* Create a shared memory file mapping */
442 static int obfsShmMap(sqlite3_file* pFile, int iPg, int pgsz, int bExtend,
443 void volatile** pp) {
444 pFile = ORIGFILE(pFile);
445 return pFile->pMethods->xShmMap(pFile, iPg, pgsz, bExtend, pp);
448 /* Perform locking on a shared-memory segment */
449 static int obfsShmLock(sqlite3_file* pFile, int offset, int n, int flags) {
450 pFile = ORIGFILE(pFile);
451 return pFile->pMethods->xShmLock(pFile, offset, n, flags);
454 /* Memory barrier operation on shared memory */
455 static void obfsShmBarrier(sqlite3_file* pFile) {
456 pFile = ORIGFILE(pFile);
457 pFile->pMethods->xShmBarrier(pFile);
460 /* Unmap a shared memory segment */
461 static int obfsShmUnmap(sqlite3_file* pFile, int deleteFlag) {
462 pFile = ORIGFILE(pFile);
463 return pFile->pMethods->xShmUnmap(pFile, deleteFlag);
466 /* Fetch a page of a memory-mapped file */
467 static int obfsFetch(sqlite3_file* pFile, sqlite3_int64 iOfst, int iAmt,
468 void** pp) {
469 *pp = nullptr;
470 return SQLITE_OK;
473 /* Release a memory-mapped page */
474 static int obfsUnfetch(sqlite3_file* pFile, sqlite3_int64 iOfst, void* pPage) {
475 pFile = ORIGFILE(pFile);
476 return pFile->pMethods->xUnfetch(pFile, iOfst, pPage);
480 ** Translate a single byte of Hex into an integer.
481 ** This routine only works if h really is a valid hexadecimal
482 ** character: 0..9a..fA..F
484 static u8 obfsHexToInt(int h) {
485 MOZ_ASSERT((h >= '0' && h <= '9') || (h >= 'a' && h <= 'f') ||
486 (h >= 'A' && h <= 'F'));
487 #if 1 /* ASCII */
488 h += 9 * (1 & (h >> 6));
489 #else /* EBCDIC */
490 h += 9 * (1 & ~(h >> 4));
491 #endif
492 return (u8)(h & 0xf);
496 ** Open a new file.
498 ** If the file is an ordinary database file, or a rollback or WAL journal
499 ** file, and if the key=XXXX parameter exists, then try to open the file
500 ** as an obfuscated database. All other open attempts fall through into
501 ** the lower-level VFS shim.
503 ** If the key=XXXX parameter exists but is not 64-bytes of hex key, then
504 ** put an error message in NS_WARNING() and return SQLITE_CANTOPEN.
506 static int obfsOpen(sqlite3_vfs* pVfs, const char* zName, sqlite3_file* pFile,
507 int flags, int* pOutFlags) {
508 ObfsFile* p;
509 sqlite3_file* pSubFile;
510 sqlite3_vfs* pSubVfs;
511 int rc, i;
512 const char* zKey;
513 u8 aKey[kKeyBytes];
514 pSubVfs = ORIGVFS(pVfs);
515 if (flags &
516 (SQLITE_OPEN_MAIN_DB | SQLITE_OPEN_WAL | SQLITE_OPEN_MAIN_JOURNAL)) {
517 zKey = sqlite3_uri_parameter(zName, "key");
518 } else {
519 zKey = nullptr;
521 if (zKey == nullptr) {
522 return pSubVfs->xOpen(pSubVfs, zName, pFile, flags, pOutFlags);
524 for (i = 0;
525 i < kKeyBytes && isxdigit(zKey[i * 2]) && isxdigit(zKey[i * 2 + 1]);
526 i++) {
527 aKey[i] = (obfsHexToInt(zKey[i * 2]) << 4) | obfsHexToInt(zKey[i * 2 + 1]);
529 if (i != kKeyBytes) {
530 NS_WARNING(
531 nsPrintfCString("invalid query parameter on %s: key=%s", zName, zKey)
532 .get());
533 return SQLITE_CANTOPEN;
535 p = (ObfsFile*)pFile;
536 memset(p, 0, sizeof(*p));
538 auto encryptCipherStrategy = MakeUnique<IPCStreamCipherStrategy>();
539 auto decryptCipherStrategy = MakeUnique<IPCStreamCipherStrategy>();
541 auto resetMethods = MakeScopeExit([pFile] { pFile->pMethods = nullptr; });
543 if (NS_WARN_IF(NS_FAILED(encryptCipherStrategy->Init(
544 CipherMode::Encrypt, Span{aKey, sizeof(aKey)},
545 IPCStreamCipherStrategy::MakeBlockPrefix())))) {
546 return SQLITE_ERROR;
549 if (NS_WARN_IF(NS_FAILED(decryptCipherStrategy->Init(
550 CipherMode::Decrypt, Span{aKey, sizeof(aKey)})))) {
551 return SQLITE_ERROR;
554 pSubFile = ORIGFILE(pFile);
555 p->base.pMethods = &obfs_io_methods;
556 rc = pSubVfs->xOpen(pSubVfs, zName, pSubFile, flags, pOutFlags);
557 if (rc) {
558 return rc;
561 resetMethods.release();
563 if (flags & (SQLITE_OPEN_WAL | SQLITE_OPEN_MAIN_JOURNAL)) {
564 sqlite3_file* pDb = sqlite3_database_file_object(zName);
565 p->pPartner = (ObfsFile*)pDb;
566 MOZ_ASSERT(p->pPartner->pPartner == nullptr);
567 p->pPartner->pPartner = p;
569 p->zFName = zName;
571 p->encryptCipherStrategy = encryptCipherStrategy.release();
572 p->decryptCipherStrategy = decryptCipherStrategy.release();
574 return SQLITE_OK;
578 ** All other VFS methods are pass-thrus.
580 static int obfsDelete(sqlite3_vfs* pVfs, const char* zPath, int syncDir) {
581 return ORIGVFS(pVfs)->xDelete(ORIGVFS(pVfs), zPath, syncDir);
583 static int obfsAccess(sqlite3_vfs* pVfs, const char* zPath, int flags,
584 int* pResOut) {
585 return ORIGVFS(pVfs)->xAccess(ORIGVFS(pVfs), zPath, flags, pResOut);
587 static int obfsFullPathname(sqlite3_vfs* pVfs, const char* zPath, int nOut,
588 char* zOut) {
589 return ORIGVFS(pVfs)->xFullPathname(ORIGVFS(pVfs), zPath, nOut, zOut);
591 static void* obfsDlOpen(sqlite3_vfs* pVfs, const char* zPath) {
592 return ORIGVFS(pVfs)->xDlOpen(ORIGVFS(pVfs), zPath);
594 static void obfsDlError(sqlite3_vfs* pVfs, int nByte, char* zErrMsg) {
595 ORIGVFS(pVfs)->xDlError(ORIGVFS(pVfs), nByte, zErrMsg);
597 static void (*obfsDlSym(sqlite3_vfs* pVfs, void* p, const char* zSym))(void) {
598 return ORIGVFS(pVfs)->xDlSym(ORIGVFS(pVfs), p, zSym);
600 static void obfsDlClose(sqlite3_vfs* pVfs, void* pHandle) {
601 ORIGVFS(pVfs)->xDlClose(ORIGVFS(pVfs), pHandle);
603 static int obfsRandomness(sqlite3_vfs* pVfs, int nByte, char* zBufOut) {
604 return ORIGVFS(pVfs)->xRandomness(ORIGVFS(pVfs), nByte, zBufOut);
606 static int obfsSleep(sqlite3_vfs* pVfs, int nMicroseconds) {
607 return ORIGVFS(pVfs)->xSleep(ORIGVFS(pVfs), nMicroseconds);
609 static int obfsCurrentTime(sqlite3_vfs* pVfs, double* pTimeOut) {
610 return ORIGVFS(pVfs)->xCurrentTime(ORIGVFS(pVfs), pTimeOut);
612 static int obfsGetLastError(sqlite3_vfs* pVfs, int a, char* b) {
613 return ORIGVFS(pVfs)->xGetLastError(ORIGVFS(pVfs), a, b);
615 static int obfsCurrentTimeInt64(sqlite3_vfs* pVfs, sqlite3_int64* p) {
616 return ORIGVFS(pVfs)->xCurrentTimeInt64(ORIGVFS(pVfs), p);
618 static int obfsSetSystemCall(sqlite3_vfs* pVfs, const char* zName,
619 sqlite3_syscall_ptr pCall) {
620 return ORIGVFS(pVfs)->xSetSystemCall(ORIGVFS(pVfs), zName, pCall);
622 static sqlite3_syscall_ptr obfsGetSystemCall(sqlite3_vfs* pVfs,
623 const char* zName) {
624 return ORIGVFS(pVfs)->xGetSystemCall(ORIGVFS(pVfs), zName);
626 static const char* obfsNextSystemCall(sqlite3_vfs* pVfs, const char* zName) {
627 return ORIGVFS(pVfs)->xNextSystemCall(ORIGVFS(pVfs), zName);
630 namespace mozilla {
631 namespace storage {
633 const char* GetObfuscatingVFSName() { return "obfsvfs"; }
635 UniquePtr<sqlite3_vfs> ConstructObfuscatingVFS(const char* aBaseVFSName) {
636 MOZ_ASSERT(aBaseVFSName);
638 if (sqlite3_vfs_find(GetObfuscatingVFSName()) != nullptr) {
639 return nullptr;
641 sqlite3_vfs* const pOrig = sqlite3_vfs_find(aBaseVFSName);
642 if (pOrig == nullptr) {
643 return nullptr;
646 #ifdef DEBUG
647 // If the VFS version is higher than the last known one, you should update
648 // this VFS adding appropriate methods for any methods added in the version
649 // change.
650 static constexpr int kLastKnownVfsVersion = 3;
651 MOZ_ASSERT(pOrig->iVersion <= kLastKnownVfsVersion);
652 #endif
654 const sqlite3_vfs obfs_vfs = {
655 pOrig->iVersion, /* iVersion */
656 static_cast<int>(pOrig->szOsFile + sizeof(ObfsFile)), /* szOsFile */
657 pOrig->mxPathname, /* mxPathname */
658 nullptr, /* pNext */
659 GetObfuscatingVFSName(), /* zName */
660 pOrig, /* pAppData */
661 obfsOpen, /* xOpen */
662 obfsDelete, /* xDelete */
663 obfsAccess, /* xAccess */
664 obfsFullPathname, /* xFullPathname */
665 obfsDlOpen, /* xDlOpen */
666 obfsDlError, /* xDlError */
667 obfsDlSym, /* xDlSym */
668 obfsDlClose, /* xDlClose */
669 obfsRandomness, /* xRandomness */
670 obfsSleep, /* xSleep */
671 obfsCurrentTime, /* xCurrentTime */
672 obfsGetLastError, /* xGetLastError */
673 obfsCurrentTimeInt64, /* xCurrentTimeInt64 */
674 obfsSetSystemCall, /* xSetSystemCall */
675 obfsGetSystemCall, /* xGetSystemCall */
676 obfsNextSystemCall /* xNextSystemCall */
679 return MakeUnique<sqlite3_vfs>(obfs_vfs);
682 } // namespace storage
683 } // namespace mozilla