tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

QuotaVFS.cpp (19474B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "QuotaVFS.h"
      8 
      9 #include "mozilla/dom/quota/PersistenceType.h"
     10 #include "mozilla/dom/quota/QuotaManager.h"
     11 #include "mozilla/dom/quota/QuotaObject.h"
     12 #include "mozilla/dom/quota/ResultExtensions.h"
     13 #include "mozilla/StaticPrefs_storage.h"
     14 #include "nsDirectoryServiceDefs.h"
     15 #include "nsEscape.h"
     16 #include "sqlite3.h"
     17 
     18 #if defined(XP_WIN) || defined(XP_UNIX)
     19 #  include "mozilla/StaticPrefs_dom.h"
     20 #endif
     21 
     22 // The last VFS version for which this file has been updated.
     23 #define LAST_KNOWN_VFS_VERSION 3
     24 
     25 // The last io_methods version for which this file has been updated.
     26 #define LAST_KNOWN_IOMETHODS_VERSION 3
     27 
     28 namespace {
     29 
     30 using namespace mozilla;
     31 using namespace mozilla::dom::quota;
     32 
     33 struct QuotaFile {
     34  // Base class.  Must be first
     35  sqlite3_file base;
     36 
     37  // quota object for this file
     38  RefPtr<QuotaObject> quotaObject;
     39 
     40  // The chunk size for this file. See the documentation for
     41  // sqlite3_file_control() and FCNTL_CHUNK_SIZE.
     42  int fileChunkSize;
     43 
     44  // This contains the vfs that actually does work
     45  sqlite3_file pReal[1];
     46 };
     47 
     48 already_AddRefed<QuotaObject> GetQuotaObjectFromName(const char* zName) {
     49  MOZ_ASSERT(zName);
     50 
     51  const char* directoryLockIdParam =
     52      sqlite3_uri_parameter(zName, "directoryLockId");
     53  if (!directoryLockIdParam) {
     54    return nullptr;
     55  }
     56 
     57  nsresult rv;
     58  const int64_t directoryLockId =
     59      nsDependentCString(directoryLockIdParam).ToInteger64(&rv);
     60  MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
     61 
     62  QuotaManager* quotaManager = QuotaManager::Get();
     63  MOZ_ASSERT(quotaManager);
     64 
     65  return quotaManager->GetQuotaObject(directoryLockId,
     66                                      NS_ConvertUTF8toUTF16(zName));
     67 }
     68 
     69 void MaybeEstablishQuotaControl(const char* zName, QuotaFile* pFile,
     70                                int flags) {
     71  MOZ_ASSERT(pFile);
     72  MOZ_ASSERT(!pFile->quotaObject);
     73 
     74  if (!(flags & (SQLITE_OPEN_URI | SQLITE_OPEN_WAL))) {
     75    return;
     76  }
     77  pFile->quotaObject = GetQuotaObjectFromName(zName);
     78 }
     79 
     80 /*
     81 ** Close a QuotaFile.
     82 */
     83 int QuotaClose(sqlite3_file* pFile) {
     84  QuotaFile* p = (QuotaFile*)pFile;
     85  int rc;
     86  rc = p->pReal->pMethods->xClose(p->pReal);
     87  if (rc == SQLITE_OK) {
     88    delete p->base.pMethods;
     89    p->base.pMethods = nullptr;
     90    p->quotaObject = nullptr;
     91 #ifdef DEBUG
     92    p->fileChunkSize = 0;
     93 #endif
     94  }
     95  return rc;
     96 }
     97 
     98 /*
     99 ** Read data from a QuotaFile.
    100 */
    101 int QuotaRead(sqlite3_file* pFile, void* zBuf, int iAmt, sqlite_int64 iOfst) {
    102  QuotaFile* p = (QuotaFile*)pFile;
    103  int rc;
    104  rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst);
    105  return rc;
    106 }
    107 
    108 /*
    109 ** Return the current file-size of a QuotaFile.
    110 */
    111 int QuotaFileSize(sqlite3_file* pFile, sqlite_int64* pSize) {
    112  QuotaFile* p = (QuotaFile*)pFile;
    113  int rc;
    114  rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);
    115  return rc;
    116 }
    117 
    118 /*
    119 ** Write data to a QuotaFile.
    120 */
    121 int QuotaWrite(sqlite3_file* pFile, const void* zBuf, int iAmt,
    122               sqlite_int64 iOfst) {
    123  QuotaFile* p = (QuotaFile*)pFile;
    124  int rc;
    125  if (p->quotaObject) {
    126    MOZ_ASSERT(INT64_MAX - iOfst >= iAmt);
    127    if (!p->quotaObject->MaybeUpdateSize(iOfst + iAmt, /* aTruncate */ false)) {
    128      return SQLITE_FULL;
    129    }
    130  }
    131  rc = p->pReal->pMethods->xWrite(p->pReal, zBuf, iAmt, iOfst);
    132  if (p->quotaObject && rc != SQLITE_OK) {
    133    NS_WARNING(
    134        "xWrite failed on a quota-controlled file, attempting to "
    135        "update its current size...");
    136    sqlite_int64 currentSize;
    137    if (QuotaFileSize(pFile, &currentSize) == SQLITE_OK) {
    138      DebugOnly<bool> res =
    139          p->quotaObject->MaybeUpdateSize(currentSize, /* aTruncate */ true);
    140      MOZ_ASSERT(res);
    141    }
    142  }
    143  return rc;
    144 }
    145 
    146 /*
    147 ** Truncate a QuotaFile.
    148 */
    149 int QuotaTruncate(sqlite3_file* pFile, sqlite_int64 size) {
    150  QuotaFile* p = (QuotaFile*)pFile;
    151  int rc;
    152  if (p->quotaObject) {
    153    if (p->fileChunkSize > 0) {
    154      // Round up to the smallest multiple of the chunk size that will hold all
    155      // the data.
    156      size =
    157          ((size + p->fileChunkSize - 1) / p->fileChunkSize) * p->fileChunkSize;
    158    }
    159    if (!p->quotaObject->MaybeUpdateSize(size, /* aTruncate */ true)) {
    160      return SQLITE_FULL;
    161    }
    162  }
    163  rc = p->pReal->pMethods->xTruncate(p->pReal, size);
    164  if (p->quotaObject) {
    165    if (rc == SQLITE_OK) {
    166 #ifdef DEBUG
    167      // Make sure xTruncate set the size exactly as we calculated above.
    168      sqlite_int64 newSize;
    169      MOZ_ASSERT(QuotaFileSize(pFile, &newSize) == SQLITE_OK);
    170      MOZ_ASSERT(newSize == size);
    171 #endif
    172    } else {
    173      NS_WARNING(
    174          "xTruncate failed on a quota-controlled file, attempting to "
    175          "update its current size...");
    176      if (QuotaFileSize(pFile, &size) == SQLITE_OK) {
    177        DebugOnly<bool> res =
    178            p->quotaObject->MaybeUpdateSize(size, /* aTruncate */ true);
    179        MOZ_ASSERT(res);
    180      }
    181    }
    182  }
    183  return rc;
    184 }
    185 
    186 /*
    187 ** Sync a QuotaFile.
    188 */
    189 int QuotaSync(sqlite3_file* pFile, int flags) {
    190  QuotaFile* p = (QuotaFile*)pFile;
    191  return p->pReal->pMethods->xSync(p->pReal, flags);
    192 }
    193 
    194 /*
    195 ** Lock a QuotaFile.
    196 */
    197 int QuotaLock(sqlite3_file* pFile, int eLock) {
    198  QuotaFile* p = (QuotaFile*)pFile;
    199  int rc;
    200  rc = p->pReal->pMethods->xLock(p->pReal, eLock);
    201  return rc;
    202 }
    203 
    204 /*
    205 ** Unlock a QuotaFile.
    206 */
    207 int QuotaUnlock(sqlite3_file* pFile, int eLock) {
    208  QuotaFile* p = (QuotaFile*)pFile;
    209  int rc;
    210  rc = p->pReal->pMethods->xUnlock(p->pReal, eLock);
    211  return rc;
    212 }
    213 
    214 /*
    215 ** Check if another file-handle holds a RESERVED lock on a QuotaFile.
    216 */
    217 int QuotaCheckReservedLock(sqlite3_file* pFile, int* pResOut) {
    218  QuotaFile* p = (QuotaFile*)pFile;
    219  int rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut);
    220  return rc;
    221 }
    222 
    223 /*
    224 ** File control method. For custom operations on a QuotaFile.
    225 */
    226 int QuotaFileControl(sqlite3_file* pFile, int op, void* pArg) {
    227  QuotaFile* p = (QuotaFile*)pFile;
    228  int rc;
    229  // Hook SQLITE_FCNTL_SIZE_HINT for quota-controlled files and do the necessary
    230  // work before passing to the SQLite VFS.
    231  if (op == SQLITE_FCNTL_SIZE_HINT && p->quotaObject) {
    232    sqlite3_int64 hintSize = *static_cast<sqlite3_int64*>(pArg);
    233    sqlite3_int64 currentSize;
    234    rc = QuotaFileSize(pFile, &currentSize);
    235    if (rc != SQLITE_OK) {
    236      return rc;
    237    }
    238    if (hintSize > currentSize) {
    239      rc = QuotaTruncate(pFile, hintSize);
    240      if (rc != SQLITE_OK) {
    241        return rc;
    242      }
    243    }
    244  }
    245  rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
    246  // Grab the file chunk size after the SQLite VFS has approved.
    247  if (op == SQLITE_FCNTL_CHUNK_SIZE && rc == SQLITE_OK) {
    248    p->fileChunkSize = *static_cast<int*>(pArg);
    249  }
    250 #ifdef DEBUG
    251  if (op == SQLITE_FCNTL_SIZE_HINT && p->quotaObject && rc == SQLITE_OK) {
    252    sqlite3_int64 hintSize = *static_cast<sqlite3_int64*>(pArg);
    253    if (p->fileChunkSize > 0) {
    254      hintSize = ((hintSize + p->fileChunkSize - 1) / p->fileChunkSize) *
    255                 p->fileChunkSize;
    256    }
    257    sqlite3_int64 currentSize;
    258    MOZ_ASSERT(QuotaFileSize(pFile, &currentSize) == SQLITE_OK);
    259    MOZ_ASSERT(currentSize >= hintSize);
    260  }
    261 #endif
    262  return rc;
    263 }
    264 
    265 /*
    266 ** Return the sector-size in bytes for a QuotaFile.
    267 */
    268 int QuotaSectorSize(sqlite3_file* pFile) {
    269  QuotaFile* p = (QuotaFile*)pFile;
    270  int rc;
    271  rc = p->pReal->pMethods->xSectorSize(p->pReal);
    272  return rc;
    273 }
    274 
    275 /*
    276 ** Return the device characteristic flags supported by a QuotaFile.
    277 */
    278 int QuotaDeviceCharacteristics(sqlite3_file* pFile) {
    279  QuotaFile* p = (QuotaFile*)pFile;
    280  int rc;
    281  rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal);
    282  return rc;
    283 }
    284 
    285 /*
    286 ** Shared-memory operations.
    287 */
    288 int QuotaShmLock(sqlite3_file* pFile, int ofst, int n, int flags) {
    289  QuotaFile* p = (QuotaFile*)pFile;
    290  return p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags);
    291 }
    292 
    293 int QuotaShmMap(sqlite3_file* pFile, int iRegion, int szRegion, int isWrite,
    294                void volatile** pp) {
    295  QuotaFile* p = (QuotaFile*)pFile;
    296  int rc;
    297  rc = p->pReal->pMethods->xShmMap(p->pReal, iRegion, szRegion, isWrite, pp);
    298  return rc;
    299 }
    300 
    301 void QuotaShmBarrier(sqlite3_file* pFile) {
    302  QuotaFile* p = (QuotaFile*)pFile;
    303  p->pReal->pMethods->xShmBarrier(p->pReal);
    304 }
    305 
    306 int QuotaShmUnmap(sqlite3_file* pFile, int delFlag) {
    307  QuotaFile* p = (QuotaFile*)pFile;
    308  int rc;
    309  rc = p->pReal->pMethods->xShmUnmap(p->pReal, delFlag);
    310  return rc;
    311 }
    312 
    313 int QuotaFetch(sqlite3_file* pFile, sqlite3_int64 iOff, int iAmt, void** pp) {
    314  QuotaFile* p = (QuotaFile*)pFile;
    315  MOZ_ASSERT(p->pReal->pMethods->iVersion >= 3);
    316  return p->pReal->pMethods->xFetch(p->pReal, iOff, iAmt, pp);
    317 }
    318 
    319 int QuotaUnfetch(sqlite3_file* pFile, sqlite3_int64 iOff, void* pResOut) {
    320  QuotaFile* p = (QuotaFile*)pFile;
    321  MOZ_ASSERT(p->pReal->pMethods->iVersion >= 3);
    322  return p->pReal->pMethods->xUnfetch(p->pReal, iOff, pResOut);
    323 }
    324 
    325 int QuotaOpen(sqlite3_vfs* vfs, const char* zName, sqlite3_file* pFile,
    326              int flags, int* pOutFlags) {
    327  sqlite3_vfs* orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
    328  int rc;
    329  QuotaFile* p = (QuotaFile*)pFile;
    330 
    331  MaybeEstablishQuotaControl(zName, p, flags);
    332 
    333  rc = orig_vfs->xOpen(orig_vfs, zName, p->pReal, flags, pOutFlags);
    334  if (rc != SQLITE_OK) return rc;
    335  if (p->pReal->pMethods) {
    336    sqlite3_io_methods* pNew = new sqlite3_io_methods;
    337    const sqlite3_io_methods* pSub = p->pReal->pMethods;
    338    memset(pNew, 0, sizeof(*pNew));
    339    // If the io_methods version is higher than the last known one, you should
    340    // update this VFS adding appropriate IO methods for any methods added in
    341    // the version change.
    342    pNew->iVersion = pSub->iVersion;
    343    MOZ_ASSERT(pNew->iVersion <= LAST_KNOWN_IOMETHODS_VERSION);
    344    pNew->xClose = QuotaClose;
    345    pNew->xRead = QuotaRead;
    346    pNew->xWrite = QuotaWrite;
    347    pNew->xTruncate = QuotaTruncate;
    348    pNew->xSync = QuotaSync;
    349    pNew->xFileSize = QuotaFileSize;
    350    pNew->xLock = QuotaLock;
    351    pNew->xUnlock = QuotaUnlock;
    352    pNew->xCheckReservedLock = QuotaCheckReservedLock;
    353    pNew->xFileControl = QuotaFileControl;
    354    pNew->xSectorSize = QuotaSectorSize;
    355    pNew->xDeviceCharacteristics = QuotaDeviceCharacteristics;
    356    if (pNew->iVersion >= 2) {
    357      // Methods added in version 2.
    358      pNew->xShmMap = pSub->xShmMap ? QuotaShmMap : nullptr;
    359      pNew->xShmLock = pSub->xShmLock ? QuotaShmLock : nullptr;
    360      pNew->xShmBarrier = pSub->xShmBarrier ? QuotaShmBarrier : nullptr;
    361      pNew->xShmUnmap = pSub->xShmUnmap ? QuotaShmUnmap : nullptr;
    362    }
    363    if (pNew->iVersion >= 3) {
    364      // Methods added in version 3.
    365      // SQLite 3.7.17 calls these methods without checking for nullptr first,
    366      // so we always define them.  Verify that we're not going to call
    367      // nullptrs, though.
    368      MOZ_ASSERT(pSub->xFetch);
    369      pNew->xFetch = QuotaFetch;
    370      MOZ_ASSERT(pSub->xUnfetch);
    371      pNew->xUnfetch = QuotaUnfetch;
    372    }
    373    pFile->pMethods = pNew;
    374  }
    375  return rc;
    376 }
    377 
    378 int QuotaDelete(sqlite3_vfs* vfs, const char* zName, int syncDir) {
    379  sqlite3_vfs* orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
    380  int rc;
    381  RefPtr<QuotaObject> quotaObject;
    382 
    383  if (StringEndsWith(nsDependentCString(zName), "-wal"_ns)) {
    384    quotaObject = GetQuotaObjectFromName(zName);
    385  }
    386 
    387  rc = orig_vfs->xDelete(orig_vfs, zName, syncDir);
    388  if (rc == SQLITE_OK && quotaObject) {
    389    MOZ_ALWAYS_TRUE(quotaObject->MaybeUpdateSize(0, /* aTruncate */ true));
    390  }
    391 
    392  return rc;
    393 }
    394 
    395 int QuotaAccess(sqlite3_vfs* vfs, const char* zName, int flags, int* pResOut) {
    396  sqlite3_vfs* orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
    397  return orig_vfs->xAccess(orig_vfs, zName, flags, pResOut);
    398 }
    399 
    400 int QuotaFullPathname(sqlite3_vfs* vfs, const char* zName, int nOut,
    401                      char* zOut) {
    402 #if defined(XP_WIN)
    403  // SQLite uses GetFullPathnameW which also normailizes file path. If a file
    404  // component ends with a dot, it would be removed. However, it's not desired.
    405  //
    406  // And that would result SQLite uses wrong database and quotaObject.
    407  // Note that we are safe to avoid the GetFullPathnameW call for \\?\ prefixed
    408  // paths.
    409  // And note that this hack will be removed once the issue is fixed directly in
    410  // SQLite.
    411 
    412  // zName that starts with "//?/" is the case when a file URI was passed and
    413  // zName that starts with "\\?\" is the case when a normal path was passed
    414  // (not file URI).
    415  if (StaticPrefs::dom_quotaManager_overrideXFullPathname() &&
    416      ((zName[0] == '/' && zName[1] == '/' && zName[2] == '?' &&
    417        zName[3] == '/') ||
    418       (zName[0] == '\\' && zName[1] == '\\' && zName[2] == '?' &&
    419        zName[3] == '\\'))) {
    420    MOZ_ASSERT(nOut >= vfs->mxPathname);
    421    MOZ_ASSERT(static_cast<size_t>(nOut) > strlen(zName));
    422 
    423    size_t index = 0;
    424    while (zName[index] != '\0') {
    425      if (zName[index] == '/') {
    426        zOut[index] = '\\';
    427      } else {
    428        zOut[index] = zName[index];
    429      }
    430 
    431      index++;
    432    }
    433    zOut[index] = '\0';
    434 
    435    return SQLITE_OK;
    436  }
    437 #elif defined(XP_UNIX)
    438  // SQLite canonicalizes (resolves path components) file paths on Unix which
    439  // doesn't work well with file path sanity checks in quota manager. This is
    440  // especially a problem on mac where /var is a symlink to /private/var.
    441  // Since QuotaVFS is used only by quota clients which never access databases
    442  // outside of PROFILE/storage, we override Unix xFullPathname with own
    443  // implementation that doesn't do any canonicalization.
    444 
    445  if (StaticPrefs::dom_quotaManager_overrideXFullPathnameUnix()) {
    446    if (nOut < 0) {
    447      // Match the return code used by SQLite's xFullPathname implementation
    448      // here and below.
    449      return SQLITE_CANTOPEN;
    450    }
    451 
    452    QM_TRY_INSPECT(
    453        const auto& path, ([&zName]() -> Result<nsString, nsresult> {
    454          NS_ConvertUTF8toUTF16 name(zName);
    455 
    456          if (name.First() == '/') {
    457            return std::move(name);
    458          }
    459 
    460          QM_TRY_INSPECT(const auto& file,
    461                         MOZ_TO_RESULT_INVOKE_TYPED(nsCOMPtr<nsIFile>,
    462                                                    NS_GetSpecialDirectory,
    463                                                    NS_OS_CURRENT_WORKING_DIR));
    464 
    465          QM_TRY(MOZ_TO_RESULT(file->Append(name)));
    466 
    467          QM_TRY_RETURN(
    468              MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsString, file, GetPath));
    469        }()),
    470        SQLITE_CANTOPEN);
    471 
    472    QM_TRY_INSPECT(const auto& quotaFile, QM_NewLocalFile(path),
    473                   SQLITE_CANTOPEN);
    474 
    475    QM_TRY_INSPECT(
    476        const auto& quotaPath,
    477        MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsString, quotaFile, GetPath),
    478        SQLITE_CANTOPEN);
    479 
    480    NS_ConvertUTF16toUTF8 sqlitePath(quotaPath);
    481 
    482    if (sqlitePath.Length() > (unsigned int)nOut) {
    483      return SQLITE_CANTOPEN;
    484    }
    485 
    486    nsCharTraits<char>::copy(zOut, sqlitePath.get(), sqlitePath.Length());
    487    zOut[sqlitePath.Length()] = '\0';
    488 
    489    return SQLITE_OK;
    490  }
    491 #endif
    492 
    493  sqlite3_vfs* orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
    494  return orig_vfs->xFullPathname(orig_vfs, zName, nOut, zOut);
    495 }
    496 
    497 void* QuotaDlOpen(sqlite3_vfs* vfs, const char* zFilename) {
    498  sqlite3_vfs* orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
    499  return orig_vfs->xDlOpen(orig_vfs, zFilename);
    500 }
    501 
    502 void QuotaDlError(sqlite3_vfs* vfs, int nByte, char* zErrMsg) {
    503  sqlite3_vfs* orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
    504  orig_vfs->xDlError(orig_vfs, nByte, zErrMsg);
    505 }
    506 
    507 void (*QuotaDlSym(sqlite3_vfs* vfs, void* pHdle, const char* zSym))(void) {
    508  sqlite3_vfs* orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
    509  return orig_vfs->xDlSym(orig_vfs, pHdle, zSym);
    510 }
    511 
    512 void QuotaDlClose(sqlite3_vfs* vfs, void* pHandle) {
    513  sqlite3_vfs* orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
    514  orig_vfs->xDlClose(orig_vfs, pHandle);
    515 }
    516 
    517 int QuotaRandomness(sqlite3_vfs* vfs, int nByte, char* zOut) {
    518  sqlite3_vfs* orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
    519  return orig_vfs->xRandomness(orig_vfs, nByte, zOut);
    520 }
    521 
    522 int QuotaSleep(sqlite3_vfs* vfs, int microseconds) {
    523  sqlite3_vfs* orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
    524  return orig_vfs->xSleep(orig_vfs, microseconds);
    525 }
    526 
    527 int QuotaCurrentTime(sqlite3_vfs* vfs, double* prNow) {
    528  sqlite3_vfs* orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
    529  return orig_vfs->xCurrentTime(orig_vfs, prNow);
    530 }
    531 
    532 int QuotaGetLastError(sqlite3_vfs* vfs, int nBuf, char* zBuf) {
    533  sqlite3_vfs* orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
    534  return orig_vfs->xGetLastError(orig_vfs, nBuf, zBuf);
    535 }
    536 
    537 int QuotaCurrentTimeInt64(sqlite3_vfs* vfs, sqlite3_int64* piNow) {
    538  sqlite3_vfs* orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
    539  return orig_vfs->xCurrentTimeInt64(orig_vfs, piNow);
    540 }
    541 
    542 static int QuotaSetSystemCall(sqlite3_vfs* vfs, const char* zName,
    543                              sqlite3_syscall_ptr pFunc) {
    544  sqlite3_vfs* orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
    545  return orig_vfs->xSetSystemCall(orig_vfs, zName, pFunc);
    546 }
    547 
    548 static sqlite3_syscall_ptr QuotaGetSystemCall(sqlite3_vfs* vfs,
    549                                              const char* zName) {
    550  sqlite3_vfs* orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
    551  return orig_vfs->xGetSystemCall(orig_vfs, zName);
    552 }
    553 
    554 static const char* QuotaNextSystemCall(sqlite3_vfs* vfs, const char* zName) {
    555  sqlite3_vfs* orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
    556  return orig_vfs->xNextSystemCall(orig_vfs, zName);
    557 }
    558 
    559 }  // namespace
    560 
    561 namespace mozilla::storage::quotavfs {
    562 
    563 const char* GetVFSName() { return "quotavfs"; }
    564 
    565 UniquePtr<sqlite3_vfs> ConstructVFS(const char* aBaseVFSName) {
    566  MOZ_ASSERT(aBaseVFSName);
    567 
    568  if (sqlite3_vfs_find(GetVFSName()) != nullptr) {
    569    return nullptr;
    570  }
    571  sqlite3_vfs* vfs = sqlite3_vfs_find(aBaseVFSName);
    572  if (!vfs) {
    573    return nullptr;
    574  }
    575 
    576  auto qvfs = MakeUnique<sqlite3_vfs>();
    577  memset(qvfs.get(), 0, sizeof(::sqlite3_vfs));
    578  // If the VFS version is higher than the last known one, you should update
    579  // this VFS adding appropriate methods for any methods added in the version
    580  // change.
    581  qvfs->iVersion = vfs->iVersion;
    582  MOZ_ASSERT(vfs->iVersion <= LAST_KNOWN_VFS_VERSION);
    583  qvfs->szOsFile = static_cast<int>(sizeof(QuotaFile) - sizeof(sqlite3_file) +
    584                                    vfs->szOsFile);
    585  qvfs->mxPathname = vfs->mxPathname;
    586  qvfs->zName = GetVFSName();
    587  qvfs->pAppData = vfs;
    588  qvfs->xOpen = QuotaOpen;
    589  qvfs->xDelete = QuotaDelete;
    590  qvfs->xAccess = QuotaAccess;
    591  qvfs->xFullPathname = QuotaFullPathname;
    592  qvfs->xDlOpen = QuotaDlOpen;
    593  qvfs->xDlError = QuotaDlError;
    594  qvfs->xDlSym = QuotaDlSym;
    595  qvfs->xDlClose = QuotaDlClose;
    596  qvfs->xRandomness = QuotaRandomness;
    597  qvfs->xSleep = QuotaSleep;
    598  qvfs->xCurrentTime = QuotaCurrentTime;
    599  qvfs->xGetLastError = QuotaGetLastError;
    600  if (qvfs->iVersion >= 2) {
    601    // Methods added in version 2.
    602    qvfs->xCurrentTimeInt64 = QuotaCurrentTimeInt64;
    603  }
    604  if (qvfs->iVersion >= 3) {
    605    // Methods added in version 3.
    606    qvfs->xSetSystemCall = QuotaSetSystemCall;
    607    qvfs->xGetSystemCall = QuotaGetSystemCall;
    608    qvfs->xNextSystemCall = QuotaNextSystemCall;
    609  }
    610  return qvfs;
    611 }
    612 
    613 already_AddRefed<QuotaObject> GetQuotaObjectForFile(sqlite3_file* pFile) {
    614  MOZ_ASSERT(pFile);
    615 
    616  QuotaFile* p = (QuotaFile*)pFile;
    617  RefPtr<QuotaObject> result = p->quotaObject;
    618  return result.forget();
    619 }
    620 
    621 }  // namespace mozilla::storage::quotavfs