Wed, 31 Dec 2014 13:27:57 +0100
Ignore runtime configuration files generated during quality assurance.
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/. */
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"
17 // The last VFS version for which this file has been updated.
18 #define LAST_KNOWN_VFS_VERSION 3
20 // The last io_methods version for which this file has been updated.
21 #define LAST_KNOWN_IOMETHODS_VERSION 3
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"
33 namespace {
35 using namespace mozilla;
36 using namespace mozilla::dom::quota;
38 struct Histograms {
39 const char *name;
40 const Telemetry::ID readB;
41 const Telemetry::ID writeB;
42 const Telemetry::ID readMS;
43 const Telemetry::ID writeMS;
44 const Telemetry::ID syncMS;
45 };
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 }
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
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 IOThreadAutoTimer(Telemetry::ID id,
82 IOInterposeObserver::Operation aOp = IOInterposeObserver::OpNone)
83 : start(TimeStamp::Now()),
84 id(id),
85 op(aOp)
86 {
87 }
89 /**
90 * This constructor is for when we want to report an operation to
91 * IOInterposer but do not require a telemetry probe.
92 *
93 * @param aOp IO Operation to report through the IOInterposer.
94 */
95 IOThreadAutoTimer(IOInterposeObserver::Operation aOp)
96 : start(TimeStamp::Now()),
97 id(Telemetry::HistogramCount),
98 op(aOp)
99 {
100 }
102 ~IOThreadAutoTimer()
103 {
104 TimeStamp end(TimeStamp::Now());
105 uint32_t mainThread = NS_IsMainThread() ? 1 : 0;
106 if (id != Telemetry::HistogramCount) {
107 Telemetry::AccumulateTimeDelta(static_cast<Telemetry::ID>(id + mainThread),
108 start, end);
109 }
110 // We don't report SQLite I/O on Windows because we have a comprehensive
111 // mechanism for intercepting I/O on that platform that captures a superset
112 // of the data captured here.
113 #if defined(MOZ_ENABLE_PROFILER_SPS) && !defined(XP_WIN)
114 if (IOInterposer::IsObservedOperation(op)) {
115 const char* main_ref = "sqlite-mainthread";
116 const char* other_ref = "sqlite-otherthread";
118 // Create observation
119 IOInterposeObserver::Observation ob(op, start, end,
120 (mainThread ? main_ref : other_ref));
121 // Report observation
122 IOInterposer::Report(ob);
123 }
124 #endif /* defined(MOZ_ENABLE_PROFILER_SPS) && !defined(XP_WIN) */
125 }
127 private:
128 const TimeStamp start;
129 const Telemetry::ID id;
130 IOInterposeObserver::Operation op;
131 };
133 struct telemetry_file {
134 // Base class. Must be first
135 sqlite3_file base;
137 // histograms pertaining to this file
138 Histograms *histograms;
140 // quota object for this file
141 nsRefPtr<QuotaObject> quotaObject;
143 // This contains the vfs that actually does work
144 sqlite3_file pReal[1];
145 };
147 /*
148 ** Close a telemetry_file.
149 */
150 int
151 xClose(sqlite3_file *pFile)
152 {
153 telemetry_file *p = (telemetry_file *)pFile;
154 int rc;
155 { // Scope for IOThreadAutoTimer
156 IOThreadAutoTimer ioTimer(IOInterposeObserver::OpClose);
157 rc = p->pReal->pMethods->xClose(p->pReal);
158 }
159 if( rc==SQLITE_OK ){
160 delete p->base.pMethods;
161 p->base.pMethods = nullptr;
162 p->quotaObject = nullptr;
163 }
164 return rc;
165 }
167 /*
168 ** Read data from a telemetry_file.
169 */
170 int
171 xRead(sqlite3_file *pFile, void *zBuf, int iAmt, sqlite_int64 iOfst)
172 {
173 telemetry_file *p = (telemetry_file *)pFile;
174 IOThreadAutoTimer ioTimer(p->histograms->readMS, IOInterposeObserver::OpRead);
175 int rc;
176 rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst);
177 // sqlite likes to read from empty files, this is normal, ignore it.
178 if (rc != SQLITE_IOERR_SHORT_READ)
179 Telemetry::Accumulate(p->histograms->readB, rc == SQLITE_OK ? iAmt : 0);
180 return rc;
181 }
183 /*
184 ** Write data to a telemetry_file.
185 */
186 int
187 xWrite(sqlite3_file *pFile, const void *zBuf, int iAmt, sqlite_int64 iOfst)
188 {
189 telemetry_file *p = (telemetry_file *)pFile;
190 if (p->quotaObject && !p->quotaObject->MaybeAllocateMoreSpace(iOfst, iAmt)) {
191 return SQLITE_FULL;
192 }
193 IOThreadAutoTimer ioTimer(p->histograms->writeMS, IOInterposeObserver::OpWrite);
194 int rc;
195 rc = p->pReal->pMethods->xWrite(p->pReal, zBuf, iAmt, iOfst);
196 Telemetry::Accumulate(p->histograms->writeB, rc == SQLITE_OK ? iAmt : 0);
197 return rc;
198 }
200 /*
201 ** Truncate a telemetry_file.
202 */
203 int
204 xTruncate(sqlite3_file *pFile, sqlite_int64 size)
205 {
206 IOThreadAutoTimer ioTimer(Telemetry::MOZ_SQLITE_TRUNCATE_MS);
207 telemetry_file *p = (telemetry_file *)pFile;
208 int rc;
209 Telemetry::AutoTimer<Telemetry::MOZ_SQLITE_TRUNCATE_MS> timer;
210 rc = p->pReal->pMethods->xTruncate(p->pReal, size);
211 if (rc == SQLITE_OK && p->quotaObject) {
212 p->quotaObject->UpdateSize(size);
213 }
214 return rc;
215 }
217 /*
218 ** Sync a telemetry_file.
219 */
220 int
221 xSync(sqlite3_file *pFile, int flags)
222 {
223 telemetry_file *p = (telemetry_file *)pFile;
224 IOThreadAutoTimer ioTimer(p->histograms->syncMS, IOInterposeObserver::OpFSync);
225 return p->pReal->pMethods->xSync(p->pReal, flags);
226 }
228 /*
229 ** Return the current file-size of a telemetry_file.
230 */
231 int
232 xFileSize(sqlite3_file *pFile, sqlite_int64 *pSize)
233 {
234 IOThreadAutoTimer ioTimer(IOInterposeObserver::OpStat);
235 telemetry_file *p = (telemetry_file *)pFile;
236 int rc;
237 rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);
238 return rc;
239 }
241 /*
242 ** Lock a telemetry_file.
243 */
244 int
245 xLock(sqlite3_file *pFile, int eLock)
246 {
247 telemetry_file *p = (telemetry_file *)pFile;
248 int rc;
249 rc = p->pReal->pMethods->xLock(p->pReal, eLock);
250 return rc;
251 }
253 /*
254 ** Unlock a telemetry_file.
255 */
256 int
257 xUnlock(sqlite3_file *pFile, int eLock)
258 {
259 telemetry_file *p = (telemetry_file *)pFile;
260 int rc;
261 rc = p->pReal->pMethods->xUnlock(p->pReal, eLock);
262 return rc;
263 }
265 /*
266 ** Check if another file-handle holds a RESERVED lock on a telemetry_file.
267 */
268 int
269 xCheckReservedLock(sqlite3_file *pFile, int *pResOut)
270 {
271 telemetry_file *p = (telemetry_file *)pFile;
272 int rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut);
273 return rc;
274 }
276 /*
277 ** File control method. For custom operations on a telemetry_file.
278 */
279 int
280 xFileControl(sqlite3_file *pFile, int op, void *pArg)
281 {
282 telemetry_file *p = (telemetry_file *)pFile;
283 int rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
284 return rc;
285 }
287 /*
288 ** Return the sector-size in bytes for a telemetry_file.
289 */
290 int
291 xSectorSize(sqlite3_file *pFile)
292 {
293 telemetry_file *p = (telemetry_file *)pFile;
294 int rc;
295 rc = p->pReal->pMethods->xSectorSize(p->pReal);
296 return rc;
297 }
299 /*
300 ** Return the device characteristic flags supported by a telemetry_file.
301 */
302 int
303 xDeviceCharacteristics(sqlite3_file *pFile)
304 {
305 telemetry_file *p = (telemetry_file *)pFile;
306 int rc;
307 rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal);
308 return rc;
309 }
311 /*
312 ** Shared-memory operations.
313 */
314 int
315 xShmLock(sqlite3_file *pFile, int ofst, int n, int flags)
316 {
317 telemetry_file *p = (telemetry_file *)pFile;
318 return p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags);
319 }
321 int
322 xShmMap(sqlite3_file *pFile, int iRegion, int szRegion, int isWrite, void volatile **pp)
323 {
324 telemetry_file *p = (telemetry_file *)pFile;
325 int rc;
326 rc = p->pReal->pMethods->xShmMap(p->pReal, iRegion, szRegion, isWrite, pp);
327 return rc;
328 }
330 void
331 xShmBarrier(sqlite3_file *pFile){
332 telemetry_file *p = (telemetry_file *)pFile;
333 p->pReal->pMethods->xShmBarrier(p->pReal);
334 }
336 int
337 xShmUnmap(sqlite3_file *pFile, int delFlag){
338 telemetry_file *p = (telemetry_file *)pFile;
339 int rc;
340 rc = p->pReal->pMethods->xShmUnmap(p->pReal, delFlag);
341 return rc;
342 }
344 int
345 xFetch(sqlite3_file *pFile, sqlite3_int64 iOff, int iAmt, void **pp)
346 {
347 telemetry_file *p = (telemetry_file *)pFile;
348 MOZ_ASSERT(p->pReal->pMethods->iVersion >= 3);
349 return p->pReal->pMethods->xFetch(p->pReal, iOff, iAmt, pp);
350 }
352 int
353 xUnfetch(sqlite3_file *pFile, sqlite3_int64 iOff, void *pResOut)
354 {
355 telemetry_file *p = (telemetry_file *)pFile;
356 MOZ_ASSERT(p->pReal->pMethods->iVersion >= 3);
357 return p->pReal->pMethods->xUnfetch(p->pReal, iOff, pResOut);
358 }
360 int
361 xOpen(sqlite3_vfs* vfs, const char *zName, sqlite3_file* pFile,
362 int flags, int *pOutFlags)
363 {
364 IOThreadAutoTimer ioTimer(Telemetry::MOZ_SQLITE_OPEN_MS,
365 IOInterposeObserver::OpCreateOrOpen);
366 Telemetry::AutoTimer<Telemetry::MOZ_SQLITE_OPEN_MS> timer;
367 sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
368 int rc;
369 telemetry_file *p = (telemetry_file *)pFile;
370 Histograms *h = nullptr;
371 // check if the filename is one we are probing for
372 for(size_t i = 0;i < sizeof(gHistograms)/sizeof(gHistograms[0]);i++) {
373 h = &gHistograms[i];
374 // last probe is the fallback probe
375 if (!h->name)
376 break;
377 if (!zName)
378 continue;
379 const char *match = strstr(zName, h->name);
380 if (!match)
381 continue;
382 char c = match[strlen(h->name)];
383 // include -wal/-journal too
384 if (!c || c == '-')
385 break;
386 }
387 p->histograms = h;
389 const char* persistenceType;
390 const char* group;
391 const char* origin;
392 if ((flags & SQLITE_OPEN_URI) &&
393 (persistenceType = sqlite3_uri_parameter(zName, "persistenceType")) &&
394 (group = sqlite3_uri_parameter(zName, "group")) &&
395 (origin = sqlite3_uri_parameter(zName, "origin"))) {
396 QuotaManager* quotaManager = QuotaManager::Get();
397 MOZ_ASSERT(quotaManager);
399 p->quotaObject = quotaManager->GetQuotaObject(PersistenceTypeFromText(
400 nsDependentCString(persistenceType)), nsDependentCString(group),
401 nsDependentCString(origin), NS_ConvertUTF8toUTF16(zName));
402 }
404 rc = orig_vfs->xOpen(orig_vfs, zName, p->pReal, flags, pOutFlags);
405 if( rc != SQLITE_OK )
406 return rc;
407 if( p->pReal->pMethods ){
408 sqlite3_io_methods *pNew = new sqlite3_io_methods;
409 const sqlite3_io_methods *pSub = p->pReal->pMethods;
410 memset(pNew, 0, sizeof(*pNew));
411 // If the io_methods version is higher than the last known one, you should
412 // update this VFS adding appropriate IO methods for any methods added in
413 // the version change.
414 pNew->iVersion = pSub->iVersion;
415 MOZ_ASSERT(pNew->iVersion <= LAST_KNOWN_IOMETHODS_VERSION);
416 pNew->xClose = xClose;
417 pNew->xRead = xRead;
418 pNew->xWrite = xWrite;
419 pNew->xTruncate = xTruncate;
420 pNew->xSync = xSync;
421 pNew->xFileSize = xFileSize;
422 pNew->xLock = xLock;
423 pNew->xUnlock = xUnlock;
424 pNew->xCheckReservedLock = xCheckReservedLock;
425 pNew->xFileControl = xFileControl;
426 pNew->xSectorSize = xSectorSize;
427 pNew->xDeviceCharacteristics = xDeviceCharacteristics;
428 if (pNew->iVersion >= 2) {
429 // Methods added in version 2.
430 pNew->xShmMap = pSub->xShmMap ? xShmMap : 0;
431 pNew->xShmLock = pSub->xShmLock ? xShmLock : 0;
432 pNew->xShmBarrier = pSub->xShmBarrier ? xShmBarrier : 0;
433 pNew->xShmUnmap = pSub->xShmUnmap ? xShmUnmap : 0;
434 }
435 if (pNew->iVersion >= 3) {
436 // Methods added in version 3.
437 // SQLite 3.7.17 calls these methods without checking for nullptr first,
438 // so we always define them. Verify that we're not going to call
439 // nullptrs, though.
440 MOZ_ASSERT(pSub->xFetch);
441 pNew->xFetch = xFetch;
442 MOZ_ASSERT(pSub->xUnfetch);
443 pNew->xUnfetch = xUnfetch;
444 }
445 pFile->pMethods = pNew;
446 }
447 return rc;
448 }
450 int
451 xDelete(sqlite3_vfs* vfs, const char *zName, int syncDir)
452 {
453 sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
454 return orig_vfs->xDelete(orig_vfs, zName, syncDir);
455 }
457 int
458 xAccess(sqlite3_vfs *vfs, const char *zName, int flags, int *pResOut)
459 {
460 sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
461 return orig_vfs->xAccess(orig_vfs, zName, flags, pResOut);
462 }
464 int
465 xFullPathname(sqlite3_vfs *vfs, const char *zName, int nOut, char *zOut)
466 {
467 sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
468 return orig_vfs->xFullPathname(orig_vfs, zName, nOut, zOut);
469 }
471 void*
472 xDlOpen(sqlite3_vfs *vfs, const char *zFilename)
473 {
474 sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
475 return orig_vfs->xDlOpen(orig_vfs, zFilename);
476 }
478 void
479 xDlError(sqlite3_vfs *vfs, int nByte, char *zErrMsg)
480 {
481 sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
482 orig_vfs->xDlError(orig_vfs, nByte, zErrMsg);
483 }
485 void
486 (*xDlSym(sqlite3_vfs *vfs, void *pHdle, const char *zSym))(void){
487 sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
488 return orig_vfs->xDlSym(orig_vfs, pHdle, zSym);
489 }
491 void
492 xDlClose(sqlite3_vfs *vfs, void *pHandle)
493 {
494 sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
495 orig_vfs->xDlClose(orig_vfs, pHandle);
496 }
498 int
499 xRandomness(sqlite3_vfs *vfs, int nByte, char *zOut)
500 {
501 sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
502 return orig_vfs->xRandomness(orig_vfs, nByte, zOut);
503 }
505 int
506 xSleep(sqlite3_vfs *vfs, int microseconds)
507 {
508 sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
509 return orig_vfs->xSleep(orig_vfs, microseconds);
510 }
512 int
513 xCurrentTime(sqlite3_vfs *vfs, double *prNow)
514 {
515 sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
516 return orig_vfs->xCurrentTime(orig_vfs, prNow);
517 }
519 int
520 xGetLastError(sqlite3_vfs *vfs, int nBuf, char *zBuf)
521 {
522 sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
523 return orig_vfs->xGetLastError(orig_vfs, nBuf, zBuf);
524 }
526 int
527 xCurrentTimeInt64(sqlite3_vfs *vfs, sqlite3_int64 *piNow)
528 {
529 sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
530 return orig_vfs->xCurrentTimeInt64(orig_vfs, piNow);
531 }
533 static
534 int
535 xSetSystemCall(sqlite3_vfs *vfs, const char *zName, sqlite3_syscall_ptr pFunc)
536 {
537 sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
538 return orig_vfs->xSetSystemCall(orig_vfs, zName, pFunc);
539 }
541 static
542 sqlite3_syscall_ptr
543 xGetSystemCall(sqlite3_vfs *vfs, const char *zName)
544 {
545 sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
546 return orig_vfs->xGetSystemCall(orig_vfs, zName);
547 }
549 static
550 const char *
551 xNextSystemCall(sqlite3_vfs *vfs, const char *zName)
552 {
553 sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
554 return orig_vfs->xNextSystemCall(orig_vfs, zName);
555 }
557 }
559 namespace mozilla {
560 namespace storage {
562 sqlite3_vfs* ConstructTelemetryVFS()
563 {
564 #if defined(XP_WIN)
565 #define EXPECTED_VFS "win32"
566 #define EXPECTED_VFS_NFS "win32"
567 #else
568 #define EXPECTED_VFS "unix"
569 #define EXPECTED_VFS_NFS "unix-excl"
570 #endif
572 bool expected_vfs;
573 sqlite3_vfs *vfs;
574 if (Preferences::GetBool(PREF_NFS_FILESYSTEM)) {
575 vfs = sqlite3_vfs_find(EXPECTED_VFS_NFS);
576 expected_vfs = (vfs != nullptr);
577 }
578 else {
579 vfs = sqlite3_vfs_find(nullptr);
580 expected_vfs = vfs->zName && !strcmp(vfs->zName, EXPECTED_VFS);
581 }
582 if (!expected_vfs) {
583 return nullptr;
584 }
586 sqlite3_vfs *tvfs = new ::sqlite3_vfs;
587 memset(tvfs, 0, sizeof(::sqlite3_vfs));
588 // If the VFS version is higher than the last known one, you should update
589 // this VFS adding appropriate methods for any methods added in the version
590 // change.
591 tvfs->iVersion = vfs->iVersion;
592 MOZ_ASSERT(vfs->iVersion <= LAST_KNOWN_VFS_VERSION);
593 tvfs->szOsFile = sizeof(telemetry_file) - sizeof(sqlite3_file) + vfs->szOsFile;
594 tvfs->mxPathname = vfs->mxPathname;
595 tvfs->zName = "telemetry-vfs";
596 tvfs->pAppData = vfs;
597 tvfs->xOpen = xOpen;
598 tvfs->xDelete = xDelete;
599 tvfs->xAccess = xAccess;
600 tvfs->xFullPathname = xFullPathname;
601 tvfs->xDlOpen = xDlOpen;
602 tvfs->xDlError = xDlError;
603 tvfs->xDlSym = xDlSym;
604 tvfs->xDlClose = xDlClose;
605 tvfs->xRandomness = xRandomness;
606 tvfs->xSleep = xSleep;
607 tvfs->xCurrentTime = xCurrentTime;
608 tvfs->xGetLastError = xGetLastError;
609 if (tvfs->iVersion >= 2) {
610 // Methods added in version 2.
611 tvfs->xCurrentTimeInt64 = xCurrentTimeInt64;
612 }
613 if (tvfs->iVersion >= 3) {
614 // Methods added in version 3.
615 tvfs->xSetSystemCall = xSetSystemCall;
616 tvfs->xGetSystemCall = xGetSystemCall;
617 tvfs->xNextSystemCall = xNextSystemCall;
618 }
619 return tvfs;
620 }
622 }
623 }