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, ¤tSize) == 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, ¤tSize);
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, ¤tSize) == 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
|