Bug 1858983 [wpt PR 42532] - Update wpt metadata, a=testonly
[gecko.git] / storage / ObfuscatingVFS.cpp
blob107384376c41a1a2f5d09a1a4a5b69b4944fffd9
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 "ObfuscatingVFS.h"
74 #include <string.h>
75 #include <ctype.h>
76 #include <stdio.h> /* For debugging only */
78 #include "mozilla/dom/quota/IPCStreamCipherStrategy.h"
79 #include "mozilla/ScopeExit.h"
80 #include "nsPrintfCString.h"
81 #include "QuotaVFS.h"
82 #include "sqlite3.h"
85 ** Forward declaration of objects used by this utility
87 using ObfsVfs = sqlite3_vfs;
90 ** Useful datatype abbreviations
92 #if !defined(SQLITE_CORE)
93 using u8 = unsigned char;
94 #endif
96 /* Access to a lower-level VFS that (might) implement dynamic loading,
97 ** access to randomness, etc.
99 #define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData))
100 #define ORIGFILE(p) ((sqlite3_file*)(((ObfsFile*)(p)) + 1))
103 ** Database page size for obfuscated databases
105 #define OBFS_PGSZ 8192
107 #define WAL_FRAMEHDRSIZE 24
109 using namespace mozilla;
110 using namespace mozilla::dom::quota;
112 /* An open file */
113 struct ObfsFile {
114 sqlite3_file base; /* IO methods */
115 const char* zFName; /* Original name of the file */
116 bool inCkpt; /* Currently doing a checkpoint */
117 ObfsFile* pPartner; /* Ptr from WAL to main-db, or from main-db to WAL */
118 void* pTemp; /* Temporary storage for encoded pages */
119 IPCStreamCipherStrategy*
120 encryptCipherStrategy; /* CipherStrategy for encryption */
121 IPCStreamCipherStrategy*
122 decryptCipherStrategy; /* CipherStrategy for decryption */
126 ** Methods for ObfsFile
128 static int obfsClose(sqlite3_file*);
129 static int obfsRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
130 static int obfsWrite(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst);
131 static int obfsTruncate(sqlite3_file*, sqlite3_int64 size);
132 static int obfsSync(sqlite3_file*, int flags);
133 static int obfsFileSize(sqlite3_file*, sqlite3_int64* pSize);
134 static int obfsLock(sqlite3_file*, int);
135 static int obfsUnlock(sqlite3_file*, int);
136 static int obfsCheckReservedLock(sqlite3_file*, int* pResOut);
137 static int obfsFileControl(sqlite3_file*, int op, void* pArg);
138 static int obfsSectorSize(sqlite3_file*);
139 static int obfsDeviceCharacteristics(sqlite3_file*);
140 static int obfsShmMap(sqlite3_file*, int iPg, int pgsz, int, void volatile**);
141 static int obfsShmLock(sqlite3_file*, int offset, int n, int flags);
142 static void obfsShmBarrier(sqlite3_file*);
143 static int obfsShmUnmap(sqlite3_file*, int deleteFlag);
144 static int obfsFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void** pp);
145 static int obfsUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void* p);
148 ** Methods for ObfsVfs
150 static int obfsOpen(sqlite3_vfs*, const char*, sqlite3_file*, int, int*);
151 static int obfsDelete(sqlite3_vfs*, const char* zPath, int syncDir);
152 static int obfsAccess(sqlite3_vfs*, const char* zPath, int flags, int*);
153 static int obfsFullPathname(sqlite3_vfs*, const char* zPath, int, char* zOut);
154 static void* obfsDlOpen(sqlite3_vfs*, const char* zPath);
155 static void obfsDlError(sqlite3_vfs*, int nByte, char* zErrMsg);
156 static void (*obfsDlSym(sqlite3_vfs* pVfs, void* p, const char* zSym))(void);
157 static void obfsDlClose(sqlite3_vfs*, void*);
158 static int obfsRandomness(sqlite3_vfs*, int nByte, char* zBufOut);
159 static int obfsSleep(sqlite3_vfs*, int nMicroseconds);
160 static int obfsCurrentTime(sqlite3_vfs*, double*);
161 static int obfsGetLastError(sqlite3_vfs*, int, char*);
162 static int obfsCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
163 static int obfsSetSystemCall(sqlite3_vfs*, const char*, sqlite3_syscall_ptr);
164 static sqlite3_syscall_ptr obfsGetSystemCall(sqlite3_vfs*, const char* z);
165 static const char* obfsNextSystemCall(sqlite3_vfs*, const char* zName);
167 static const sqlite3_io_methods obfs_io_methods = {
168 3, /* iVersion */
169 obfsClose, /* xClose */
170 obfsRead, /* xRead */
171 obfsWrite, /* xWrite */
172 obfsTruncate, /* xTruncate */
173 obfsSync, /* xSync */
174 obfsFileSize, /* xFileSize */
175 obfsLock, /* xLock */
176 obfsUnlock, /* xUnlock */
177 obfsCheckReservedLock, /* xCheckReservedLock */
178 obfsFileControl, /* xFileControl */
179 obfsSectorSize, /* xSectorSize */
180 obfsDeviceCharacteristics, /* xDeviceCharacteristics */
181 obfsShmMap, /* xShmMap */
182 obfsShmLock, /* xShmLock */
183 obfsShmBarrier, /* xShmBarrier */
184 obfsShmUnmap, /* xShmUnmap */
185 obfsFetch, /* xFetch */
186 obfsUnfetch /* xUnfetch */
189 static constexpr int kKeyBytes = 32;
190 static constexpr int kIvBytes = IPCStreamCipherStrategy::BlockPrefixLength;
191 static constexpr int kClearTextPrefixBytesOnFirstPage = 32;
192 static constexpr int kReservedBytes = 32;
193 static constexpr int kBasicBlockSize = IPCStreamCipherStrategy::BasicBlockSize;
194 static_assert(kClearTextPrefixBytesOnFirstPage % kBasicBlockSize == 0);
195 static_assert(kReservedBytes % kBasicBlockSize == 0);
197 /* Obfuscate a page using p->encryptCipherStrategy.
199 ** A new random nonce is created and stored in the last 32 bytes
200 ** of the page. All other bytes of the page are obfuscasted using the
201 ** CipherStrategy. Except, for page-1 (including the SQLite
202 ** database header) the first 32 bytes are not obfuscated
204 ** Return a pointer to the obfuscated content, which is held in the
205 ** p->pTemp buffer. Or return a NULL pointer if something goes wrong.
206 ** Errors are reported using NS_WARNING().
208 static void* obfsEncode(ObfsFile* p, /* File containing page to be obfuscated */
209 u8* a, /* database page to be obfuscated */
210 int nByte /* Bytes of content in a[]. Must be a multiple
211 of kBasicBlockSize. */
213 u8 aIv[kIvBytes];
214 u8* pOut;
215 int i;
217 static_assert((kIvBytes & (kIvBytes - 1)) == 0);
218 sqlite3_randomness(kIvBytes, aIv);
219 pOut = (u8*)p->pTemp;
220 if (pOut == nullptr) {
221 pOut = static_cast<u8*>(sqlite3_malloc64(nByte));
222 if (pOut == nullptr) {
223 NS_WARNING(nsPrintfCString("unable to allocate a buffer in which to"
224 " write obfuscated database content for %s",
225 p->zFName)
226 .get());
227 return nullptr;
229 p->pTemp = pOut;
231 if (memcmp(a, "SQLite format 3", 16) == 0) {
232 i = kClearTextPrefixBytesOnFirstPage;
233 if (a[20] != kReservedBytes) {
234 NS_WARNING(nsPrintfCString("obfuscated database must have reserved-bytes"
235 " set to %d",
236 kReservedBytes)
237 .get());
238 return nullptr;
240 memcpy(pOut, a, kClearTextPrefixBytesOnFirstPage);
241 } else {
242 i = 0;
244 const int payloadLength = nByte - kReservedBytes - i;
245 MOZ_ASSERT(payloadLength > 0);
246 // XXX I guess this can be done in-place as well, then we don't need the
247 // temporary page at all, I guess?
248 p->encryptCipherStrategy->Cipher(
249 Span{aIv}, Span{a + i, static_cast<unsigned>(payloadLength)},
250 Span{pOut + i, static_cast<unsigned>(payloadLength)});
251 memcpy(pOut + nByte - kReservedBytes, aIv, kIvBytes);
253 return pOut;
256 /* De-obfuscate a page using p->decryptCipherStrategy.
258 ** The deobfuscation is done in-place.
260 ** For pages that begin with the SQLite header text, the first
261 ** 32 bytes are not deobfuscated.
263 static void obfsDecode(ObfsFile* p, /* File containing page to be obfuscated */
264 u8* a, /* database page to be obfuscated */
265 int nByte /* Bytes of content in a[]. Must be a multiple
266 of kBasicBlockSize. */
268 int i;
270 if (memcmp(a, "SQLite format 3", 16) == 0) {
271 i = kClearTextPrefixBytesOnFirstPage;
272 } else {
273 i = 0;
275 const int payloadLength = nByte - kReservedBytes - i;
276 MOZ_ASSERT(payloadLength > 0);
277 p->decryptCipherStrategy->Cipher(
278 Span{a + nByte - kReservedBytes, kIvBytes},
279 Span{a + i, static_cast<unsigned>(payloadLength)},
280 Span{a + i, static_cast<unsigned>(payloadLength)});
281 memset(a + nByte - kReservedBytes, 0, kIvBytes);
285 ** Close an obfsucated file.
287 static int obfsClose(sqlite3_file* pFile) {
288 ObfsFile* p = (ObfsFile*)pFile;
289 if (p->pPartner) {
290 MOZ_ASSERT(p->pPartner->pPartner == p);
291 p->pPartner->pPartner = nullptr;
292 p->pPartner = nullptr;
294 sqlite3_free(p->pTemp);
296 delete p->decryptCipherStrategy;
297 delete p->encryptCipherStrategy;
299 pFile = ORIGFILE(pFile);
300 return pFile->pMethods->xClose(pFile);
304 ** Read data from an obfuscated file.
306 ** If the file is less than one full page in length, then return
307 ** a substitute "prototype" page-1. This prototype page one
308 ** specifies a database in WAL mode with an 8192-byte page size
309 ** and a 32-byte reserved-bytes value. Those settings are necessary
310 ** for obfuscation to function correctly.
312 static int obfsRead(sqlite3_file* pFile, void* zBuf, int iAmt,
313 sqlite_int64 iOfst) {
314 int rc;
315 ObfsFile* p = (ObfsFile*)pFile;
316 pFile = ORIGFILE(pFile);
317 rc = pFile->pMethods->xRead(pFile, zBuf, iAmt, iOfst);
318 if (rc == SQLITE_OK) {
319 if ((iAmt == OBFS_PGSZ || iAmt == OBFS_PGSZ + WAL_FRAMEHDRSIZE) &&
320 !p->inCkpt) {
321 obfsDecode(p, ((u8*)zBuf) + iAmt - OBFS_PGSZ, OBFS_PGSZ);
323 } else if (rc == SQLITE_IOERR_SHORT_READ && iOfst == 0 && iAmt >= 100) {
324 static const unsigned char aEmptyDb[] = {
325 // Offset 0, Size 16, The header string: "SQLite format 3\000"
326 0x53, 0x51, 0x4c, 0x69, 0x74, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61,
327 0x74, 0x20, 0x33, 0x00,
328 // XXX Add description for other fields
329 0x20, 0x00, 0x02, 0x02, kReservedBytes, 0x40, 0x20, 0x20, 0x00, 0x00,
330 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
331 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
332 0x00, 0x00,
333 // Offset 52, Size 4, The page number of the largest root b-tree page
334 // when in auto-vacuum or incremental-vacuum modes, or zero otherwise.
335 0x00, 0x00, 0x00, 0x01};
337 memcpy(zBuf, aEmptyDb, sizeof(aEmptyDb));
338 memset(((u8*)zBuf) + sizeof(aEmptyDb), 0, iAmt - sizeof(aEmptyDb));
339 rc = SQLITE_OK;
341 return rc;
345 ** Write data to an obfuscated file or journal.
347 static int obfsWrite(sqlite3_file* pFile, const void* zBuf, int iAmt,
348 sqlite_int64 iOfst) {
349 ObfsFile* p = (ObfsFile*)pFile;
350 pFile = ORIGFILE(pFile);
351 if (iAmt == OBFS_PGSZ && !p->inCkpt) {
352 zBuf = obfsEncode(p, (u8*)zBuf, iAmt);
353 if (zBuf == nullptr) {
354 return SQLITE_IOERR;
357 return pFile->pMethods->xWrite(pFile, zBuf, iAmt, iOfst);
361 ** Truncate an obfuscated file.
363 static int obfsTruncate(sqlite3_file* pFile, sqlite_int64 size) {
364 pFile = ORIGFILE(pFile);
365 return pFile->pMethods->xTruncate(pFile, size);
369 ** Sync an obfuscated file.
371 static int obfsSync(sqlite3_file* pFile, int flags) {
372 pFile = ORIGFILE(pFile);
373 return pFile->pMethods->xSync(pFile, flags);
377 ** Return the current file-size of an obfuscated file.
379 static int obfsFileSize(sqlite3_file* pFile, sqlite_int64* pSize) {
380 ObfsFile* p = (ObfsFile*)pFile;
381 pFile = ORIGFILE(p);
382 return pFile->pMethods->xFileSize(pFile, pSize);
386 ** Lock an obfuscated file.
388 static int obfsLock(sqlite3_file* pFile, int eLock) {
389 pFile = ORIGFILE(pFile);
390 return pFile->pMethods->xLock(pFile, eLock);
394 ** Unlock an obfuscated file.
396 static int obfsUnlock(sqlite3_file* pFile, int eLock) {
397 pFile = ORIGFILE(pFile);
398 return pFile->pMethods->xUnlock(pFile, eLock);
402 ** Check if another file-handle holds a RESERVED lock on an obfuscated file.
404 static int obfsCheckReservedLock(sqlite3_file* pFile, int* pResOut) {
405 pFile = ORIGFILE(pFile);
406 return pFile->pMethods->xCheckReservedLock(pFile, pResOut);
410 ** File control method. For custom operations on an obfuscated file.
412 static int obfsFileControl(sqlite3_file* pFile, int op, void* pArg) {
413 int rc;
414 ObfsFile* p = (ObfsFile*)pFile;
415 pFile = ORIGFILE(pFile);
416 if (op == SQLITE_FCNTL_PRAGMA) {
417 char** azArg = (char**)pArg;
418 MOZ_ASSERT(azArg[1] != nullptr);
419 if (azArg[2] != nullptr && sqlite3_stricmp(azArg[1], "page_size") == 0) {
420 /* Do not allow page size changes on an obfuscated database */
421 return SQLITE_OK;
423 } else if (op == SQLITE_FCNTL_CKPT_START || op == SQLITE_FCNTL_CKPT_DONE) {
424 p->inCkpt = op == SQLITE_FCNTL_CKPT_START;
425 if (p->pPartner) {
426 p->pPartner->inCkpt = p->inCkpt;
429 rc = pFile->pMethods->xFileControl(pFile, op, pArg);
430 if (rc == SQLITE_OK && op == SQLITE_FCNTL_VFSNAME) {
431 *(char**)pArg = sqlite3_mprintf("obfs/%z", *(char**)pArg);
433 return rc;
437 ** Return the sector-size in bytes for an obfuscated file.
439 static int obfsSectorSize(sqlite3_file* pFile) {
440 pFile = ORIGFILE(pFile);
441 return pFile->pMethods->xSectorSize(pFile);
445 ** Return the device characteristic flags supported by an obfuscated file.
447 static int obfsDeviceCharacteristics(sqlite3_file* pFile) {
448 pFile = ORIGFILE(pFile);
449 return pFile->pMethods->xDeviceCharacteristics(pFile);
452 /* Create a shared memory file mapping */
453 static int obfsShmMap(sqlite3_file* pFile, int iPg, int pgsz, int bExtend,
454 void volatile** pp) {
455 pFile = ORIGFILE(pFile);
456 return pFile->pMethods->xShmMap(pFile, iPg, pgsz, bExtend, pp);
459 /* Perform locking on a shared-memory segment */
460 static int obfsShmLock(sqlite3_file* pFile, int offset, int n, int flags) {
461 pFile = ORIGFILE(pFile);
462 return pFile->pMethods->xShmLock(pFile, offset, n, flags);
465 /* Memory barrier operation on shared memory */
466 static void obfsShmBarrier(sqlite3_file* pFile) {
467 pFile = ORIGFILE(pFile);
468 pFile->pMethods->xShmBarrier(pFile);
471 /* Unmap a shared memory segment */
472 static int obfsShmUnmap(sqlite3_file* pFile, int deleteFlag) {
473 pFile = ORIGFILE(pFile);
474 return pFile->pMethods->xShmUnmap(pFile, deleteFlag);
477 /* Fetch a page of a memory-mapped file */
478 static int obfsFetch(sqlite3_file* pFile, sqlite3_int64 iOfst, int iAmt,
479 void** pp) {
480 *pp = nullptr;
481 return SQLITE_OK;
484 /* Release a memory-mapped page */
485 static int obfsUnfetch(sqlite3_file* pFile, sqlite3_int64 iOfst, void* pPage) {
486 pFile = ORIGFILE(pFile);
487 return pFile->pMethods->xUnfetch(pFile, iOfst, pPage);
491 ** Translate a single byte of Hex into an integer.
492 ** This routine only works if h really is a valid hexadecimal
493 ** character: 0..9a..fA..F
495 static u8 obfsHexToInt(int h) {
496 MOZ_ASSERT((h >= '0' && h <= '9') || (h >= 'a' && h <= 'f') ||
497 (h >= 'A' && h <= 'F'));
498 #if 1 /* ASCII */
499 h += 9 * (1 & (h >> 6));
500 #else /* EBCDIC */
501 h += 9 * (1 & ~(h >> 4));
502 #endif
503 return (u8)(h & 0xf);
507 ** Open a new file.
509 ** If the file is an ordinary database file, or a rollback or WAL journal
510 ** file, and if the key=XXXX parameter exists, then try to open the file
511 ** as an obfuscated database. All other open attempts fall through into
512 ** the lower-level VFS shim.
514 ** If the key=XXXX parameter exists but is not 64-bytes of hex key, then
515 ** put an error message in NS_WARNING() and return SQLITE_CANTOPEN.
517 static int obfsOpen(sqlite3_vfs* pVfs, const char* zName, sqlite3_file* pFile,
518 int flags, int* pOutFlags) {
519 ObfsFile* p;
520 sqlite3_file* pSubFile;
521 sqlite3_vfs* pSubVfs;
522 int rc, i;
523 const char* zKey;
524 u8 aKey[kKeyBytes];
525 pSubVfs = ORIGVFS(pVfs);
526 if (flags &
527 (SQLITE_OPEN_MAIN_DB | SQLITE_OPEN_WAL | SQLITE_OPEN_MAIN_JOURNAL)) {
528 zKey = sqlite3_uri_parameter(zName, "key");
529 } else {
530 zKey = nullptr;
532 if (zKey == nullptr) {
533 return pSubVfs->xOpen(pSubVfs, zName, pFile, flags, pOutFlags);
535 for (i = 0;
536 i < kKeyBytes && isxdigit(zKey[i * 2]) && isxdigit(zKey[i * 2 + 1]);
537 i++) {
538 aKey[i] = (obfsHexToInt(zKey[i * 2]) << 4) | obfsHexToInt(zKey[i * 2 + 1]);
540 if (i != kKeyBytes) {
541 NS_WARNING(
542 nsPrintfCString("invalid query parameter on %s: key=%s", zName, zKey)
543 .get());
544 return SQLITE_CANTOPEN;
546 p = (ObfsFile*)pFile;
547 memset(p, 0, sizeof(*p));
549 auto encryptCipherStrategy = MakeUnique<IPCStreamCipherStrategy>();
550 auto decryptCipherStrategy = MakeUnique<IPCStreamCipherStrategy>();
552 auto resetMethods = MakeScopeExit([pFile] { pFile->pMethods = nullptr; });
554 if (NS_WARN_IF(NS_FAILED(encryptCipherStrategy->Init(
555 CipherMode::Encrypt, Span{aKey, sizeof(aKey)},
556 IPCStreamCipherStrategy::MakeBlockPrefix())))) {
557 return SQLITE_ERROR;
560 if (NS_WARN_IF(NS_FAILED(decryptCipherStrategy->Init(
561 CipherMode::Decrypt, Span{aKey, sizeof(aKey)})))) {
562 return SQLITE_ERROR;
565 pSubFile = ORIGFILE(pFile);
566 p->base.pMethods = &obfs_io_methods;
567 rc = pSubVfs->xOpen(pSubVfs, zName, pSubFile, flags, pOutFlags);
568 if (rc) {
569 return rc;
572 resetMethods.release();
574 if (flags & (SQLITE_OPEN_WAL | SQLITE_OPEN_MAIN_JOURNAL)) {
575 sqlite3_file* pDb = sqlite3_database_file_object(zName);
576 p->pPartner = (ObfsFile*)pDb;
577 MOZ_ASSERT(p->pPartner->pPartner == nullptr);
578 p->pPartner->pPartner = p;
580 p->zFName = zName;
582 p->encryptCipherStrategy = encryptCipherStrategy.release();
583 p->decryptCipherStrategy = decryptCipherStrategy.release();
585 return SQLITE_OK;
589 ** All other VFS methods are pass-thrus.
591 static int obfsDelete(sqlite3_vfs* pVfs, const char* zPath, int syncDir) {
592 return ORIGVFS(pVfs)->xDelete(ORIGVFS(pVfs), zPath, syncDir);
594 static int obfsAccess(sqlite3_vfs* pVfs, const char* zPath, int flags,
595 int* pResOut) {
596 return ORIGVFS(pVfs)->xAccess(ORIGVFS(pVfs), zPath, flags, pResOut);
598 static int obfsFullPathname(sqlite3_vfs* pVfs, const char* zPath, int nOut,
599 char* zOut) {
600 return ORIGVFS(pVfs)->xFullPathname(ORIGVFS(pVfs), zPath, nOut, zOut);
602 static void* obfsDlOpen(sqlite3_vfs* pVfs, const char* zPath) {
603 return ORIGVFS(pVfs)->xDlOpen(ORIGVFS(pVfs), zPath);
605 static void obfsDlError(sqlite3_vfs* pVfs, int nByte, char* zErrMsg) {
606 ORIGVFS(pVfs)->xDlError(ORIGVFS(pVfs), nByte, zErrMsg);
608 static void (*obfsDlSym(sqlite3_vfs* pVfs, void* p, const char* zSym))(void) {
609 return ORIGVFS(pVfs)->xDlSym(ORIGVFS(pVfs), p, zSym);
611 static void obfsDlClose(sqlite3_vfs* pVfs, void* pHandle) {
612 ORIGVFS(pVfs)->xDlClose(ORIGVFS(pVfs), pHandle);
614 static int obfsRandomness(sqlite3_vfs* pVfs, int nByte, char* zBufOut) {
615 return ORIGVFS(pVfs)->xRandomness(ORIGVFS(pVfs), nByte, zBufOut);
617 static int obfsSleep(sqlite3_vfs* pVfs, int nMicroseconds) {
618 return ORIGVFS(pVfs)->xSleep(ORIGVFS(pVfs), nMicroseconds);
620 static int obfsCurrentTime(sqlite3_vfs* pVfs, double* pTimeOut) {
621 return ORIGVFS(pVfs)->xCurrentTime(ORIGVFS(pVfs), pTimeOut);
623 static int obfsGetLastError(sqlite3_vfs* pVfs, int a, char* b) {
624 return ORIGVFS(pVfs)->xGetLastError(ORIGVFS(pVfs), a, b);
626 static int obfsCurrentTimeInt64(sqlite3_vfs* pVfs, sqlite3_int64* p) {
627 return ORIGVFS(pVfs)->xCurrentTimeInt64(ORIGVFS(pVfs), p);
629 static int obfsSetSystemCall(sqlite3_vfs* pVfs, const char* zName,
630 sqlite3_syscall_ptr pCall) {
631 return ORIGVFS(pVfs)->xSetSystemCall(ORIGVFS(pVfs), zName, pCall);
633 static sqlite3_syscall_ptr obfsGetSystemCall(sqlite3_vfs* pVfs,
634 const char* zName) {
635 return ORIGVFS(pVfs)->xGetSystemCall(ORIGVFS(pVfs), zName);
637 static const char* obfsNextSystemCall(sqlite3_vfs* pVfs, const char* zName) {
638 return ORIGVFS(pVfs)->xNextSystemCall(ORIGVFS(pVfs), zName);
641 namespace mozilla::storage::obfsvfs {
643 const char* GetVFSName() { return "obfsvfs"; }
645 UniquePtr<sqlite3_vfs> ConstructVFS(const char* aBaseVFSName) {
646 MOZ_ASSERT(aBaseVFSName);
648 if (sqlite3_vfs_find(GetVFSName()) != nullptr) {
649 return nullptr;
651 sqlite3_vfs* const pOrig = sqlite3_vfs_find(aBaseVFSName);
652 if (pOrig == nullptr) {
653 return nullptr;
656 #ifdef DEBUG
657 // If the VFS version is higher than the last known one, you should update
658 // this VFS adding appropriate methods for any methods added in the version
659 // change.
660 static constexpr int kLastKnownVfsVersion = 3;
661 MOZ_ASSERT(pOrig->iVersion <= kLastKnownVfsVersion);
662 #endif
664 const sqlite3_vfs obfs_vfs = {
665 pOrig->iVersion, /* iVersion */
666 static_cast<int>(pOrig->szOsFile + sizeof(ObfsFile)), /* szOsFile */
667 pOrig->mxPathname, /* mxPathname */
668 nullptr, /* pNext */
669 GetVFSName(), /* zName */
670 pOrig, /* pAppData */
671 obfsOpen, /* xOpen */
672 obfsDelete, /* xDelete */
673 obfsAccess, /* xAccess */
674 obfsFullPathname, /* xFullPathname */
675 obfsDlOpen, /* xDlOpen */
676 obfsDlError, /* xDlError */
677 obfsDlSym, /* xDlSym */
678 obfsDlClose, /* xDlClose */
679 obfsRandomness, /* xRandomness */
680 obfsSleep, /* xSleep */
681 obfsCurrentTime, /* xCurrentTime */
682 obfsGetLastError, /* xGetLastError */
683 obfsCurrentTimeInt64, /* xCurrentTimeInt64 */
684 obfsSetSystemCall, /* xSetSystemCall */
685 obfsGetSystemCall, /* xGetSystemCall */
686 obfsNextSystemCall /* xNextSystemCall */
689 return MakeUnique<sqlite3_vfs>(obfs_vfs);
692 already_AddRefed<QuotaObject> GetQuotaObjectForFile(sqlite3_file* pFile) {
693 return quotavfs::GetQuotaObjectForFile(ORIGFILE(pFile));
696 } // namespace mozilla::storage::obfsvfs