LCOV - code coverage report
Current view: top level - storage - TelemetryVFS.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 239 369 64.8 %
Date: 2017-07-14 16:53:18 Functions: 25 42 59.5 %
Legend: Lines: hit not hit

          Line data    Source code
       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 <string.h>
       8             : #include "mozilla/Telemetry.h"
       9             : #include "mozilla/Preferences.h"
      10             : #include "sqlite3.h"
      11             : #include "nsThreadUtils.h"
      12             : #include "mozilla/dom/quota/PersistenceType.h"
      13             : #include "mozilla/dom/quota/QuotaManager.h"
      14             : #include "mozilla/dom/quota/QuotaObject.h"
      15             : #include "mozilla/IOInterposer.h"
      16             : 
      17             : // The last VFS version for which this file has been updated.
      18             : #define LAST_KNOWN_VFS_VERSION 3
      19             : 
      20             : // The last io_methods version for which this file has been updated.
      21             : #define LAST_KNOWN_IOMETHODS_VERSION 3
      22             : 
      23             : /**
      24             :  * This preference is a workaround to allow users/sysadmins to identify
      25             :  * that the profile exists on an NFS share whose implementation
      26             :  * is incompatible with SQLite's default locking implementation.
      27             :  * Bug 433129 attempted to automatically identify such file-systems,
      28             :  * but a reliable way was not found and it was determined that the fallback
      29             :  * locking is slower than POSIX locking, so we do not want to do it by default.
      30             : */
      31             : #define PREF_NFS_FILESYSTEM   "storage.nfs_filesystem"
      32             : 
      33             : namespace {
      34             : 
      35             : using namespace mozilla;
      36             : using namespace mozilla::dom::quota;
      37             : 
      38             : struct Histograms {
      39             :   const char *name;
      40             :   const Telemetry::HistogramID readB;
      41             :   const Telemetry::HistogramID writeB;
      42             :   const Telemetry::HistogramID readMS;
      43             :   const Telemetry::HistogramID writeMS;
      44             :   const Telemetry::HistogramID syncMS;
      45             : };
      46             : 
      47             : #define SQLITE_TELEMETRY(FILENAME, HGRAM) \
      48             :   { FILENAME, \
      49             :     Telemetry::MOZ_SQLITE_ ## HGRAM ## _READ_B, \
      50             :     Telemetry::MOZ_SQLITE_ ## HGRAM ## _WRITE_B, \
      51             :     Telemetry::MOZ_SQLITE_ ## HGRAM ## _READ_MS, \
      52             :     Telemetry::MOZ_SQLITE_ ## HGRAM ## _WRITE_MS, \
      53             :     Telemetry::MOZ_SQLITE_ ## HGRAM ## _SYNC_MS \
      54             :   }
      55             : 
      56             : Histograms gHistograms[] = {
      57             :   SQLITE_TELEMETRY("places.sqlite", PLACES),
      58             :   SQLITE_TELEMETRY("cookies.sqlite", COOKIES),
      59             :   SQLITE_TELEMETRY("webappsstore.sqlite", WEBAPPS),
      60             :   SQLITE_TELEMETRY(nullptr, OTHER)
      61             : };
      62             : #undef SQLITE_TELEMETRY
      63             : 
      64             : /** RAII class for measuring how long io takes on/off main thread
      65             :  */
      66             : class IOThreadAutoTimer {
      67             : public:
      68             :   /**
      69             :    * IOThreadAutoTimer measures time spent in IO. Additionally it
      70             :    * automatically determines whether IO is happening on the main
      71             :    * thread and picks an appropriate histogram.
      72             :    *
      73             :    * @param id takes a telemetry histogram id. The id+1 must be an
      74             :    * equivalent histogram for the main thread. Eg, MOZ_SQLITE_OPEN_MS
      75             :    * is followed by MOZ_SQLITE_OPEN_MAIN_THREAD_MS.
      76             :    *
      77             :    * @param aOp optionally takes an IO operation to report through the
      78             :    * IOInterposer. Filename will be reported as NULL, and reference will be
      79             :    * either "sqlite-mainthread" or "sqlite-otherthread".
      80             :    */
      81         111 :   explicit IOThreadAutoTimer(Telemetry::HistogramID aId,
      82             :     IOInterposeObserver::Operation aOp = IOInterposeObserver::OpNone)
      83         111 :     : start(TimeStamp::Now()),
      84             :       id(aId)
      85             : #if defined(MOZ_GECKO_PROFILER) && !defined(XP_WIN)
      86         111 :       , op(aOp)
      87             : #endif
      88             :   {
      89         111 :   }
      90             : 
      91             :   /**
      92             :    * This constructor is for when we want to report an operation to
      93             :    * IOInterposer but do not require a telemetry probe.
      94             :    *
      95             :    * @param aOp IO Operation to report through the IOInterposer.
      96             :    */
      97          72 :   explicit IOThreadAutoTimer(IOInterposeObserver::Operation aOp)
      98          72 :     : start(TimeStamp::Now()),
      99             :       id(Telemetry::HistogramCount)
     100             : #if defined(MOZ_GECKO_PROFILER) && !defined(XP_WIN)
     101          72 :       , op(aOp)
     102             : #endif
     103             :   {
     104          72 :   }
     105             : 
     106         183 :   ~IOThreadAutoTimer()
     107         183 :   {
     108         183 :     TimeStamp end(TimeStamp::Now());
     109         183 :     uint32_t mainThread = NS_IsMainThread() ? 1 : 0;
     110         183 :     if (id != Telemetry::HistogramCount) {
     111         111 :       Telemetry::AccumulateTimeDelta(static_cast<Telemetry::HistogramID>(id + mainThread),
     112         111 :                                      start, end);
     113             :     }
     114             :     // We don't report SQLite I/O on Windows because we have a comprehensive
     115             :     // mechanism for intercepting I/O on that platform that captures a superset
     116             :     // of the data captured here.
     117             : #if defined(MOZ_GECKO_PROFILER) && !defined(XP_WIN)
     118         183 :     if (IOInterposer::IsObservedOperation(op)) {
     119         183 :       const char* main_ref  = "sqlite-mainthread";
     120         183 :       const char* other_ref = "sqlite-otherthread";
     121             : 
     122             :       // Create observation
     123             :       IOInterposeObserver::Observation ob(op, start, end,
     124         366 :                                           (mainThread ? main_ref : other_ref));
     125             :       // Report observation
     126         183 :       IOInterposer::Report(ob);
     127             :     }
     128             : #endif /* defined(MOZ_GECKO_PROFILER) && !defined(XP_WIN) */
     129         183 :   }
     130             : 
     131             : private:
     132             :   const TimeStamp start;
     133             :   const Telemetry::HistogramID id;
     134             : #if defined(MOZ_GECKO_PROFILER) && !defined(XP_WIN)
     135             :   IOInterposeObserver::Operation op;
     136             : #endif
     137             : };
     138             : 
     139             : struct telemetry_file {
     140             :   // Base class.  Must be first
     141             :   sqlite3_file base;
     142             : 
     143             :   // histograms pertaining to this file
     144             :   Histograms *histograms;
     145             : 
     146             :   // quota object for this file
     147             :   RefPtr<QuotaObject> quotaObject;
     148             : 
     149             :   // The chunk size for this file. See the documentation for
     150             :   // sqlite3_file_control() and FCNTL_CHUNK_SIZE.
     151             :   int fileChunkSize;
     152             : 
     153             :   // This contains the vfs that actually does work
     154             :   sqlite3_file pReal[1];
     155             : };
     156             : 
     157             : const char*
     158           8 : DatabasePathFromWALPath(const char *zWALName)
     159             : {
     160             :   /**
     161             :    * Do some sketchy pointer arithmetic to find the parameter key. The WAL
     162             :    * filename is in the middle of a big allocated block that contains:
     163             :    *
     164             :    *   - Random Values
     165             :    *   - Main Database Path
     166             :    *   - \0
     167             :    *   - Multiple URI components consisting of:
     168             :    *     - Key
     169             :    *     - \0
     170             :    *     - Value
     171             :    *     - \0
     172             :    *   - \0
     173             :    *   - Journal Path
     174             :    *   - \0
     175             :    *   - WAL Path (zWALName)
     176             :    *   - \0
     177             :    *
     178             :    * Because the main database path is preceded by a random value we have to be
     179             :    * careful when trying to figure out when we should terminate this loop.
     180             :    */
     181           8 :   MOZ_ASSERT(zWALName);
     182             : 
     183          16 :   nsDependentCSubstring dbPath(zWALName, strlen(zWALName));
     184             : 
     185             :   // Chop off the "-wal" suffix.
     186           8 :   NS_NAMED_LITERAL_CSTRING(kWALSuffix, "-wal");
     187           8 :   MOZ_ASSERT(StringEndsWith(dbPath, kWALSuffix));
     188             : 
     189           8 :   dbPath.Rebind(zWALName, dbPath.Length() - kWALSuffix.Length());
     190           8 :   MOZ_ASSERT(!dbPath.IsEmpty());
     191             : 
     192             :   // We want to scan to the end of the key/value URI pairs. Skip the preceding
     193             :   // null and go to the last char of the journal path.
     194           8 :   const char* cursor = zWALName - 2;
     195             : 
     196             :   // Make sure we just skipped a null.
     197           8 :   MOZ_ASSERT(!*(cursor + 1));
     198             : 
     199             :   // Walk backwards over the journal path.
     200         748 :   while (*cursor) {
     201         370 :     cursor--;
     202             :   }
     203             : 
     204             :   // There should be another null here.
     205           8 :   cursor--;
     206           8 :   MOZ_ASSERT(!*cursor);
     207             : 
     208             :   // Back up one more char to the last char of the previous string. It may be
     209             :   // the database path or it may be a key/value URI pair.
     210           8 :   cursor--;
     211             : 
     212             : #ifdef DEBUG
     213             :   {
     214             :     // Verify that we just walked over the journal path. Account for the two
     215             :     // nulls we just skipped.
     216           8 :     const char *journalStart = cursor + 3;
     217             : 
     218             :     nsDependentCSubstring journalPath(journalStart,
     219          16 :                                       strlen(journalStart));
     220             : 
     221             :     // Chop off the "-journal" suffix.
     222           8 :     NS_NAMED_LITERAL_CSTRING(kJournalSuffix, "-journal");
     223           8 :     MOZ_ASSERT(StringEndsWith(journalPath, kJournalSuffix));
     224             : 
     225           8 :     journalPath.Rebind(journalStart,
     226          16 :                         journalPath.Length() - kJournalSuffix.Length());
     227           8 :     MOZ_ASSERT(!journalPath.IsEmpty());
     228             : 
     229             :     // Make sure that the database name is a substring of the journal name.
     230           8 :     MOZ_ASSERT(journalPath == dbPath);
     231             :   }
     232             : #endif
     233             : 
     234             :   // Now we're either at the end of the key/value URI pairs or we're at the
     235             :   // end of the database path. Carefully walk backwards one character at a
     236             :   // time to do this safely without running past the beginning of the database
     237             :   // path.
     238           8 :   const char *const dbPathStart = dbPath.BeginReading();
     239           8 :   const char *dbPathCursor = dbPath.EndReading() - 1;
     240           8 :   bool isDBPath = true;
     241             : 
     242             :   while (true) {
     243         306 :     MOZ_ASSERT(*dbPathCursor, "dbPathCursor should never see a null char!");
     244             : 
     245         306 :     if (isDBPath) {
     246         612 :       isDBPath = dbPathStart <= dbPathCursor &&
     247         612 :                  *dbPathCursor == *cursor &&
     248         306 :                  *cursor;
     249             :     }
     250             : 
     251         306 :     if (!isDBPath) {
     252             :       // This isn't the database path so it must be a value. Scan past it and
     253             :       // the key also.
     254           0 :       for (size_t stringCount = 0; stringCount < 2; stringCount++) {
     255             :         // Scan past the string to the preceding null character.
     256           0 :         while (*cursor) {
     257           0 :           cursor--;
     258             :         }
     259             : 
     260             :         // Back up one more char to the last char of preceding string.
     261           0 :         cursor--;
     262             :       }
     263             : 
     264             :       // Reset and start again.
     265           0 :       dbPathCursor = dbPath.EndReading() - 1;
     266           0 :       isDBPath = true;
     267             : 
     268           0 :       continue;
     269             :     }
     270             : 
     271         306 :     MOZ_ASSERT(isDBPath);
     272         306 :     MOZ_ASSERT(*cursor);
     273             : 
     274         306 :     if (dbPathStart == dbPathCursor) {
     275             :       // Found the full database path, we're all done.
     276           8 :       MOZ_ASSERT(nsDependentCString(cursor) == dbPath);
     277          16 :       return cursor;
     278             :     }
     279             : 
     280             :     // Change the cursors and go through the loop again.
     281         298 :     cursor--;
     282         298 :     dbPathCursor--;
     283         298 :   }
     284             : 
     285             :   MOZ_CRASH("Should never get here!");
     286             : }
     287             : 
     288             : already_AddRefed<QuotaObject>
     289           8 : GetQuotaObjectFromNameAndParameters(const char *zName,
     290             :                                     const char *zURIParameterKey)
     291             : {
     292           8 :   MOZ_ASSERT(zName);
     293           8 :   MOZ_ASSERT(zURIParameterKey);
     294             : 
     295             :   const char *persistenceType =
     296           8 :     sqlite3_uri_parameter(zURIParameterKey, "persistenceType");
     297           8 :   if (!persistenceType) {
     298           8 :     return nullptr;
     299             :   }
     300             : 
     301           0 :   const char *group = sqlite3_uri_parameter(zURIParameterKey, "group");
     302           0 :   if (!group) {
     303           0 :     NS_WARNING("SQLite URI had 'persistenceType' but not 'group'?!");
     304           0 :     return nullptr;
     305             :   }
     306             : 
     307           0 :   const char *origin = sqlite3_uri_parameter(zURIParameterKey, "origin");
     308           0 :   if (!origin) {
     309             :     NS_WARNING("SQLite URI had 'persistenceType' and 'group' but not "
     310           0 :                "'origin'?!");
     311           0 :     return nullptr;
     312             :   }
     313             : 
     314           0 :   QuotaManager *quotaManager = QuotaManager::Get();
     315           0 :   MOZ_ASSERT(quotaManager);
     316             : 
     317             :   return quotaManager->GetQuotaObject(
     318           0 :     PersistenceTypeFromText(nsDependentCString(persistenceType)),
     319           0 :     nsDependentCString(group),
     320           0 :     nsDependentCString(origin),
     321           0 :     NS_ConvertUTF8toUTF16(zName));
     322             : }
     323             : 
     324             : void
     325          18 : MaybeEstablishQuotaControl(const char *zName,
     326             :                            telemetry_file *pFile,
     327             :                            int flags)
     328             : {
     329          18 :   MOZ_ASSERT(pFile);
     330          18 :   MOZ_ASSERT(!pFile->quotaObject);
     331             : 
     332          18 :   if (!(flags & (SQLITE_OPEN_URI | SQLITE_OPEN_WAL))) {
     333          10 :     return;
     334             :   }
     335             : 
     336           8 :   MOZ_ASSERT(zName);
     337             : 
     338           8 :   const char *zURIParameterKey = (flags & SQLITE_OPEN_WAL) ?
     339             :                                  DatabasePathFromWALPath(zName) :
     340           8 :                                  zName;
     341             : 
     342           8 :   MOZ_ASSERT(zURIParameterKey);
     343             : 
     344             :   pFile->quotaObject =
     345           8 :     GetQuotaObjectFromNameAndParameters(zName, zURIParameterKey);
     346             : }
     347             : 
     348             : /*
     349             : ** Close a telemetry_file.
     350             : */
     351             : int
     352           2 : xClose(sqlite3_file *pFile)
     353             : {
     354           2 :   telemetry_file *p = (telemetry_file *)pFile;
     355             :   int rc;
     356             :   { // Scope for IOThreadAutoTimer
     357           4 :     IOThreadAutoTimer ioTimer(IOInterposeObserver::OpClose);
     358           2 :     rc = p->pReal->pMethods->xClose(p->pReal);
     359             :   }
     360           2 :   if( rc==SQLITE_OK ){
     361           2 :     delete p->base.pMethods;
     362           2 :     p->base.pMethods = nullptr;
     363           2 :     p->quotaObject = nullptr;
     364             : #ifdef DEBUG
     365           2 :     p->fileChunkSize = 0;
     366             : #endif
     367             :   }
     368           2 :   return rc;
     369             : }
     370             : 
     371             : /*
     372             : ** Read data from a telemetry_file.
     373             : */
     374             : int
     375          65 : xRead(sqlite3_file *pFile, void *zBuf, int iAmt, sqlite_int64 iOfst)
     376             : {
     377          65 :   telemetry_file *p = (telemetry_file *)pFile;
     378         130 :   IOThreadAutoTimer ioTimer(p->histograms->readMS, IOInterposeObserver::OpRead);
     379             :   int rc;
     380          65 :   rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst);
     381             :   // sqlite likes to read from empty files, this is normal, ignore it.
     382          65 :   if (rc != SQLITE_IOERR_SHORT_READ)
     383          65 :     Telemetry::Accumulate(p->histograms->readB, rc == SQLITE_OK ? iAmt : 0);
     384         130 :   return rc;
     385             : }
     386             : 
     387             : /*
     388             : ** Return the current file-size of a telemetry_file.
     389             : */
     390             : int
     391          70 : xFileSize(sqlite3_file *pFile, sqlite_int64 *pSize)
     392             : {
     393         140 :   IOThreadAutoTimer ioTimer(IOInterposeObserver::OpStat);
     394          70 :   telemetry_file *p = (telemetry_file *)pFile;
     395             :   int rc;
     396          70 :   rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);
     397         140 :   return rc;
     398             : }
     399             : 
     400             : /*
     401             : ** Write data to a telemetry_file.
     402             : */
     403             : int
     404          27 : xWrite(sqlite3_file *pFile, const void *zBuf, int iAmt, sqlite_int64 iOfst)
     405             : {
     406          27 :   telemetry_file *p = (telemetry_file *)pFile;
     407          54 :   IOThreadAutoTimer ioTimer(p->histograms->writeMS, IOInterposeObserver::OpWrite);
     408             :   int rc;
     409          27 :   if (p->quotaObject) {
     410           0 :     MOZ_ASSERT(INT64_MAX - iOfst >= iAmt);
     411           0 :     if (!p->quotaObject->MaybeUpdateSize(iOfst + iAmt, /* aTruncate */ false)) {
     412           0 :       return SQLITE_FULL;
     413             :     }
     414             :   }
     415          27 :   rc = p->pReal->pMethods->xWrite(p->pReal, zBuf, iAmt, iOfst);
     416          27 :   Telemetry::Accumulate(p->histograms->writeB, rc == SQLITE_OK ? iAmt : 0);
     417          27 :   if (p->quotaObject && rc != SQLITE_OK) {
     418             :     NS_WARNING("xWrite failed on a quota-controlled file, attempting to "
     419           0 :                "update its current size...");
     420             :     sqlite_int64 currentSize;
     421           0 :     if (xFileSize(pFile, &currentSize) == SQLITE_OK) {
     422           0 :       p->quotaObject->MaybeUpdateSize(currentSize, /* aTruncate */ true);
     423             :     }
     424             :   }
     425          27 :   return rc;
     426             : }
     427             : 
     428             : /*
     429             : ** Truncate a telemetry_file.
     430             : */
     431             : int
     432           0 : xTruncate(sqlite3_file *pFile, sqlite_int64 size)
     433             : {
     434           0 :   IOThreadAutoTimer ioTimer(Telemetry::MOZ_SQLITE_TRUNCATE_MS);
     435           0 :   telemetry_file *p = (telemetry_file *)pFile;
     436             :   int rc;
     437           0 :   Telemetry::AutoTimer<Telemetry::MOZ_SQLITE_TRUNCATE_MS> timer;
     438           0 :   if (p->quotaObject) {
     439           0 :     if (p->fileChunkSize > 0) {
     440             :       // Round up to the smallest multiple of the chunk size that will hold all
     441             :       // the data.
     442           0 :       size =
     443           0 :         ((size + p->fileChunkSize - 1) / p->fileChunkSize) * p->fileChunkSize;
     444             :     }
     445           0 :     if (!p->quotaObject->MaybeUpdateSize(size, /* aTruncate */ true)) {
     446           0 :       return SQLITE_FULL;
     447             :     }
     448             :   }
     449           0 :   rc = p->pReal->pMethods->xTruncate(p->pReal, size);
     450           0 :   if (p->quotaObject) {
     451           0 :     if (rc == SQLITE_OK) {
     452             : #ifdef DEBUG
     453             :       // Make sure xTruncate set the size exactly as we calculated above.
     454             :       sqlite_int64 newSize;
     455           0 :       MOZ_ASSERT(xFileSize(pFile, &newSize) == SQLITE_OK);
     456           0 :       MOZ_ASSERT(newSize == size);
     457             : #endif
     458             :     } else {
     459             :       NS_WARNING("xTruncate failed on a quota-controlled file, attempting to "
     460           0 :                  "update its current size...");
     461           0 :       if (xFileSize(pFile, &size) == SQLITE_OK) {
     462           0 :         p->quotaObject->MaybeUpdateSize(size, /* aTruncate */ true);
     463             :       }
     464             :     }
     465             :   }
     466           0 :   return rc;
     467             : }
     468             : 
     469             : /*
     470             : ** Sync a telemetry_file.
     471             : */
     472             : int
     473           1 : xSync(sqlite3_file *pFile, int flags)
     474             : {
     475           1 :   telemetry_file *p = (telemetry_file *)pFile;
     476           2 :   IOThreadAutoTimer ioTimer(p->histograms->syncMS, IOInterposeObserver::OpFSync);
     477           2 :   return p->pReal->pMethods->xSync(p->pReal, flags);
     478             : }
     479             : 
     480             : /*
     481             : ** Lock a telemetry_file.
     482             : */
     483             : int
     484          21 : xLock(sqlite3_file *pFile, int eLock)
     485             : {
     486          21 :   telemetry_file *p = (telemetry_file *)pFile;
     487             :   int rc;
     488          21 :   rc = p->pReal->pMethods->xLock(p->pReal, eLock);
     489          21 :   return rc;
     490             : }
     491             : 
     492             : /*
     493             : ** Unlock a telemetry_file.
     494             : */
     495             : int
     496          12 : xUnlock(sqlite3_file *pFile, int eLock)
     497             : {
     498          12 :   telemetry_file *p = (telemetry_file *)pFile;
     499             :   int rc;
     500          12 :   rc = p->pReal->pMethods->xUnlock(p->pReal, eLock);
     501          12 :   return rc;
     502             : }
     503             : 
     504             : /*
     505             : ** Check if another file-handle holds a RESERVED lock on a telemetry_file.
     506             : */
     507             : int
     508           0 : xCheckReservedLock(sqlite3_file *pFile, int *pResOut)
     509             : {
     510           0 :   telemetry_file *p = (telemetry_file *)pFile;
     511           0 :   int rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut);
     512           0 :   return rc;
     513             : }
     514             : 
     515             : /*
     516             : ** File control method. For custom operations on a telemetry_file.
     517             : */
     518             : int
     519         123 : xFileControl(sqlite3_file *pFile, int op, void *pArg)
     520             : {
     521         123 :   telemetry_file *p = (telemetry_file *)pFile;
     522             :   int rc;
     523             :   // Hook SQLITE_FCNTL_SIZE_HINT for quota-controlled files and do the necessary
     524             :   // work before passing to the SQLite VFS.
     525         123 :   if (op == SQLITE_FCNTL_SIZE_HINT && p->quotaObject) {
     526           0 :     sqlite3_int64 hintSize = *static_cast<sqlite3_int64*>(pArg);
     527             :     sqlite3_int64 currentSize;
     528           0 :     rc = xFileSize(pFile, &currentSize);
     529           0 :     if (rc != SQLITE_OK) {
     530           0 :       return rc;
     531             :     }
     532           0 :     if (hintSize > currentSize) {
     533           0 :       rc = xTruncate(pFile, hintSize);
     534           0 :       if (rc != SQLITE_OK) {
     535           0 :         return rc;
     536             :       }
     537             :     }
     538             :   }
     539         123 :   rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
     540             :   // Grab the file chunk size after the SQLite VFS has approved.
     541         123 :   if (op == SQLITE_FCNTL_CHUNK_SIZE && rc == SQLITE_OK) {
     542           2 :     p->fileChunkSize = *static_cast<int*>(pArg);
     543             :   }
     544             : #ifdef DEBUG
     545         123 :   if (op == SQLITE_FCNTL_SIZE_HINT && p->quotaObject && rc == SQLITE_OK) {
     546           0 :     sqlite3_int64 hintSize = *static_cast<sqlite3_int64*>(pArg);
     547           0 :     if (p->fileChunkSize > 0) {
     548           0 :       hintSize =
     549           0 :         ((hintSize + p->fileChunkSize - 1) / p->fileChunkSize) *
     550           0 :           p->fileChunkSize;
     551             :     }
     552             :     sqlite3_int64 currentSize;
     553           0 :     MOZ_ASSERT(xFileSize(pFile, &currentSize) == SQLITE_OK);
     554           0 :     MOZ_ASSERT(currentSize >= hintSize);
     555             :   }
     556             : #endif
     557         123 :   return rc;
     558             : }
     559             : 
     560             : /*
     561             : ** Return the sector-size in bytes for a telemetry_file.
     562             : */
     563             : int
     564           0 : xSectorSize(sqlite3_file *pFile)
     565             : {
     566           0 :   telemetry_file *p = (telemetry_file *)pFile;
     567             :   int rc;
     568           0 :   rc = p->pReal->pMethods->xSectorSize(p->pReal);
     569           0 :   return rc;
     570             : }
     571             : 
     572             : /*
     573             : ** Return the device characteristic flags supported by a telemetry_file.
     574             : */
     575             : int
     576          46 : xDeviceCharacteristics(sqlite3_file *pFile)
     577             : {
     578          46 :   telemetry_file *p = (telemetry_file *)pFile;
     579             :   int rc;
     580          46 :   rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal);
     581          46 :   return rc;
     582             : }
     583             : 
     584             : /*
     585             : ** Shared-memory operations.
     586             : */
     587             : int
     588          88 : xShmLock(sqlite3_file *pFile, int ofst, int n, int flags)
     589             : {
     590          88 :   telemetry_file *p = (telemetry_file *)pFile;
     591          88 :   return p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags);
     592             : }
     593             : 
     594             : int
     595          12 : xShmMap(sqlite3_file *pFile, int iRegion, int szRegion, int isWrite, void volatile **pp)
     596             : {
     597          12 :   telemetry_file *p = (telemetry_file *)pFile;
     598             :   int rc;
     599          12 :   rc = p->pReal->pMethods->xShmMap(p->pReal, iRegion, szRegion, isWrite, pp);
     600          12 :   return rc;
     601             : }
     602             : 
     603             : void
     604          66 : xShmBarrier(sqlite3_file *pFile){
     605          66 :   telemetry_file *p = (telemetry_file *)pFile;
     606          66 :   p->pReal->pMethods->xShmBarrier(p->pReal);
     607          66 : }
     608             : 
     609             : int
     610           1 : xShmUnmap(sqlite3_file *pFile, int delFlag){
     611           1 :   telemetry_file *p = (telemetry_file *)pFile;
     612             :   int rc;
     613           1 :   rc = p->pReal->pMethods->xShmUnmap(p->pReal, delFlag);
     614           1 :   return rc;
     615             : }
     616             : 
     617             : int
     618           0 : xFetch(sqlite3_file *pFile, sqlite3_int64 iOff, int iAmt, void **pp)
     619             : {
     620           0 :   telemetry_file *p = (telemetry_file *)pFile;
     621           0 :   MOZ_ASSERT(p->pReal->pMethods->iVersion >= 3);
     622           0 :   return p->pReal->pMethods->xFetch(p->pReal, iOff, iAmt, pp);
     623             : }
     624             : 
     625             : int
     626           0 : xUnfetch(sqlite3_file *pFile, sqlite3_int64 iOff, void *pResOut)
     627             : {
     628           0 :   telemetry_file *p = (telemetry_file *)pFile;
     629           0 :   MOZ_ASSERT(p->pReal->pMethods->iVersion >= 3);
     630           0 :   return p->pReal->pMethods->xUnfetch(p->pReal, iOff, pResOut);
     631             : }
     632             : 
     633             : int
     634          18 : xOpen(sqlite3_vfs* vfs, const char *zName, sqlite3_file* pFile,
     635             :           int flags, int *pOutFlags)
     636             : {
     637             :   IOThreadAutoTimer ioTimer(Telemetry::MOZ_SQLITE_OPEN_MS,
     638          36 :                             IOInterposeObserver::OpCreateOrOpen);
     639          36 :   Telemetry::AutoTimer<Telemetry::MOZ_SQLITE_OPEN_MS> timer;
     640          18 :   sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
     641             :   int rc;
     642          18 :   telemetry_file *p = (telemetry_file *)pFile;
     643          18 :   Histograms *h = nullptr;
     644             :   // check if the filename is one we are probing for
     645          48 :   for(size_t i = 0;i < sizeof(gHistograms)/sizeof(gHistograms[0]);i++) {
     646          48 :     h = &gHistograms[i];
     647             :     // last probe is the fallback probe
     648          48 :     if (!h->name)
     649           6 :       break;
     650          42 :     if (!zName)
     651           0 :       continue;
     652          42 :     const char *match = strstr(zName, h->name);
     653          42 :     if (!match)
     654          30 :       continue;
     655          12 :     char c = match[strlen(h->name)];
     656             :     // include -wal/-journal too
     657          12 :     if (!c || c == '-')
     658             :       break;
     659             :   }
     660          18 :   p->histograms = h;
     661             : 
     662          18 :   MaybeEstablishQuotaControl(zName, p, flags);
     663             : 
     664          18 :   rc = orig_vfs->xOpen(orig_vfs, zName, p->pReal, flags, pOutFlags);
     665          18 :   if( rc != SQLITE_OK )
     666           0 :     return rc;
     667          18 :   if( p->pReal->pMethods ){
     668          18 :     sqlite3_io_methods *pNew = new sqlite3_io_methods;
     669          18 :     const sqlite3_io_methods *pSub = p->pReal->pMethods;
     670          18 :     memset(pNew, 0, sizeof(*pNew));
     671             :     // If the io_methods version is higher than the last known one, you should
     672             :     // update this VFS adding appropriate IO methods for any methods added in
     673             :     // the version change.
     674          18 :     pNew->iVersion = pSub->iVersion;
     675          18 :     MOZ_ASSERT(pNew->iVersion <= LAST_KNOWN_IOMETHODS_VERSION);
     676          18 :     pNew->xClose = xClose;
     677          18 :     pNew->xRead = xRead;
     678          18 :     pNew->xWrite = xWrite;
     679          18 :     pNew->xTruncate = xTruncate;
     680          18 :     pNew->xSync = xSync;
     681          18 :     pNew->xFileSize = xFileSize;
     682          18 :     pNew->xLock = xLock;
     683          18 :     pNew->xUnlock = xUnlock;
     684          18 :     pNew->xCheckReservedLock = xCheckReservedLock;
     685          18 :     pNew->xFileControl = xFileControl;
     686          18 :     pNew->xSectorSize = xSectorSize;
     687          18 :     pNew->xDeviceCharacteristics = xDeviceCharacteristics;
     688          18 :     if (pNew->iVersion >= 2) {
     689             :       // Methods added in version 2.
     690          18 :       pNew->xShmMap = pSub->xShmMap ? xShmMap : 0;
     691          18 :       pNew->xShmLock = pSub->xShmLock ? xShmLock : 0;
     692          18 :       pNew->xShmBarrier = pSub->xShmBarrier ? xShmBarrier : 0;
     693          18 :       pNew->xShmUnmap = pSub->xShmUnmap ? xShmUnmap : 0;
     694             :     }
     695          18 :     if (pNew->iVersion >= 3) {
     696             :       // Methods added in version 3.
     697             :       // SQLite 3.7.17 calls these methods without checking for nullptr first,
     698             :       // so we always define them.  Verify that we're not going to call
     699             :       // nullptrs, though.
     700          18 :       MOZ_ASSERT(pSub->xFetch);
     701          18 :       pNew->xFetch = xFetch;
     702          18 :       MOZ_ASSERT(pSub->xUnfetch);
     703          18 :       pNew->xUnfetch = xUnfetch;
     704             :     }
     705          18 :     pFile->pMethods = pNew;
     706             :   }
     707          18 :   return rc;
     708             : }
     709             : 
     710             : int
     711           0 : xDelete(sqlite3_vfs* vfs, const char *zName, int syncDir)
     712             : {
     713           0 :   sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
     714             :   int rc;
     715           0 :   RefPtr<QuotaObject> quotaObject;
     716             : 
     717           0 :   if (StringEndsWith(nsDependentCString(zName), NS_LITERAL_CSTRING("-wal"))) {
     718           0 :     const char *zURIParameterKey = DatabasePathFromWALPath(zName);
     719           0 :     MOZ_ASSERT(zURIParameterKey);
     720             : 
     721           0 :     quotaObject = GetQuotaObjectFromNameAndParameters(zName, zURIParameterKey);
     722             :   }
     723             : 
     724           0 :   rc = orig_vfs->xDelete(orig_vfs, zName, syncDir);
     725           0 :   if (rc == SQLITE_OK && quotaObject) {
     726           0 :     MOZ_ALWAYS_TRUE(quotaObject->MaybeUpdateSize(0, /* aTruncate */ true));
     727             :   }
     728             : 
     729           0 :   return rc;
     730             : }
     731             : 
     732             : int
     733          36 : xAccess(sqlite3_vfs *vfs, const char *zName, int flags, int *pResOut)
     734             : {
     735          36 :   sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
     736          36 :   return orig_vfs->xAccess(orig_vfs, zName, flags, pResOut);
     737             : }
     738             : 
     739             : int
     740          12 : xFullPathname(sqlite3_vfs *vfs, const char *zName, int nOut, char *zOut)
     741             : {
     742          12 :   sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
     743          12 :   return orig_vfs->xFullPathname(orig_vfs, zName, nOut, zOut);
     744             : }
     745             : 
     746             : void*
     747           0 : xDlOpen(sqlite3_vfs *vfs, const char *zFilename)
     748             : {
     749           0 :   sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
     750           0 :   return orig_vfs->xDlOpen(orig_vfs, zFilename);
     751             : }
     752             : 
     753             : void
     754           0 : xDlError(sqlite3_vfs *vfs, int nByte, char *zErrMsg)
     755             : {
     756           0 :   sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
     757           0 :   orig_vfs->xDlError(orig_vfs, nByte, zErrMsg);
     758           0 : }
     759             : 
     760             : void
     761           0 : (*xDlSym(sqlite3_vfs *vfs, void *pHdle, const char *zSym))(void){
     762           0 :   sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
     763           0 :   return orig_vfs->xDlSym(orig_vfs, pHdle, zSym);
     764             : }
     765             : 
     766             : void
     767           0 : xDlClose(sqlite3_vfs *vfs, void *pHandle)
     768             : {
     769           0 :   sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
     770           0 :   orig_vfs->xDlClose(orig_vfs, pHandle);
     771           0 : }
     772             : 
     773             : int
     774           1 : xRandomness(sqlite3_vfs *vfs, int nByte, char *zOut)
     775             : {
     776           1 :   sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
     777           1 :   return orig_vfs->xRandomness(orig_vfs, nByte, zOut);
     778             : }
     779             : 
     780             : int
     781           0 : xSleep(sqlite3_vfs *vfs, int microseconds)
     782             : {
     783           0 :   sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
     784           0 :   return orig_vfs->xSleep(orig_vfs, microseconds);
     785             : }
     786             : 
     787             : int
     788           0 : xCurrentTime(sqlite3_vfs *vfs, double *prNow)
     789             : {
     790           0 :   sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
     791           0 :   return orig_vfs->xCurrentTime(orig_vfs, prNow);
     792             : }
     793             : 
     794             : int
     795           0 : xGetLastError(sqlite3_vfs *vfs, int nBuf, char *zBuf)
     796             : {
     797           0 :   sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
     798           0 :   return orig_vfs->xGetLastError(orig_vfs, nBuf, zBuf);
     799             : }
     800             : 
     801             : int
     802           1 : xCurrentTimeInt64(sqlite3_vfs *vfs, sqlite3_int64 *piNow)
     803             : {
     804           1 :   sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
     805           1 :   return orig_vfs->xCurrentTimeInt64(orig_vfs, piNow);
     806             : }
     807             : 
     808             : static
     809             : int
     810           0 : xSetSystemCall(sqlite3_vfs *vfs, const char *zName, sqlite3_syscall_ptr pFunc)
     811             : {
     812           0 :   sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
     813           0 :   return orig_vfs->xSetSystemCall(orig_vfs, zName, pFunc);
     814             : }
     815             : 
     816             : static
     817             : sqlite3_syscall_ptr
     818           0 : xGetSystemCall(sqlite3_vfs *vfs, const char *zName)
     819             : {
     820           0 :   sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
     821           0 :   return orig_vfs->xGetSystemCall(orig_vfs, zName);
     822             : }
     823             : 
     824             : static
     825             : const char *
     826           0 : xNextSystemCall(sqlite3_vfs *vfs, const char *zName)
     827             : {
     828           0 :   sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
     829           0 :   return orig_vfs->xNextSystemCall(orig_vfs, zName);
     830             : }
     831             : 
     832             : } // namespace
     833             : 
     834             : namespace mozilla {
     835             : namespace storage {
     836             : 
     837           1 : sqlite3_vfs* ConstructTelemetryVFS()
     838             : {
     839             : #if defined(XP_WIN)
     840             : #define EXPECTED_VFS     "win32"
     841             : #define EXPECTED_VFS_NFS "win32"
     842             : #else
     843             : #define EXPECTED_VFS     "unix"
     844             : #define EXPECTED_VFS_NFS "unix-excl"
     845             : #endif
     846             : 
     847             :   bool expected_vfs;
     848             :   sqlite3_vfs *vfs;
     849           1 :   if (Preferences::GetBool(PREF_NFS_FILESYSTEM)) {
     850           0 :     vfs = sqlite3_vfs_find(EXPECTED_VFS_NFS);
     851           0 :     expected_vfs = (vfs != nullptr);
     852             :   }
     853             :   else {
     854           1 :     vfs = sqlite3_vfs_find(nullptr);
     855           1 :     expected_vfs = vfs->zName && !strcmp(vfs->zName, EXPECTED_VFS);
     856             :   }
     857           1 :   if (!expected_vfs) {
     858           0 :     return nullptr;
     859             :   }
     860             : 
     861           1 :   sqlite3_vfs *tvfs = new ::sqlite3_vfs;
     862           1 :   memset(tvfs, 0, sizeof(::sqlite3_vfs));
     863             :   // If the VFS version is higher than the last known one, you should update
     864             :   // this VFS adding appropriate methods for any methods added in the version
     865             :   // change.
     866           1 :   tvfs->iVersion = vfs->iVersion;
     867           1 :   MOZ_ASSERT(vfs->iVersion <= LAST_KNOWN_VFS_VERSION);
     868           1 :   tvfs->szOsFile = sizeof(telemetry_file) - sizeof(sqlite3_file) + vfs->szOsFile;
     869           1 :   tvfs->mxPathname = vfs->mxPathname;
     870           1 :   tvfs->zName = "telemetry-vfs";
     871           1 :   tvfs->pAppData = vfs;
     872           1 :   tvfs->xOpen = xOpen;
     873           1 :   tvfs->xDelete = xDelete;
     874           1 :   tvfs->xAccess = xAccess;
     875           1 :   tvfs->xFullPathname = xFullPathname;
     876           1 :   tvfs->xDlOpen = xDlOpen;
     877           1 :   tvfs->xDlError = xDlError;
     878           1 :   tvfs->xDlSym = xDlSym;
     879           1 :   tvfs->xDlClose = xDlClose;
     880           1 :   tvfs->xRandomness = xRandomness;
     881           1 :   tvfs->xSleep = xSleep;
     882           1 :   tvfs->xCurrentTime = xCurrentTime;
     883           1 :   tvfs->xGetLastError = xGetLastError;
     884           1 :   if (tvfs->iVersion >= 2) {
     885             :     // Methods added in version 2.
     886           1 :     tvfs->xCurrentTimeInt64 = xCurrentTimeInt64;
     887             :   }
     888           1 :   if (tvfs->iVersion >= 3) {
     889             :     // Methods added in version 3.
     890           1 :     tvfs->xSetSystemCall = xSetSystemCall;
     891           1 :     tvfs->xGetSystemCall = xGetSystemCall;
     892           1 :     tvfs->xNextSystemCall = xNextSystemCall;
     893             :   }
     894           1 :   return tvfs;
     895             : }
     896             : 
     897             : already_AddRefed<QuotaObject>
     898           0 : GetQuotaObjectForFile(sqlite3_file *pFile)
     899             : {
     900           0 :   MOZ_ASSERT(pFile);
     901             : 
     902           0 :   telemetry_file *p = (telemetry_file *)pFile;
     903           0 :   RefPtr<QuotaObject> result = p->quotaObject;
     904           0 :   return result.forget();
     905             : }
     906             : 
     907             : } // namespace storage
     908             : } // namespace mozilla

Generated by: LCOV version 1.13