4 ** The author disclaims copyright to this source code. In place of
5 ** a legal notice, here is a blessing:
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.
18 ** This extension requires SQLite 3.32.0 or later.
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.
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.
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
70 ** * Requires SQLite 3.32.0 or later.
72 #include "ObfuscatingVFS.h"
76 #include <stdio.h> /* For debugging only */
78 #include "mozilla/dom/quota/IPCStreamCipherStrategy.h"
79 #include "mozilla/ScopeExit.h"
80 #include "nsPrintfCString.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;
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
;
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
= {
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. */
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",
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"
240 memcpy(pOut
, a
, kClearTextPrefixBytesOnFirstPage
);
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
);
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. */
270 if (memcmp(a
, "SQLite format 3", 16) == 0) {
271 i
= kClearTextPrefixBytesOnFirstPage
;
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
;
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
) {
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
) &&
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,
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
));
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) {
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
;
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
) {
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 */
423 } else if (op
== SQLITE_FCNTL_CKPT_START
|| op
== SQLITE_FCNTL_CKPT_DONE
) {
424 p
->inCkpt
= op
== SQLITE_FCNTL_CKPT_START
;
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
);
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
,
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'));
499 h
+= 9 * (1 & (h
>> 6));
501 h
+= 9 * (1 & ~(h
>> 4));
503 return (u8
)(h
& 0xf);
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
) {
520 sqlite3_file
* pSubFile
;
521 sqlite3_vfs
* pSubVfs
;
525 pSubVfs
= ORIGVFS(pVfs
);
527 (SQLITE_OPEN_MAIN_DB
| SQLITE_OPEN_WAL
| SQLITE_OPEN_MAIN_JOURNAL
)) {
528 zKey
= sqlite3_uri_parameter(zName
, "key");
532 if (zKey
== nullptr) {
533 return pSubVfs
->xOpen(pSubVfs
, zName
, pFile
, flags
, pOutFlags
);
536 i
< kKeyBytes
&& isxdigit(zKey
[i
* 2]) && isxdigit(zKey
[i
* 2 + 1]);
538 aKey
[i
] = (obfsHexToInt(zKey
[i
* 2]) << 4) | obfsHexToInt(zKey
[i
* 2 + 1]);
540 if (i
!= kKeyBytes
) {
542 nsPrintfCString("invalid query parameter on %s: key=%s", zName
, zKey
)
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())))) {
560 if (NS_WARN_IF(NS_FAILED(decryptCipherStrategy
->Init(
561 CipherMode::Decrypt
, Span
{aKey
, sizeof(aKey
)})))) {
565 pSubFile
= ORIGFILE(pFile
);
566 p
->base
.pMethods
= &obfs_io_methods
;
567 rc
= pSubVfs
->xOpen(pSubVfs
, zName
, pSubFile
, flags
, pOutFlags
);
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
;
582 p
->encryptCipherStrategy
= encryptCipherStrategy
.release();
583 p
->decryptCipherStrategy
= decryptCipherStrategy
.release();
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
,
596 return ORIGVFS(pVfs
)->xAccess(ORIGVFS(pVfs
), zPath
, flags
, pResOut
);
598 static int obfsFullPathname(sqlite3_vfs
* pVfs
, const char* zPath
, int nOut
,
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
,
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) {
651 sqlite3_vfs
* const pOrig
= sqlite3_vfs_find(aBaseVFSName
);
652 if (pOrig
== nullptr) {
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
660 static constexpr int kLastKnownVfsVersion
= 3;
661 MOZ_ASSERT(pOrig
->iVersion
<= kLastKnownVfsVersion
);
664 const sqlite3_vfs obfs_vfs
= {
665 pOrig
->iVersion
, /* iVersion */
666 static_cast<int>(pOrig
->szOsFile
+ sizeof(ObfsFile
)), /* szOsFile */
667 pOrig
->mxPathname
, /* mxPathname */
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