michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: #include "mozilla/Telemetry.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "sqlite3.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "mozilla/dom/quota/PersistenceType.h" michael@0: #include "mozilla/dom/quota/QuotaManager.h" michael@0: #include "mozilla/dom/quota/QuotaObject.h" michael@0: #include "mozilla/IOInterposer.h" michael@0: michael@0: // The last VFS version for which this file has been updated. michael@0: #define LAST_KNOWN_VFS_VERSION 3 michael@0: michael@0: // The last io_methods version for which this file has been updated. michael@0: #define LAST_KNOWN_IOMETHODS_VERSION 3 michael@0: michael@0: /** michael@0: * This preference is a workaround to allow users/sysadmins to identify michael@0: * that the profile exists on an NFS share whose implementation michael@0: * is incompatible with SQLite's default locking implementation. michael@0: * Bug 433129 attempted to automatically identify such file-systems, michael@0: * but a reliable way was not found and it was determined that the fallback michael@0: * locking is slower than POSIX locking, so we do not want to do it by default. michael@0: */ michael@0: #define PREF_NFS_FILESYSTEM "storage.nfs_filesystem" michael@0: michael@0: namespace { michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom::quota; michael@0: michael@0: struct Histograms { michael@0: const char *name; michael@0: const Telemetry::ID readB; michael@0: const Telemetry::ID writeB; michael@0: const Telemetry::ID readMS; michael@0: const Telemetry::ID writeMS; michael@0: const Telemetry::ID syncMS; michael@0: }; michael@0: michael@0: #define SQLITE_TELEMETRY(FILENAME, HGRAM) \ michael@0: { FILENAME, \ michael@0: Telemetry::MOZ_SQLITE_ ## HGRAM ## _READ_B, \ michael@0: Telemetry::MOZ_SQLITE_ ## HGRAM ## _WRITE_B, \ michael@0: Telemetry::MOZ_SQLITE_ ## HGRAM ## _READ_MS, \ michael@0: Telemetry::MOZ_SQLITE_ ## HGRAM ## _WRITE_MS, \ michael@0: Telemetry::MOZ_SQLITE_ ## HGRAM ## _SYNC_MS \ michael@0: } michael@0: michael@0: Histograms gHistograms[] = { michael@0: SQLITE_TELEMETRY("places.sqlite", PLACES), michael@0: SQLITE_TELEMETRY("cookies.sqlite", COOKIES), michael@0: SQLITE_TELEMETRY("webappsstore.sqlite", WEBAPPS), michael@0: SQLITE_TELEMETRY(nullptr, OTHER) michael@0: }; michael@0: #undef SQLITE_TELEMETRY michael@0: michael@0: /** RAII class for measuring how long io takes on/off main thread michael@0: */ michael@0: class IOThreadAutoTimer { michael@0: public: michael@0: /** michael@0: * IOThreadAutoTimer measures time spent in IO. Additionally it michael@0: * automatically determines whether IO is happening on the main michael@0: * thread and picks an appropriate histogram. michael@0: * michael@0: * @param id takes a telemetry histogram id. The id+1 must be an michael@0: * equivalent histogram for the main thread. Eg, MOZ_SQLITE_OPEN_MS michael@0: * is followed by MOZ_SQLITE_OPEN_MAIN_THREAD_MS. michael@0: * michael@0: * @param aOp optionally takes an IO operation to report through the michael@0: * IOInterposer. Filename will be reported as NULL, and reference will be michael@0: * either "sqlite-mainthread" or "sqlite-otherthread". michael@0: */ michael@0: IOThreadAutoTimer(Telemetry::ID id, michael@0: IOInterposeObserver::Operation aOp = IOInterposeObserver::OpNone) michael@0: : start(TimeStamp::Now()), michael@0: id(id), michael@0: op(aOp) michael@0: { michael@0: } michael@0: michael@0: /** michael@0: * This constructor is for when we want to report an operation to michael@0: * IOInterposer but do not require a telemetry probe. michael@0: * michael@0: * @param aOp IO Operation to report through the IOInterposer. michael@0: */ michael@0: IOThreadAutoTimer(IOInterposeObserver::Operation aOp) michael@0: : start(TimeStamp::Now()), michael@0: id(Telemetry::HistogramCount), michael@0: op(aOp) michael@0: { michael@0: } michael@0: michael@0: ~IOThreadAutoTimer() michael@0: { michael@0: TimeStamp end(TimeStamp::Now()); michael@0: uint32_t mainThread = NS_IsMainThread() ? 1 : 0; michael@0: if (id != Telemetry::HistogramCount) { michael@0: Telemetry::AccumulateTimeDelta(static_cast(id + mainThread), michael@0: start, end); michael@0: } michael@0: // We don't report SQLite I/O on Windows because we have a comprehensive michael@0: // mechanism for intercepting I/O on that platform that captures a superset michael@0: // of the data captured here. michael@0: #if defined(MOZ_ENABLE_PROFILER_SPS) && !defined(XP_WIN) michael@0: if (IOInterposer::IsObservedOperation(op)) { michael@0: const char* main_ref = "sqlite-mainthread"; michael@0: const char* other_ref = "sqlite-otherthread"; michael@0: michael@0: // Create observation michael@0: IOInterposeObserver::Observation ob(op, start, end, michael@0: (mainThread ? main_ref : other_ref)); michael@0: // Report observation michael@0: IOInterposer::Report(ob); michael@0: } michael@0: #endif /* defined(MOZ_ENABLE_PROFILER_SPS) && !defined(XP_WIN) */ michael@0: } michael@0: michael@0: private: michael@0: const TimeStamp start; michael@0: const Telemetry::ID id; michael@0: IOInterposeObserver::Operation op; michael@0: }; michael@0: michael@0: struct telemetry_file { michael@0: // Base class. Must be first michael@0: sqlite3_file base; michael@0: michael@0: // histograms pertaining to this file michael@0: Histograms *histograms; michael@0: michael@0: // quota object for this file michael@0: nsRefPtr quotaObject; michael@0: michael@0: // This contains the vfs that actually does work michael@0: sqlite3_file pReal[1]; michael@0: }; michael@0: michael@0: /* michael@0: ** Close a telemetry_file. michael@0: */ michael@0: int michael@0: xClose(sqlite3_file *pFile) michael@0: { michael@0: telemetry_file *p = (telemetry_file *)pFile; michael@0: int rc; michael@0: { // Scope for IOThreadAutoTimer michael@0: IOThreadAutoTimer ioTimer(IOInterposeObserver::OpClose); michael@0: rc = p->pReal->pMethods->xClose(p->pReal); michael@0: } michael@0: if( rc==SQLITE_OK ){ michael@0: delete p->base.pMethods; michael@0: p->base.pMethods = nullptr; michael@0: p->quotaObject = nullptr; michael@0: } michael@0: return rc; michael@0: } michael@0: michael@0: /* michael@0: ** Read data from a telemetry_file. michael@0: */ michael@0: int michael@0: xRead(sqlite3_file *pFile, void *zBuf, int iAmt, sqlite_int64 iOfst) michael@0: { michael@0: telemetry_file *p = (telemetry_file *)pFile; michael@0: IOThreadAutoTimer ioTimer(p->histograms->readMS, IOInterposeObserver::OpRead); michael@0: int rc; michael@0: rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst); michael@0: // sqlite likes to read from empty files, this is normal, ignore it. michael@0: if (rc != SQLITE_IOERR_SHORT_READ) michael@0: Telemetry::Accumulate(p->histograms->readB, rc == SQLITE_OK ? iAmt : 0); michael@0: return rc; michael@0: } michael@0: michael@0: /* michael@0: ** Write data to a telemetry_file. michael@0: */ michael@0: int michael@0: xWrite(sqlite3_file *pFile, const void *zBuf, int iAmt, sqlite_int64 iOfst) michael@0: { michael@0: telemetry_file *p = (telemetry_file *)pFile; michael@0: if (p->quotaObject && !p->quotaObject->MaybeAllocateMoreSpace(iOfst, iAmt)) { michael@0: return SQLITE_FULL; michael@0: } michael@0: IOThreadAutoTimer ioTimer(p->histograms->writeMS, IOInterposeObserver::OpWrite); michael@0: int rc; michael@0: rc = p->pReal->pMethods->xWrite(p->pReal, zBuf, iAmt, iOfst); michael@0: Telemetry::Accumulate(p->histograms->writeB, rc == SQLITE_OK ? iAmt : 0); michael@0: return rc; michael@0: } michael@0: michael@0: /* michael@0: ** Truncate a telemetry_file. michael@0: */ michael@0: int michael@0: xTruncate(sqlite3_file *pFile, sqlite_int64 size) michael@0: { michael@0: IOThreadAutoTimer ioTimer(Telemetry::MOZ_SQLITE_TRUNCATE_MS); michael@0: telemetry_file *p = (telemetry_file *)pFile; michael@0: int rc; michael@0: Telemetry::AutoTimer timer; michael@0: rc = p->pReal->pMethods->xTruncate(p->pReal, size); michael@0: if (rc == SQLITE_OK && p->quotaObject) { michael@0: p->quotaObject->UpdateSize(size); michael@0: } michael@0: return rc; michael@0: } michael@0: michael@0: /* michael@0: ** Sync a telemetry_file. michael@0: */ michael@0: int michael@0: xSync(sqlite3_file *pFile, int flags) michael@0: { michael@0: telemetry_file *p = (telemetry_file *)pFile; michael@0: IOThreadAutoTimer ioTimer(p->histograms->syncMS, IOInterposeObserver::OpFSync); michael@0: return p->pReal->pMethods->xSync(p->pReal, flags); michael@0: } michael@0: michael@0: /* michael@0: ** Return the current file-size of a telemetry_file. michael@0: */ michael@0: int michael@0: xFileSize(sqlite3_file *pFile, sqlite_int64 *pSize) michael@0: { michael@0: IOThreadAutoTimer ioTimer(IOInterposeObserver::OpStat); michael@0: telemetry_file *p = (telemetry_file *)pFile; michael@0: int rc; michael@0: rc = p->pReal->pMethods->xFileSize(p->pReal, pSize); michael@0: return rc; michael@0: } michael@0: michael@0: /* michael@0: ** Lock a telemetry_file. michael@0: */ michael@0: int michael@0: xLock(sqlite3_file *pFile, int eLock) michael@0: { michael@0: telemetry_file *p = (telemetry_file *)pFile; michael@0: int rc; michael@0: rc = p->pReal->pMethods->xLock(p->pReal, eLock); michael@0: return rc; michael@0: } michael@0: michael@0: /* michael@0: ** Unlock a telemetry_file. michael@0: */ michael@0: int michael@0: xUnlock(sqlite3_file *pFile, int eLock) michael@0: { michael@0: telemetry_file *p = (telemetry_file *)pFile; michael@0: int rc; michael@0: rc = p->pReal->pMethods->xUnlock(p->pReal, eLock); michael@0: return rc; michael@0: } michael@0: michael@0: /* michael@0: ** Check if another file-handle holds a RESERVED lock on a telemetry_file. michael@0: */ michael@0: int michael@0: xCheckReservedLock(sqlite3_file *pFile, int *pResOut) michael@0: { michael@0: telemetry_file *p = (telemetry_file *)pFile; michael@0: int rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut); michael@0: return rc; michael@0: } michael@0: michael@0: /* michael@0: ** File control method. For custom operations on a telemetry_file. michael@0: */ michael@0: int michael@0: xFileControl(sqlite3_file *pFile, int op, void *pArg) michael@0: { michael@0: telemetry_file *p = (telemetry_file *)pFile; michael@0: int rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg); michael@0: return rc; michael@0: } michael@0: michael@0: /* michael@0: ** Return the sector-size in bytes for a telemetry_file. michael@0: */ michael@0: int michael@0: xSectorSize(sqlite3_file *pFile) michael@0: { michael@0: telemetry_file *p = (telemetry_file *)pFile; michael@0: int rc; michael@0: rc = p->pReal->pMethods->xSectorSize(p->pReal); michael@0: return rc; michael@0: } michael@0: michael@0: /* michael@0: ** Return the device characteristic flags supported by a telemetry_file. michael@0: */ michael@0: int michael@0: xDeviceCharacteristics(sqlite3_file *pFile) michael@0: { michael@0: telemetry_file *p = (telemetry_file *)pFile; michael@0: int rc; michael@0: rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal); michael@0: return rc; michael@0: } michael@0: michael@0: /* michael@0: ** Shared-memory operations. michael@0: */ michael@0: int michael@0: xShmLock(sqlite3_file *pFile, int ofst, int n, int flags) michael@0: { michael@0: telemetry_file *p = (telemetry_file *)pFile; michael@0: return p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags); michael@0: } michael@0: michael@0: int michael@0: xShmMap(sqlite3_file *pFile, int iRegion, int szRegion, int isWrite, void volatile **pp) michael@0: { michael@0: telemetry_file *p = (telemetry_file *)pFile; michael@0: int rc; michael@0: rc = p->pReal->pMethods->xShmMap(p->pReal, iRegion, szRegion, isWrite, pp); michael@0: return rc; michael@0: } michael@0: michael@0: void michael@0: xShmBarrier(sqlite3_file *pFile){ michael@0: telemetry_file *p = (telemetry_file *)pFile; michael@0: p->pReal->pMethods->xShmBarrier(p->pReal); michael@0: } michael@0: michael@0: int michael@0: xShmUnmap(sqlite3_file *pFile, int delFlag){ michael@0: telemetry_file *p = (telemetry_file *)pFile; michael@0: int rc; michael@0: rc = p->pReal->pMethods->xShmUnmap(p->pReal, delFlag); michael@0: return rc; michael@0: } michael@0: michael@0: int michael@0: xFetch(sqlite3_file *pFile, sqlite3_int64 iOff, int iAmt, void **pp) michael@0: { michael@0: telemetry_file *p = (telemetry_file *)pFile; michael@0: MOZ_ASSERT(p->pReal->pMethods->iVersion >= 3); michael@0: return p->pReal->pMethods->xFetch(p->pReal, iOff, iAmt, pp); michael@0: } michael@0: michael@0: int michael@0: xUnfetch(sqlite3_file *pFile, sqlite3_int64 iOff, void *pResOut) michael@0: { michael@0: telemetry_file *p = (telemetry_file *)pFile; michael@0: MOZ_ASSERT(p->pReal->pMethods->iVersion >= 3); michael@0: return p->pReal->pMethods->xUnfetch(p->pReal, iOff, pResOut); michael@0: } michael@0: michael@0: int michael@0: xOpen(sqlite3_vfs* vfs, const char *zName, sqlite3_file* pFile, michael@0: int flags, int *pOutFlags) michael@0: { michael@0: IOThreadAutoTimer ioTimer(Telemetry::MOZ_SQLITE_OPEN_MS, michael@0: IOInterposeObserver::OpCreateOrOpen); michael@0: Telemetry::AutoTimer timer; michael@0: sqlite3_vfs *orig_vfs = static_cast(vfs->pAppData); michael@0: int rc; michael@0: telemetry_file *p = (telemetry_file *)pFile; michael@0: Histograms *h = nullptr; michael@0: // check if the filename is one we are probing for michael@0: for(size_t i = 0;i < sizeof(gHistograms)/sizeof(gHistograms[0]);i++) { michael@0: h = &gHistograms[i]; michael@0: // last probe is the fallback probe michael@0: if (!h->name) michael@0: break; michael@0: if (!zName) michael@0: continue; michael@0: const char *match = strstr(zName, h->name); michael@0: if (!match) michael@0: continue; michael@0: char c = match[strlen(h->name)]; michael@0: // include -wal/-journal too michael@0: if (!c || c == '-') michael@0: break; michael@0: } michael@0: p->histograms = h; michael@0: michael@0: const char* persistenceType; michael@0: const char* group; michael@0: const char* origin; michael@0: if ((flags & SQLITE_OPEN_URI) && michael@0: (persistenceType = sqlite3_uri_parameter(zName, "persistenceType")) && michael@0: (group = sqlite3_uri_parameter(zName, "group")) && michael@0: (origin = sqlite3_uri_parameter(zName, "origin"))) { michael@0: QuotaManager* quotaManager = QuotaManager::Get(); michael@0: MOZ_ASSERT(quotaManager); michael@0: michael@0: p->quotaObject = quotaManager->GetQuotaObject(PersistenceTypeFromText( michael@0: nsDependentCString(persistenceType)), nsDependentCString(group), michael@0: nsDependentCString(origin), NS_ConvertUTF8toUTF16(zName)); michael@0: } michael@0: michael@0: rc = orig_vfs->xOpen(orig_vfs, zName, p->pReal, flags, pOutFlags); michael@0: if( rc != SQLITE_OK ) michael@0: return rc; michael@0: if( p->pReal->pMethods ){ michael@0: sqlite3_io_methods *pNew = new sqlite3_io_methods; michael@0: const sqlite3_io_methods *pSub = p->pReal->pMethods; michael@0: memset(pNew, 0, sizeof(*pNew)); michael@0: // If the io_methods version is higher than the last known one, you should michael@0: // update this VFS adding appropriate IO methods for any methods added in michael@0: // the version change. michael@0: pNew->iVersion = pSub->iVersion; michael@0: MOZ_ASSERT(pNew->iVersion <= LAST_KNOWN_IOMETHODS_VERSION); michael@0: pNew->xClose = xClose; michael@0: pNew->xRead = xRead; michael@0: pNew->xWrite = xWrite; michael@0: pNew->xTruncate = xTruncate; michael@0: pNew->xSync = xSync; michael@0: pNew->xFileSize = xFileSize; michael@0: pNew->xLock = xLock; michael@0: pNew->xUnlock = xUnlock; michael@0: pNew->xCheckReservedLock = xCheckReservedLock; michael@0: pNew->xFileControl = xFileControl; michael@0: pNew->xSectorSize = xSectorSize; michael@0: pNew->xDeviceCharacteristics = xDeviceCharacteristics; michael@0: if (pNew->iVersion >= 2) { michael@0: // Methods added in version 2. michael@0: pNew->xShmMap = pSub->xShmMap ? xShmMap : 0; michael@0: pNew->xShmLock = pSub->xShmLock ? xShmLock : 0; michael@0: pNew->xShmBarrier = pSub->xShmBarrier ? xShmBarrier : 0; michael@0: pNew->xShmUnmap = pSub->xShmUnmap ? xShmUnmap : 0; michael@0: } michael@0: if (pNew->iVersion >= 3) { michael@0: // Methods added in version 3. michael@0: // SQLite 3.7.17 calls these methods without checking for nullptr first, michael@0: // so we always define them. Verify that we're not going to call michael@0: // nullptrs, though. michael@0: MOZ_ASSERT(pSub->xFetch); michael@0: pNew->xFetch = xFetch; michael@0: MOZ_ASSERT(pSub->xUnfetch); michael@0: pNew->xUnfetch = xUnfetch; michael@0: } michael@0: pFile->pMethods = pNew; michael@0: } michael@0: return rc; michael@0: } michael@0: michael@0: int michael@0: xDelete(sqlite3_vfs* vfs, const char *zName, int syncDir) michael@0: { michael@0: sqlite3_vfs *orig_vfs = static_cast(vfs->pAppData); michael@0: return orig_vfs->xDelete(orig_vfs, zName, syncDir); michael@0: } michael@0: michael@0: int michael@0: xAccess(sqlite3_vfs *vfs, const char *zName, int flags, int *pResOut) michael@0: { michael@0: sqlite3_vfs *orig_vfs = static_cast(vfs->pAppData); michael@0: return orig_vfs->xAccess(orig_vfs, zName, flags, pResOut); michael@0: } michael@0: michael@0: int michael@0: xFullPathname(sqlite3_vfs *vfs, const char *zName, int nOut, char *zOut) michael@0: { michael@0: sqlite3_vfs *orig_vfs = static_cast(vfs->pAppData); michael@0: return orig_vfs->xFullPathname(orig_vfs, zName, nOut, zOut); michael@0: } michael@0: michael@0: void* michael@0: xDlOpen(sqlite3_vfs *vfs, const char *zFilename) michael@0: { michael@0: sqlite3_vfs *orig_vfs = static_cast(vfs->pAppData); michael@0: return orig_vfs->xDlOpen(orig_vfs, zFilename); michael@0: } michael@0: michael@0: void michael@0: xDlError(sqlite3_vfs *vfs, int nByte, char *zErrMsg) michael@0: { michael@0: sqlite3_vfs *orig_vfs = static_cast(vfs->pAppData); michael@0: orig_vfs->xDlError(orig_vfs, nByte, zErrMsg); michael@0: } michael@0: michael@0: void michael@0: (*xDlSym(sqlite3_vfs *vfs, void *pHdle, const char *zSym))(void){ michael@0: sqlite3_vfs *orig_vfs = static_cast(vfs->pAppData); michael@0: return orig_vfs->xDlSym(orig_vfs, pHdle, zSym); michael@0: } michael@0: michael@0: void michael@0: xDlClose(sqlite3_vfs *vfs, void *pHandle) michael@0: { michael@0: sqlite3_vfs *orig_vfs = static_cast(vfs->pAppData); michael@0: orig_vfs->xDlClose(orig_vfs, pHandle); michael@0: } michael@0: michael@0: int michael@0: xRandomness(sqlite3_vfs *vfs, int nByte, char *zOut) michael@0: { michael@0: sqlite3_vfs *orig_vfs = static_cast(vfs->pAppData); michael@0: return orig_vfs->xRandomness(orig_vfs, nByte, zOut); michael@0: } michael@0: michael@0: int michael@0: xSleep(sqlite3_vfs *vfs, int microseconds) michael@0: { michael@0: sqlite3_vfs *orig_vfs = static_cast(vfs->pAppData); michael@0: return orig_vfs->xSleep(orig_vfs, microseconds); michael@0: } michael@0: michael@0: int michael@0: xCurrentTime(sqlite3_vfs *vfs, double *prNow) michael@0: { michael@0: sqlite3_vfs *orig_vfs = static_cast(vfs->pAppData); michael@0: return orig_vfs->xCurrentTime(orig_vfs, prNow); michael@0: } michael@0: michael@0: int michael@0: xGetLastError(sqlite3_vfs *vfs, int nBuf, char *zBuf) michael@0: { michael@0: sqlite3_vfs *orig_vfs = static_cast(vfs->pAppData); michael@0: return orig_vfs->xGetLastError(orig_vfs, nBuf, zBuf); michael@0: } michael@0: michael@0: int michael@0: xCurrentTimeInt64(sqlite3_vfs *vfs, sqlite3_int64 *piNow) michael@0: { michael@0: sqlite3_vfs *orig_vfs = static_cast(vfs->pAppData); michael@0: return orig_vfs->xCurrentTimeInt64(orig_vfs, piNow); michael@0: } michael@0: michael@0: static michael@0: int michael@0: xSetSystemCall(sqlite3_vfs *vfs, const char *zName, sqlite3_syscall_ptr pFunc) michael@0: { michael@0: sqlite3_vfs *orig_vfs = static_cast(vfs->pAppData); michael@0: return orig_vfs->xSetSystemCall(orig_vfs, zName, pFunc); michael@0: } michael@0: michael@0: static michael@0: sqlite3_syscall_ptr michael@0: xGetSystemCall(sqlite3_vfs *vfs, const char *zName) michael@0: { michael@0: sqlite3_vfs *orig_vfs = static_cast(vfs->pAppData); michael@0: return orig_vfs->xGetSystemCall(orig_vfs, zName); michael@0: } michael@0: michael@0: static michael@0: const char * michael@0: xNextSystemCall(sqlite3_vfs *vfs, const char *zName) michael@0: { michael@0: sqlite3_vfs *orig_vfs = static_cast(vfs->pAppData); michael@0: return orig_vfs->xNextSystemCall(orig_vfs, zName); michael@0: } michael@0: michael@0: } michael@0: michael@0: namespace mozilla { michael@0: namespace storage { michael@0: michael@0: sqlite3_vfs* ConstructTelemetryVFS() michael@0: { michael@0: #if defined(XP_WIN) michael@0: #define EXPECTED_VFS "win32" michael@0: #define EXPECTED_VFS_NFS "win32" michael@0: #else michael@0: #define EXPECTED_VFS "unix" michael@0: #define EXPECTED_VFS_NFS "unix-excl" michael@0: #endif michael@0: michael@0: bool expected_vfs; michael@0: sqlite3_vfs *vfs; michael@0: if (Preferences::GetBool(PREF_NFS_FILESYSTEM)) { michael@0: vfs = sqlite3_vfs_find(EXPECTED_VFS_NFS); michael@0: expected_vfs = (vfs != nullptr); michael@0: } michael@0: else { michael@0: vfs = sqlite3_vfs_find(nullptr); michael@0: expected_vfs = vfs->zName && !strcmp(vfs->zName, EXPECTED_VFS); michael@0: } michael@0: if (!expected_vfs) { michael@0: return nullptr; michael@0: } michael@0: michael@0: sqlite3_vfs *tvfs = new ::sqlite3_vfs; michael@0: memset(tvfs, 0, sizeof(::sqlite3_vfs)); michael@0: // If the VFS version is higher than the last known one, you should update michael@0: // this VFS adding appropriate methods for any methods added in the version michael@0: // change. michael@0: tvfs->iVersion = vfs->iVersion; michael@0: MOZ_ASSERT(vfs->iVersion <= LAST_KNOWN_VFS_VERSION); michael@0: tvfs->szOsFile = sizeof(telemetry_file) - sizeof(sqlite3_file) + vfs->szOsFile; michael@0: tvfs->mxPathname = vfs->mxPathname; michael@0: tvfs->zName = "telemetry-vfs"; michael@0: tvfs->pAppData = vfs; michael@0: tvfs->xOpen = xOpen; michael@0: tvfs->xDelete = xDelete; michael@0: tvfs->xAccess = xAccess; michael@0: tvfs->xFullPathname = xFullPathname; michael@0: tvfs->xDlOpen = xDlOpen; michael@0: tvfs->xDlError = xDlError; michael@0: tvfs->xDlSym = xDlSym; michael@0: tvfs->xDlClose = xDlClose; michael@0: tvfs->xRandomness = xRandomness; michael@0: tvfs->xSleep = xSleep; michael@0: tvfs->xCurrentTime = xCurrentTime; michael@0: tvfs->xGetLastError = xGetLastError; michael@0: if (tvfs->iVersion >= 2) { michael@0: // Methods added in version 2. michael@0: tvfs->xCurrentTimeInt64 = xCurrentTimeInt64; michael@0: } michael@0: if (tvfs->iVersion >= 3) { michael@0: // Methods added in version 3. michael@0: tvfs->xSetSystemCall = xSetSystemCall; michael@0: tvfs->xGetSystemCall = xGetSystemCall; michael@0: tvfs->xNextSystemCall = xNextSystemCall; michael@0: } michael@0: return tvfs; michael@0: } michael@0: michael@0: } michael@0: }