xref: /sqlite-3.40.0/src/test_vfs.c (revision 7093a3be)
1c7991bdfSdan /*
2c7991bdfSdan ** 2010 May 05
3c7991bdfSdan **
4c7991bdfSdan ** The author disclaims copyright to this source code.  In place of
5c7991bdfSdan ** a legal notice, here is a blessing:
6c7991bdfSdan **
7c7991bdfSdan **    May you do good and not evil.
8c7991bdfSdan **    May you find forgiveness for yourself and forgive others.
9c7991bdfSdan **    May you share freely, never taking more than you give.
10c7991bdfSdan **
11c7991bdfSdan ******************************************************************************
12c7991bdfSdan **
13c8ce3972Sdan ** This file contains the implementation of the Tcl [testvfs] command,
14c8ce3972Sdan ** used to create SQLite VFS implementations with various properties and
15c8ce3972Sdan ** instrumentation to support testing SQLite.
16c8ce3972Sdan **
17c8ce3972Sdan **   testvfs VFSNAME ?OPTIONS?
18c8ce3972Sdan **
19c8ce3972Sdan ** Available options are:
20c8ce3972Sdan **
21c8ce3972Sdan **   -noshm      BOOLEAN        (True to omit shm methods. Default false)
22c8ce3972Sdan **   -default    BOOLEAN        (True to make the vfs default. Default false)
23c8ce3972Sdan **   -szosfile   INTEGER        (Value for sqlite3_vfs.szOsFile)
24c8ce3972Sdan **   -mxpathname INTEGER        (Value for sqlite3_vfs.mxPathname)
258c408004Sdan **   -iversion   INTEGER        (Value for sqlite3_vfs.iVersion)
26c8ce3972Sdan */
27a12b6fa3Sdrh #if SQLITE_TEST          /* This file is used for testing only */
28c8ce3972Sdan 
29c7991bdfSdan #include "sqlite3.h"
30c7991bdfSdan #include "sqliteInt.h"
3152b1dbb5Smistachkin #if defined(INCLUDE_SQLITE_TCL_H)
3252b1dbb5Smistachkin #  include "sqlite_tcl.h"
3352b1dbb5Smistachkin #else
3452b1dbb5Smistachkin #  include "tcl.h"
3552b1dbb5Smistachkin #endif
36c7991bdfSdan 
37c7991bdfSdan typedef struct Testvfs Testvfs;
38c7991bdfSdan typedef struct TestvfsShm TestvfsShm;
39c7991bdfSdan typedef struct TestvfsBuffer TestvfsBuffer;
407fd555a6Sdan typedef struct TestvfsFile TestvfsFile;
41c8ce3972Sdan typedef struct TestvfsFd TestvfsFd;
427fd555a6Sdan 
437fd555a6Sdan /*
447fd555a6Sdan ** An open file handle.
457fd555a6Sdan */
467fd555a6Sdan struct TestvfsFile {
47d9e5c4f6Sdrh   sqlite3_file base;              /* Base class.  Must be first */
48c8ce3972Sdan   TestvfsFd *pFd;                 /* File data */
49c8ce3972Sdan };
50c8ce3972Sdan #define tvfsGetFd(pFile) (((TestvfsFile *)pFile)->pFd)
51c8ce3972Sdan 
52c8ce3972Sdan struct TestvfsFd {
537fd555a6Sdan   sqlite3_vfs *pVfs;              /* The VFS */
547fd555a6Sdan   const char *zFilename;          /* Filename as passed to xOpen() */
55d9e5c4f6Sdrh   sqlite3_file *pReal;            /* The real, underlying file descriptor */
567fd555a6Sdan   Tcl_Obj *pShmId;                /* Shared memory id for Tcl callbacks */
57ef4ee8f2Sdan 
587fd555a6Sdan   TestvfsBuffer *pShm;            /* Shared memory buffer */
59ef4ee8f2Sdan   u32 excllock;                   /* Mask of exclusive locks */
60ef4ee8f2Sdan   u32 sharedlock;                 /* Mask of shared locks */
61c8ce3972Sdan   TestvfsFd *pNext;               /* Next handle opened on the same file */
62d9e5c4f6Sdrh };
63d9e5c4f6Sdrh 
64c8ce3972Sdan 
65f9b4419dSdan #define FAULT_INJECT_NONE       0
66f9b4419dSdan #define FAULT_INJECT_TRANSIENT  1
67f9b4419dSdan #define FAULT_INJECT_PERSISTENT 2
68f9b4419dSdan 
69f9b4419dSdan typedef struct TestFaultInject TestFaultInject;
70f9b4419dSdan struct TestFaultInject {
71f9b4419dSdan   int iCnt;                       /* Remaining calls before fault injection */
72f9b4419dSdan   int eFault;                     /* A FAULT_INJECT_* value */
73f9b4419dSdan   int nFail;                      /* Number of faults injected */
74f9b4419dSdan };
75c7991bdfSdan 
76c7991bdfSdan /*
77c7991bdfSdan ** An instance of this structure is allocated for each VFS created. The
78c7991bdfSdan ** sqlite3_vfs.pAppData field of the VFS structure registered with SQLite
79c7991bdfSdan ** is set to point to it.
80c7991bdfSdan */
81c7991bdfSdan struct Testvfs {
82c7991bdfSdan   char *zName;                    /* Name of this VFS */
83c7991bdfSdan   sqlite3_vfs *pParent;           /* The VFS to use for file IO */
84c7991bdfSdan   sqlite3_vfs *pVfs;              /* The testvfs registered with SQLite */
85c7991bdfSdan   Tcl_Interp *interp;             /* Interpreter to run script in */
861f55e28dSdan   Tcl_Obj *pScript;               /* Script to execute */
87c7991bdfSdan   TestvfsBuffer *pBuffer;         /* List of shared buffers */
887fd555a6Sdan   int isNoshm;
895373b76bSdan   int isFullshm;
900235ab99Sdan 
91f9b4419dSdan   int mask;                       /* Mask controlling [script] and [ioerr] */
92f9b4419dSdan 
93f9b4419dSdan   TestFaultInject ioerr_err;
94f9b4419dSdan   TestFaultInject full_err;
95f9b4419dSdan   TestFaultInject cantopen_err;
96f9b4419dSdan 
97f9b4419dSdan #if 0
980235ab99Sdan   int iIoerrCnt;
990235ab99Sdan   int ioerr;
1001f55e28dSdan   int nIoerrFail;
101146ed78bSdan   int iFullCnt;
102146ed78bSdan   int fullerr;
103146ed78bSdan   int nFullFail;
104f9b4419dSdan #endif
105146ed78bSdan 
1062a321c75Sdan   int iDevchar;
1072a321c75Sdan   int iSectorsize;
108c7991bdfSdan };
109c7991bdfSdan 
110c7991bdfSdan /*
1111f55e28dSdan ** The Testvfs.mask variable is set to a combination of the following.
1121f55e28dSdan ** If a bit is clear in Testvfs.mask, then calls made by SQLite to the
1131f55e28dSdan ** corresponding VFS method is ignored for purposes of:
1141f55e28dSdan **
1151f55e28dSdan **   + Simulating IO errors, and
1161f55e28dSdan **   + Invoking the Tcl callback script.
1171f55e28dSdan */
1181f55e28dSdan #define TESTVFS_SHMOPEN_MASK      0x00000001
1191f55e28dSdan #define TESTVFS_SHMLOCK_MASK      0x00000010
1206b017cc6Sdrh #define TESTVFS_SHMMAP_MASK       0x00000020
1216b017cc6Sdrh #define TESTVFS_SHMBARRIER_MASK   0x00000040
1226b017cc6Sdrh #define TESTVFS_SHMCLOSE_MASK     0x00000080
1231f55e28dSdan 
12413a3cb82Sdan #define TESTVFS_OPEN_MASK         0x00000100
12513a3cb82Sdan #define TESTVFS_SYNC_MASK         0x00000200
126b0ac3e3aSdan #define TESTVFS_DELETE_MASK       0x00000400
1272a321c75Sdan #define TESTVFS_CLOSE_MASK        0x00000800
1282a321c75Sdan #define TESTVFS_WRITE_MASK        0x00001000
1292a321c75Sdan #define TESTVFS_TRUNCATE_MASK     0x00002000
130f9b4419dSdan #define TESTVFS_ACCESS_MASK       0x00004000
1318e98037cSdan #define TESTVFS_FULLPATHNAME_MASK 0x00008000
132262765a7Sdan #define TESTVFS_READ_MASK         0x00010000
133d7a558a9Sdan #define TESTVFS_UNLOCK_MASK       0x00020000
13462e603a9Sdrh #define TESTVFS_LOCK_MASK         0x00040000
13562e603a9Sdrh #define TESTVFS_CKLOCK_MASK       0x00080000
136140a5987Sdan #define TESTVFS_FCNTL_MASK        0x00100000
137262765a7Sdan 
138140a5987Sdan #define TESTVFS_ALL_MASK          0x001FFFFF
13913a3cb82Sdan 
14013a3cb82Sdan 
1410e986f51Sdan #define TESTVFS_MAX_PAGES 1024
1421f55e28dSdan 
1431f55e28dSdan /*
144ef4ee8f2Sdan ** A shared-memory buffer. There is one of these objects for each shared
145ef4ee8f2Sdan ** memory region opened by clients. If two clients open the same file,
146ef4ee8f2Sdan ** there are two TestvfsFile structures but only one TestvfsBuffer structure.
147c7991bdfSdan */
148c7991bdfSdan struct TestvfsBuffer {
149c7991bdfSdan   char *zFile;                    /* Associated file name */
15013a3cb82Sdan   int pgsz;                       /* Page size */
15113a3cb82Sdan   u8 *aPage[TESTVFS_MAX_PAGES];   /* Array of ckalloc'd pages */
152c8ce3972Sdan   TestvfsFd *pFile;               /* List of open handles */
153c7991bdfSdan   TestvfsBuffer *pNext;           /* Next in linked list of all buffers */
154c7991bdfSdan };
155c7991bdfSdan 
156c7991bdfSdan 
157c7991bdfSdan #define PARENTVFS(x) (((Testvfs *)((x)->pAppData))->pParent)
158c7991bdfSdan 
1591f55e28dSdan #define TESTVFS_MAX_ARGS 12
1601f55e28dSdan 
161c7991bdfSdan 
162c7991bdfSdan /*
1637fd555a6Sdan ** Method declarations for TestvfsFile.
164c7991bdfSdan */
165c7991bdfSdan static int tvfsClose(sqlite3_file*);
166c7991bdfSdan static int tvfsRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
167c7991bdfSdan static int tvfsWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
168c7991bdfSdan static int tvfsTruncate(sqlite3_file*, sqlite3_int64 size);
169c7991bdfSdan static int tvfsSync(sqlite3_file*, int flags);
170c7991bdfSdan static int tvfsFileSize(sqlite3_file*, sqlite3_int64 *pSize);
171c7991bdfSdan static int tvfsLock(sqlite3_file*, int);
172c7991bdfSdan static int tvfsUnlock(sqlite3_file*, int);
173c7991bdfSdan static int tvfsCheckReservedLock(sqlite3_file*, int *);
174c7991bdfSdan static int tvfsFileControl(sqlite3_file*, int op, void *pArg);
175c7991bdfSdan static int tvfsSectorSize(sqlite3_file*);
176c7991bdfSdan static int tvfsDeviceCharacteristics(sqlite3_file*);
177c7991bdfSdan 
178c7991bdfSdan /*
179c7991bdfSdan ** Method declarations for tvfs_vfs.
180c7991bdfSdan */
181c7991bdfSdan static int tvfsOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
182c7991bdfSdan static int tvfsDelete(sqlite3_vfs*, const char *zName, int syncDir);
183c7991bdfSdan static int tvfsAccess(sqlite3_vfs*, const char *zName, int flags, int *);
184c7991bdfSdan static int tvfsFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
185c7991bdfSdan #ifndef SQLITE_OMIT_LOAD_EXTENSION
186c7991bdfSdan static void *tvfsDlOpen(sqlite3_vfs*, const char *zFilename);
187c7991bdfSdan static void tvfsDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
188c7991bdfSdan static void (*tvfsDlSym(sqlite3_vfs*,void*, const char *zSymbol))(void);
189c7991bdfSdan static void tvfsDlClose(sqlite3_vfs*, void*);
190c7991bdfSdan #endif /* SQLITE_OMIT_LOAD_EXTENSION */
191c7991bdfSdan static int tvfsRandomness(sqlite3_vfs*, int nByte, char *zOut);
192c7991bdfSdan static int tvfsSleep(sqlite3_vfs*, int microseconds);
193c7991bdfSdan static int tvfsCurrentTime(sqlite3_vfs*, double*);
194c7991bdfSdan 
195d9e5c4f6Sdrh static int tvfsShmOpen(sqlite3_file*);
19673b64e4dSdrh static int tvfsShmLock(sqlite3_file*, int , int, int);
1976b017cc6Sdrh static int tvfsShmMap(sqlite3_file*,int,int,int, void volatile **);
198286a2884Sdrh static void tvfsShmBarrier(sqlite3_file*);
199da9fe0c3Sdan static int tvfsShmUnmap(sqlite3_file*, int);
200c7991bdfSdan 
201789efdb9Sdan static int tvfsFetch(sqlite3_file*, sqlite3_int64, int, void**);
202789efdb9Sdan static int tvfsUnfetch(sqlite3_file*, sqlite3_int64, void*);
203789efdb9Sdan 
204c7991bdfSdan static sqlite3_io_methods tvfs_io_methods = {
205789efdb9Sdan   3,                              /* iVersion */
206c7991bdfSdan   tvfsClose,                      /* xClose */
207c7991bdfSdan   tvfsRead,                       /* xRead */
208c7991bdfSdan   tvfsWrite,                      /* xWrite */
209c7991bdfSdan   tvfsTruncate,                   /* xTruncate */
210c7991bdfSdan   tvfsSync,                       /* xSync */
211c7991bdfSdan   tvfsFileSize,                   /* xFileSize */
212c7991bdfSdan   tvfsLock,                       /* xLock */
213c7991bdfSdan   tvfsUnlock,                     /* xUnlock */
214c7991bdfSdan   tvfsCheckReservedLock,          /* xCheckReservedLock */
215c7991bdfSdan   tvfsFileControl,                /* xFileControl */
216c7991bdfSdan   tvfsSectorSize,                 /* xSectorSize */
217d9e5c4f6Sdrh   tvfsDeviceCharacteristics,      /* xDeviceCharacteristics */
2186b017cc6Sdrh   tvfsShmMap,                     /* xShmMap */
219da9fe0c3Sdan   tvfsShmLock,                    /* xShmLock */
220286a2884Sdrh   tvfsShmBarrier,                 /* xShmBarrier */
221789efdb9Sdan   tvfsShmUnmap,                   /* xShmUnmap */
222789efdb9Sdan   tvfsFetch,
223789efdb9Sdan   tvfsUnfetch
224c7991bdfSdan };
225c7991bdfSdan 
tvfsResultCode(Testvfs * p,int * pRc)226d764c7deSdan static int tvfsResultCode(Testvfs *p, int *pRc){
227d764c7deSdan   struct errcode {
228d764c7deSdan     int eCode;
229d764c7deSdan     const char *zCode;
230d764c7deSdan   } aCode[] = {
231d764c7deSdan     { SQLITE_OK,       "SQLITE_OK"     },
232d764c7deSdan     { SQLITE_ERROR,    "SQLITE_ERROR"  },
233d764c7deSdan     { SQLITE_IOERR,    "SQLITE_IOERR"  },
234d764c7deSdan     { SQLITE_LOCKED,   "SQLITE_LOCKED" },
235d764c7deSdan     { SQLITE_BUSY,     "SQLITE_BUSY"   },
2368d69a581Sdan     { SQLITE_READONLY, "SQLITE_READONLY"   },
23789dec01eSdan     { SQLITE_READONLY_CANTINIT, "SQLITE_READONLY_CANTINIT"   },
23890255b81Sdan     { SQLITE_NOTFOUND, "SQLITE_NOTFOUND"   },
23994c7ab50Sdan     { -1,              "SQLITE_OMIT"   },
240d764c7deSdan   };
241d764c7deSdan 
242d764c7deSdan   const char *z;
243d764c7deSdan   int i;
244d764c7deSdan 
245d764c7deSdan   z = Tcl_GetStringResult(p->interp);
246d764c7deSdan   for(i=0; i<ArraySize(aCode); i++){
247d764c7deSdan     if( 0==strcmp(z, aCode[i].zCode) ){
248d764c7deSdan       *pRc = aCode[i].eCode;
249d764c7deSdan       return 1;
250d764c7deSdan     }
251d764c7deSdan   }
252d764c7deSdan 
253d764c7deSdan   return 0;
254d764c7deSdan }
255d764c7deSdan 
tvfsInjectFault(TestFaultInject * p)256f9b4419dSdan static int tvfsInjectFault(TestFaultInject *p){
257146ed78bSdan   int ret = 0;
258f9b4419dSdan   if( p->eFault ){
259f9b4419dSdan     p->iCnt--;
260f9b4419dSdan     if( p->iCnt==0 || (p->iCnt<0 && p->eFault==FAULT_INJECT_PERSISTENT ) ){
261146ed78bSdan       ret = 1;
262f9b4419dSdan       p->nFail++;
263146ed78bSdan     }
264146ed78bSdan   }
265146ed78bSdan   return ret;
266146ed78bSdan }
267146ed78bSdan 
268f9b4419dSdan 
tvfsInjectIoerr(Testvfs * p)269f9b4419dSdan static int tvfsInjectIoerr(Testvfs *p){
270f9b4419dSdan   return tvfsInjectFault(&p->ioerr_err);
271f9b4419dSdan }
272f9b4419dSdan 
tvfsInjectFullerr(Testvfs * p)273146ed78bSdan static int tvfsInjectFullerr(Testvfs *p){
274f9b4419dSdan   return tvfsInjectFault(&p->full_err);
275146ed78bSdan }
tvfsInjectCantopenerr(Testvfs * p)276f9b4419dSdan static int tvfsInjectCantopenerr(Testvfs *p){
277f9b4419dSdan   return tvfsInjectFault(&p->cantopen_err);
278146ed78bSdan }
279146ed78bSdan 
280d764c7deSdan 
tvfsExecTcl(Testvfs * p,const char * zMethod,Tcl_Obj * arg1,Tcl_Obj * arg2,Tcl_Obj * arg3,Tcl_Obj * arg4)281d764c7deSdan static void tvfsExecTcl(
282d764c7deSdan   Testvfs *p,
283d764c7deSdan   const char *zMethod,
284d764c7deSdan   Tcl_Obj *arg1,
285d764c7deSdan   Tcl_Obj *arg2,
286b88e24fdSdan   Tcl_Obj *arg3,
287b88e24fdSdan   Tcl_Obj *arg4
288d764c7deSdan ){
289d764c7deSdan   int rc;                         /* Return code from Tcl_EvalObj() */
290328876c0Sdan   Tcl_Obj *pEval;
291d764c7deSdan   assert( p->pScript );
292d764c7deSdan 
293328876c0Sdan   assert( zMethod );
294328876c0Sdan   assert( p );
295328876c0Sdan   assert( arg2==0 || arg1!=0 );
296328876c0Sdan   assert( arg3==0 || arg2!=0 );
297d764c7deSdan 
298328876c0Sdan   pEval = Tcl_DuplicateObj(p->pScript);
299328876c0Sdan   Tcl_IncrRefCount(p->pScript);
300328876c0Sdan   Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewStringObj(zMethod, -1));
301328876c0Sdan   if( arg1 ) Tcl_ListObjAppendElement(p->interp, pEval, arg1);
302328876c0Sdan   if( arg2 ) Tcl_ListObjAppendElement(p->interp, pEval, arg2);
303328876c0Sdan   if( arg3 ) Tcl_ListObjAppendElement(p->interp, pEval, arg3);
304b88e24fdSdan   if( arg4 ) Tcl_ListObjAppendElement(p->interp, pEval, arg4);
305d764c7deSdan 
306328876c0Sdan   rc = Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL);
307d764c7deSdan   if( rc!=TCL_OK ){
308d764c7deSdan     Tcl_BackgroundError(p->interp);
309d764c7deSdan     Tcl_ResetResult(p->interp);
310d764c7deSdan   }
311d764c7deSdan }
312d764c7deSdan 
313d764c7deSdan 
314c7991bdfSdan /*
315c7991bdfSdan ** Close an tvfs-file.
316c7991bdfSdan */
tvfsClose(sqlite3_file * pFile)317c7991bdfSdan static int tvfsClose(sqlite3_file *pFile){
318c8ce3972Sdan   TestvfsFile *pTestfile = (TestvfsFile *)pFile;
319c8ce3972Sdan   TestvfsFd *pFd = pTestfile->pFd;
3202a321c75Sdan   Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
3212a321c75Sdan 
3222a321c75Sdan   if( p->pScript && p->mask&TESTVFS_CLOSE_MASK ){
3232a321c75Sdan     tvfsExecTcl(p, "xClose",
324b88e24fdSdan         Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0, 0
3252a321c75Sdan     );
3262a321c75Sdan   }
3272a321c75Sdan 
3282a321c75Sdan   if( pFd->pShmId ){
3292a321c75Sdan     Tcl_DecrRefCount(pFd->pShmId);
3302a321c75Sdan     pFd->pShmId = 0;
3317fd555a6Sdan   }
3327fd555a6Sdan   if( pFile->pMethods ){
3337fd555a6Sdan     ckfree((char *)pFile->pMethods);
3347fd555a6Sdan   }
3358f2ce914Sdrh   sqlite3OsClose(pFd->pReal);
336c8ce3972Sdan   ckfree((char *)pFd);
337c8ce3972Sdan   pTestfile->pFd = 0;
3388f2ce914Sdrh   return SQLITE_OK;
339c7991bdfSdan }
340c7991bdfSdan 
341c7991bdfSdan /*
342c7991bdfSdan ** Read data from an tvfs-file.
343c7991bdfSdan */
tvfsRead(sqlite3_file * pFile,void * zBuf,int iAmt,sqlite_int64 iOfst)344c7991bdfSdan static int tvfsRead(
345c7991bdfSdan   sqlite3_file *pFile,
346c7991bdfSdan   void *zBuf,
347c7991bdfSdan   int iAmt,
348c7991bdfSdan   sqlite_int64 iOfst
349c7991bdfSdan ){
350262765a7Sdan   int rc = SQLITE_OK;
351262765a7Sdan   TestvfsFd *pFd = tvfsGetFd(pFile);
352262765a7Sdan   Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
353262765a7Sdan   if( p->pScript && p->mask&TESTVFS_READ_MASK ){
354262765a7Sdan     tvfsExecTcl(p, "xRead",
355b88e24fdSdan         Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0, 0
356262765a7Sdan     );
357262765a7Sdan     tvfsResultCode(p, &rc);
358262765a7Sdan   }
359262765a7Sdan   if( rc==SQLITE_OK && p->mask&TESTVFS_READ_MASK && tvfsInjectIoerr(p) ){
360262765a7Sdan     rc = SQLITE_IOERR;
361262765a7Sdan   }
362262765a7Sdan   if( rc==SQLITE_OK ){
363262765a7Sdan     rc = sqlite3OsRead(pFd->pReal, zBuf, iAmt, iOfst);
364262765a7Sdan   }
365262765a7Sdan   return rc;
366c7991bdfSdan }
367c7991bdfSdan 
368c7991bdfSdan /*
369c7991bdfSdan ** Write data to an tvfs-file.
370c7991bdfSdan */
tvfsWrite(sqlite3_file * pFile,const void * zBuf,int iAmt,sqlite_int64 iOfst)371c7991bdfSdan static int tvfsWrite(
372c7991bdfSdan   sqlite3_file *pFile,
373c7991bdfSdan   const void *zBuf,
374c7991bdfSdan   int iAmt,
375c7991bdfSdan   sqlite_int64 iOfst
376c7991bdfSdan ){
3772a321c75Sdan   int rc = SQLITE_OK;
378c8ce3972Sdan   TestvfsFd *pFd = tvfsGetFd(pFile);
3792a321c75Sdan   Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
3802a321c75Sdan 
3812a321c75Sdan   if( p->pScript && p->mask&TESTVFS_WRITE_MASK ){
3822a321c75Sdan     tvfsExecTcl(p, "xWrite",
383428c218cSdan         Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId,
384b88e24fdSdan         Tcl_NewWideIntObj(iOfst), Tcl_NewIntObj(iAmt)
3852a321c75Sdan     );
3862a321c75Sdan     tvfsResultCode(p, &rc);
38794c7ab50Sdan     if( rc<0 ) return SQLITE_OK;
3882a321c75Sdan   }
3892a321c75Sdan 
390346e4267Sdan   if( rc==SQLITE_OK && tvfsInjectFullerr(p) ){
391346e4267Sdan     rc = SQLITE_FULL;
392346e4267Sdan   }
393346e4267Sdan   if( rc==SQLITE_OK && p->mask&TESTVFS_WRITE_MASK && tvfsInjectIoerr(p) ){
394346e4267Sdan     rc = SQLITE_IOERR;
395346e4267Sdan   }
396146ed78bSdan 
3972a321c75Sdan   if( rc==SQLITE_OK ){
3982a321c75Sdan     rc = sqlite3OsWrite(pFd->pReal, zBuf, iAmt, iOfst);
3992a321c75Sdan   }
4002a321c75Sdan   return rc;
401c7991bdfSdan }
402c7991bdfSdan 
403c7991bdfSdan /*
404c7991bdfSdan ** Truncate an tvfs-file.
405c7991bdfSdan */
tvfsTruncate(sqlite3_file * pFile,sqlite_int64 size)406c7991bdfSdan static int tvfsTruncate(sqlite3_file *pFile, sqlite_int64 size){
4072a321c75Sdan   int rc = SQLITE_OK;
408c8ce3972Sdan   TestvfsFd *pFd = tvfsGetFd(pFile);
4092a321c75Sdan   Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
4102a321c75Sdan 
4112a321c75Sdan   if( p->pScript && p->mask&TESTVFS_TRUNCATE_MASK ){
4122a321c75Sdan     tvfsExecTcl(p, "xTruncate",
413b88e24fdSdan         Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0, 0
4142a321c75Sdan     );
4152a321c75Sdan     tvfsResultCode(p, &rc);
4162a321c75Sdan   }
4172a321c75Sdan 
4182a321c75Sdan   if( rc==SQLITE_OK ){
4192a321c75Sdan     rc = sqlite3OsTruncate(pFd->pReal, size);
4202a321c75Sdan   }
4212a321c75Sdan   return rc;
422c7991bdfSdan }
423c7991bdfSdan 
424c7991bdfSdan /*
425c7991bdfSdan ** Sync an tvfs-file.
426c7991bdfSdan */
tvfsSync(sqlite3_file * pFile,int flags)427c7991bdfSdan static int tvfsSync(sqlite3_file *pFile, int flags){
428d764c7deSdan   int rc = SQLITE_OK;
429c8ce3972Sdan   TestvfsFd *pFd = tvfsGetFd(pFile);
430d764c7deSdan   Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
431d764c7deSdan 
432d764c7deSdan   if( p->pScript && p->mask&TESTVFS_SYNC_MASK ){
4337bb22ac7Smistachkin     char *zFlags = 0;
434d764c7deSdan 
435d764c7deSdan     switch( flags ){
436d764c7deSdan       case SQLITE_SYNC_NORMAL:
437d764c7deSdan         zFlags = "normal";
438d764c7deSdan         break;
439d764c7deSdan       case SQLITE_SYNC_FULL:
440d764c7deSdan         zFlags = "full";
441d764c7deSdan         break;
442d764c7deSdan       case SQLITE_SYNC_NORMAL|SQLITE_SYNC_DATAONLY:
443d764c7deSdan         zFlags = "normal|dataonly";
444d764c7deSdan         break;
445d764c7deSdan       case SQLITE_SYNC_FULL|SQLITE_SYNC_DATAONLY:
446d764c7deSdan         zFlags = "full|dataonly";
447d764c7deSdan         break;
448d764c7deSdan       default:
449d764c7deSdan         assert(0);
450d764c7deSdan     }
451d764c7deSdan 
452d764c7deSdan     tvfsExecTcl(p, "xSync",
453d764c7deSdan         Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId,
454b88e24fdSdan         Tcl_NewStringObj(zFlags, -1), 0
455d764c7deSdan     );
456d764c7deSdan     tvfsResultCode(p, &rc);
457d764c7deSdan   }
458d764c7deSdan 
459146ed78bSdan   if( rc==SQLITE_OK && tvfsInjectFullerr(p) ) rc = SQLITE_FULL;
460146ed78bSdan 
461d764c7deSdan   if( rc==SQLITE_OK ){
462d764c7deSdan     rc = sqlite3OsSync(pFd->pReal, flags);
463d764c7deSdan   }
464d764c7deSdan 
465d764c7deSdan   return rc;
466c7991bdfSdan }
467c7991bdfSdan 
468c7991bdfSdan /*
469c7991bdfSdan ** Return the current file-size of an tvfs-file.
470c7991bdfSdan */
tvfsFileSize(sqlite3_file * pFile,sqlite_int64 * pSize)471c7991bdfSdan static int tvfsFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
472c8ce3972Sdan   TestvfsFd *p = tvfsGetFd(pFile);
473c7991bdfSdan   return sqlite3OsFileSize(p->pReal, pSize);
474c7991bdfSdan }
475c7991bdfSdan 
476c7991bdfSdan /*
477c7991bdfSdan ** Lock an tvfs-file.
478c7991bdfSdan */
tvfsLock(sqlite3_file * pFile,int eLock)479c7991bdfSdan static int tvfsLock(sqlite3_file *pFile, int eLock){
48062e603a9Sdrh   TestvfsFd *pFd = tvfsGetFd(pFile);
48162e603a9Sdrh   Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
48262e603a9Sdrh   if( p->pScript && p->mask&TESTVFS_LOCK_MASK ){
48362e603a9Sdrh     char zLock[30];
48462e603a9Sdrh     sqlite3_snprintf(sizeof(zLock),zLock,"%d",eLock);
48562e603a9Sdrh     tvfsExecTcl(p, "xLock", Tcl_NewStringObj(pFd->zFilename, -1),
48662e603a9Sdrh                    Tcl_NewStringObj(zLock, -1), 0, 0);
48762e603a9Sdrh   }
48862e603a9Sdrh   return sqlite3OsLock(pFd->pReal, eLock);
489c7991bdfSdan }
490c7991bdfSdan 
491c7991bdfSdan /*
492c7991bdfSdan ** Unlock an tvfs-file.
493c7991bdfSdan */
tvfsUnlock(sqlite3_file * pFile,int eLock)494c7991bdfSdan static int tvfsUnlock(sqlite3_file *pFile, int eLock){
495d7a558a9Sdan   TestvfsFd *pFd = tvfsGetFd(pFile);
496d7a558a9Sdan   Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
49762e603a9Sdrh   if( p->pScript && p->mask&TESTVFS_UNLOCK_MASK ){
49862e603a9Sdrh     char zLock[30];
49962e603a9Sdrh     sqlite3_snprintf(sizeof(zLock),zLock,"%d",eLock);
50062e603a9Sdrh     tvfsExecTcl(p, "xUnlock", Tcl_NewStringObj(pFd->zFilename, -1),
50162e603a9Sdrh                    Tcl_NewStringObj(zLock, -1), 0, 0);
50262e603a9Sdrh   }
503d7a558a9Sdan   if( p->mask&TESTVFS_WRITE_MASK && tvfsInjectIoerr(p) ){
504d7a558a9Sdan     return SQLITE_IOERR_UNLOCK;
505d7a558a9Sdan   }
506d7a558a9Sdan   return sqlite3OsUnlock(pFd->pReal, eLock);
507c7991bdfSdan }
508c7991bdfSdan 
509c7991bdfSdan /*
510c7991bdfSdan ** Check if another file-handle holds a RESERVED lock on an tvfs-file.
511c7991bdfSdan */
tvfsCheckReservedLock(sqlite3_file * pFile,int * pResOut)512c7991bdfSdan static int tvfsCheckReservedLock(sqlite3_file *pFile, int *pResOut){
51362e603a9Sdrh   TestvfsFd *pFd = tvfsGetFd(pFile);
51462e603a9Sdrh   Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
51562e603a9Sdrh   if( p->pScript && p->mask&TESTVFS_CKLOCK_MASK ){
51662e603a9Sdrh     tvfsExecTcl(p, "xCheckReservedLock", Tcl_NewStringObj(pFd->zFilename, -1),
51762e603a9Sdrh                    0, 0, 0);
51862e603a9Sdrh   }
51962e603a9Sdrh   return sqlite3OsCheckReservedLock(pFd->pReal, pResOut);
520c7991bdfSdan }
521c7991bdfSdan 
522c7991bdfSdan /*
523c7991bdfSdan ** File control method. For custom operations on an tvfs-file.
524c7991bdfSdan */
tvfsFileControl(sqlite3_file * pFile,int op,void * pArg)525c7991bdfSdan static int tvfsFileControl(sqlite3_file *pFile, int op, void *pArg){
526140a5987Sdan   TestvfsFd *pFd = tvfsGetFd(pFile);
527140a5987Sdan   Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
52892c700dbSdrh   if( op==SQLITE_FCNTL_PRAGMA ){
52992c700dbSdrh     char **argv = (char**)pArg;
53092c700dbSdrh     if( sqlite3_stricmp(argv[1],"error")==0 ){
53192c700dbSdrh       int rc = SQLITE_ERROR;
53292c700dbSdrh       if( argv[2] ){
53392c700dbSdrh         const char *z = argv[2];
53492c700dbSdrh         int x = atoi(z);
53592c700dbSdrh         if( x ){
53692c700dbSdrh           rc = x;
53792c700dbSdrh           while( sqlite3Isdigit(z[0]) ){ z++; }
53892c700dbSdrh           while( sqlite3Isspace(z[0]) ){ z++; }
53992c700dbSdrh         }
54092c700dbSdrh         if( z[0] ) argv[0] = sqlite3_mprintf("%s", z);
54192c700dbSdrh       }
54292c700dbSdrh       return rc;
54392c700dbSdrh     }
544c8517f61Sdrh     if( sqlite3_stricmp(argv[1], "filename")==0 ){
545140a5987Sdan       argv[0] = sqlite3_mprintf("%s", pFd->zFilename);
546c8517f61Sdrh       return SQLITE_OK;
547c8517f61Sdrh     }
54892c700dbSdrh   }
549140a5987Sdan   if( p->pScript && (p->mask&TESTVFS_FCNTL_MASK) ){
550140a5987Sdan     struct Fcntl {
551140a5987Sdan       int iFnctl;
552140a5987Sdan       const char *zFnctl;
553140a5987Sdan     } aF[] = {
554140a5987Sdan       { SQLITE_FCNTL_BEGIN_ATOMIC_WRITE, "BEGIN_ATOMIC_WRITE" },
555140a5987Sdan       { SQLITE_FCNTL_COMMIT_ATOMIC_WRITE, "COMMIT_ATOMIC_WRITE" },
55690255b81Sdan       { SQLITE_FCNTL_ZIPVFS, "ZIPVFS" },
557140a5987Sdan     };
558140a5987Sdan     int i;
559140a5987Sdan     for(i=0; i<sizeof(aF)/sizeof(aF[0]); i++){
560140a5987Sdan       if( op==aF[i].iFnctl ) break;
561140a5987Sdan     }
562140a5987Sdan     if( i<sizeof(aF)/sizeof(aF[0]) ){
563140a5987Sdan       int rc = 0;
564140a5987Sdan       tvfsExecTcl(p, "xFileControl",
565140a5987Sdan           Tcl_NewStringObj(pFd->zFilename, -1),
566140a5987Sdan           Tcl_NewStringObj(aF[i].zFnctl, -1),
567140a5987Sdan           0, 0
568140a5987Sdan       );
569140a5987Sdan       tvfsResultCode(p, &rc);
57090255b81Sdan       if( rc ) return (rc<0 ? SQLITE_OK : rc);
571140a5987Sdan     }
572140a5987Sdan   }
573140a5987Sdan   return sqlite3OsFileControl(pFd->pReal, op, pArg);
574c7991bdfSdan }
575c7991bdfSdan 
576c7991bdfSdan /*
577c7991bdfSdan ** Return the sector-size in bytes for an tvfs-file.
578c7991bdfSdan */
tvfsSectorSize(sqlite3_file * pFile)579c7991bdfSdan static int tvfsSectorSize(sqlite3_file *pFile){
580c8ce3972Sdan   TestvfsFd *pFd = tvfsGetFd(pFile);
5812a321c75Sdan   Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
5822a321c75Sdan   if( p->iSectorsize>=0 ){
5832a321c75Sdan     return p->iSectorsize;
5842a321c75Sdan   }
5852a321c75Sdan   return sqlite3OsSectorSize(pFd->pReal);
586c7991bdfSdan }
587c7991bdfSdan 
588c7991bdfSdan /*
589c7991bdfSdan ** Return the device characteristic flags supported by an tvfs-file.
590c7991bdfSdan */
tvfsDeviceCharacteristics(sqlite3_file * pFile)591c7991bdfSdan static int tvfsDeviceCharacteristics(sqlite3_file *pFile){
592c8ce3972Sdan   TestvfsFd *pFd = tvfsGetFd(pFile);
5932a321c75Sdan   Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
5942a321c75Sdan   if( p->iDevchar>=0 ){
5952a321c75Sdan     return p->iDevchar;
5962a321c75Sdan   }
5972a321c75Sdan   return sqlite3OsDeviceCharacteristics(pFd->pReal);
598c7991bdfSdan }
599c7991bdfSdan 
600c7991bdfSdan /*
601c7991bdfSdan ** Open an tvfs file handle.
602c7991bdfSdan */
tvfsOpen(sqlite3_vfs * pVfs,const char * zName,sqlite3_file * pFile,int flags,int * pOutFlags)603c7991bdfSdan static int tvfsOpen(
604c7991bdfSdan   sqlite3_vfs *pVfs,
605c7991bdfSdan   const char *zName,
606c7991bdfSdan   sqlite3_file *pFile,
607c7991bdfSdan   int flags,
608c7991bdfSdan   int *pOutFlags
609c7991bdfSdan ){
610c7991bdfSdan   int rc;
611c8ce3972Sdan   TestvfsFile *pTestfile = (TestvfsFile *)pFile;
612c8ce3972Sdan   TestvfsFd *pFd;
613d764c7deSdan   Tcl_Obj *pId = 0;
614d764c7deSdan   Testvfs *p = (Testvfs *)pVfs->pAppData;
615d764c7deSdan 
616c8ce3972Sdan   pFd = (TestvfsFd *)ckalloc(sizeof(TestvfsFd) + PARENTVFS(pVfs)->szOsFile);
617c8ce3972Sdan   memset(pFd, 0, sizeof(TestvfsFd) + PARENTVFS(pVfs)->szOsFile);
618d764c7deSdan   pFd->pShm = 0;
619d764c7deSdan   pFd->pShmId = 0;
620d764c7deSdan   pFd->zFilename = zName;
621d764c7deSdan   pFd->pVfs = pVfs;
622d764c7deSdan   pFd->pReal = (sqlite3_file *)&pFd[1];
62347e909bbSdan   memset(pTestfile, 0, sizeof(TestvfsFile));
624c8ce3972Sdan   pTestfile->pFd = pFd;
625d764c7deSdan 
626d764c7deSdan   /* Evaluate the Tcl script:
627d764c7deSdan   **
628cd74b611Sdan   **   SCRIPT xOpen FILENAME KEY-VALUE-ARGS
629d764c7deSdan   **
630d764c7deSdan   ** If the script returns an SQLite error code other than SQLITE_OK, an
631d764c7deSdan   ** error is returned to the caller. If it returns SQLITE_OK, the new
632d764c7deSdan   ** connection is named "anon". Otherwise, the value returned by the
633d764c7deSdan   ** script is used as the connection name.
634d764c7deSdan   */
635d764c7deSdan   Tcl_ResetResult(p->interp);
636d764c7deSdan   if( p->pScript && p->mask&TESTVFS_OPEN_MASK ){
637cd74b611Sdan     Tcl_Obj *pArg = Tcl_NewObj();
638cd74b611Sdan     Tcl_IncrRefCount(pArg);
639cd74b611Sdan     if( flags&SQLITE_OPEN_MAIN_DB ){
640cd74b611Sdan       const char *z = &zName[strlen(zName)+1];
641cd74b611Sdan       while( *z ){
642cd74b611Sdan         Tcl_ListObjAppendElement(0, pArg, Tcl_NewStringObj(z, -1));
643cd74b611Sdan         z += strlen(z) + 1;
644cd74b611Sdan         Tcl_ListObjAppendElement(0, pArg, Tcl_NewStringObj(z, -1));
645cd74b611Sdan         z += strlen(z) + 1;
646cd74b611Sdan       }
647cd74b611Sdan     }
648b88e24fdSdan     tvfsExecTcl(p, "xOpen", Tcl_NewStringObj(pFd->zFilename, -1), pArg, 0, 0);
649cd74b611Sdan     Tcl_DecrRefCount(pArg);
650d764c7deSdan     if( tvfsResultCode(p, &rc) ){
651d764c7deSdan       if( rc!=SQLITE_OK ) return rc;
652d764c7deSdan     }else{
653d764c7deSdan       pId = Tcl_GetObjResult(p->interp);
654d764c7deSdan     }
655d764c7deSdan   }
656f9b4419dSdan 
657f9b4419dSdan   if( (p->mask&TESTVFS_OPEN_MASK) &&  tvfsInjectIoerr(p) ) return SQLITE_IOERR;
658f9b4419dSdan   if( tvfsInjectCantopenerr(p) ) return SQLITE_CANTOPEN;
659f9b4419dSdan   if( tvfsInjectFullerr(p) ) return SQLITE_FULL;
660f9b4419dSdan 
661d764c7deSdan   if( !pId ){
662d764c7deSdan     pId = Tcl_NewStringObj("anon", -1);
663d764c7deSdan   }
664d764c7deSdan   Tcl_IncrRefCount(pId);
665d764c7deSdan   pFd->pShmId = pId;
666d764c7deSdan   Tcl_ResetResult(p->interp);
667d764c7deSdan 
668d764c7deSdan   rc = sqlite3OsOpen(PARENTVFS(pVfs), zName, pFd->pReal, flags, pOutFlags);
669d764c7deSdan   if( pFd->pReal->pMethods ){
6707fd555a6Sdan     sqlite3_io_methods *pMethods;
67189ccf448Sdan     int nByte;
67289ccf448Sdan 
67389ccf448Sdan     if( pVfs->iVersion>1 ){
67489ccf448Sdan       nByte = sizeof(sqlite3_io_methods);
67589ccf448Sdan     }else{
676da9fe0c3Sdan       nByte = offsetof(sqlite3_io_methods, xShmMap);
67789ccf448Sdan     }
67889ccf448Sdan 
67989ccf448Sdan     pMethods = (sqlite3_io_methods *)ckalloc(nByte);
68089ccf448Sdan     memcpy(pMethods, &tvfs_io_methods, nByte);
681789efdb9Sdan     pMethods->iVersion = pFd->pReal->pMethods->iVersion;
682789efdb9Sdan     if( pMethods->iVersion>pVfs->iVersion ){
68389ccf448Sdan       pMethods->iVersion = pVfs->iVersion;
684789efdb9Sdan     }
68589ccf448Sdan     if( pVfs->iVersion>1 && ((Testvfs *)pVfs->pAppData)->isNoshm ){
686da9fe0c3Sdan       pMethods->xShmUnmap = 0;
6877fd555a6Sdan       pMethods->xShmLock = 0;
688286a2884Sdrh       pMethods->xShmBarrier = 0;
68918801915Sdan       pMethods->xShmMap = 0;
690c7991bdfSdan     }
6917fd555a6Sdan     pFile->pMethods = pMethods;
6927fd555a6Sdan   }
6937fd555a6Sdan 
694c7991bdfSdan   return rc;
695c7991bdfSdan }
696c7991bdfSdan 
697c7991bdfSdan /*
698c7991bdfSdan ** Delete the file located at zPath. If the dirSync argument is true,
699c7991bdfSdan ** ensure the file-system modifications are synced to disk before
700c7991bdfSdan ** returning.
701c7991bdfSdan */
tvfsDelete(sqlite3_vfs * pVfs,const char * zPath,int dirSync)702c7991bdfSdan static int tvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
703b0ac3e3aSdan   int rc = SQLITE_OK;
704b0ac3e3aSdan   Testvfs *p = (Testvfs *)pVfs->pAppData;
705b0ac3e3aSdan 
706b0ac3e3aSdan   if( p->pScript && p->mask&TESTVFS_DELETE_MASK ){
707b0ac3e3aSdan     tvfsExecTcl(p, "xDelete",
708b88e24fdSdan         Tcl_NewStringObj(zPath, -1), Tcl_NewIntObj(dirSync), 0, 0
709b0ac3e3aSdan     );
710b0ac3e3aSdan     tvfsResultCode(p, &rc);
711b0ac3e3aSdan   }
712b0ac3e3aSdan   if( rc==SQLITE_OK ){
713b0ac3e3aSdan     rc = sqlite3OsDelete(PARENTVFS(pVfs), zPath, dirSync);
714b0ac3e3aSdan   }
715b0ac3e3aSdan   return rc;
716c7991bdfSdan }
717c7991bdfSdan 
718c7991bdfSdan /*
719c7991bdfSdan ** Test for access permissions. Return true if the requested permission
720c7991bdfSdan ** is available, or false otherwise.
721c7991bdfSdan */
tvfsAccess(sqlite3_vfs * pVfs,const char * zPath,int flags,int * pResOut)722c7991bdfSdan static int tvfsAccess(
723c7991bdfSdan   sqlite3_vfs *pVfs,
724c7991bdfSdan   const char *zPath,
725c7991bdfSdan   int flags,
726c7991bdfSdan   int *pResOut
727c7991bdfSdan ){
728f9b4419dSdan   Testvfs *p = (Testvfs *)pVfs->pAppData;
729f9b4419dSdan   if( p->pScript && p->mask&TESTVFS_ACCESS_MASK ){
730f9b4419dSdan     int rc;
731f9b4419dSdan     char *zArg = 0;
732f9b4419dSdan     if( flags==SQLITE_ACCESS_EXISTS ) zArg = "SQLITE_ACCESS_EXISTS";
733f9b4419dSdan     if( flags==SQLITE_ACCESS_READWRITE ) zArg = "SQLITE_ACCESS_READWRITE";
734f9b4419dSdan     if( flags==SQLITE_ACCESS_READ ) zArg = "SQLITE_ACCESS_READ";
735f9b4419dSdan     tvfsExecTcl(p, "xAccess",
736b88e24fdSdan         Tcl_NewStringObj(zPath, -1), Tcl_NewStringObj(zArg, -1), 0, 0
737f9b4419dSdan     );
738f9b4419dSdan     if( tvfsResultCode(p, &rc) ){
739f9b4419dSdan       if( rc!=SQLITE_OK ) return rc;
740f9b4419dSdan     }else{
741f9b4419dSdan       Tcl_Interp *interp = p->interp;
742f9b4419dSdan       if( TCL_OK==Tcl_GetBooleanFromObj(0, Tcl_GetObjResult(interp), pResOut) ){
743f9b4419dSdan         return SQLITE_OK;
744f9b4419dSdan       }
745f9b4419dSdan     }
746f9b4419dSdan   }
747c7991bdfSdan   return sqlite3OsAccess(PARENTVFS(pVfs), zPath, flags, pResOut);
748c7991bdfSdan }
749c7991bdfSdan 
750c7991bdfSdan /*
751c7991bdfSdan ** Populate buffer zOut with the full canonical pathname corresponding
752c7991bdfSdan ** to the pathname in zPath. zOut is guaranteed to point to a buffer
753c7991bdfSdan ** of at least (DEVSYM_MAX_PATHNAME+1) bytes.
754c7991bdfSdan */
tvfsFullPathname(sqlite3_vfs * pVfs,const char * zPath,int nOut,char * zOut)755c7991bdfSdan static int tvfsFullPathname(
756c7991bdfSdan   sqlite3_vfs *pVfs,
757c7991bdfSdan   const char *zPath,
758c7991bdfSdan   int nOut,
759c7991bdfSdan   char *zOut
760c7991bdfSdan ){
7618e98037cSdan   Testvfs *p = (Testvfs *)pVfs->pAppData;
7628e98037cSdan   if( p->pScript && p->mask&TESTVFS_FULLPATHNAME_MASK ){
7638e98037cSdan     int rc;
764b88e24fdSdan     tvfsExecTcl(p, "xFullPathname", Tcl_NewStringObj(zPath, -1), 0, 0, 0);
7658e98037cSdan     if( tvfsResultCode(p, &rc) ){
7668e98037cSdan       if( rc!=SQLITE_OK ) return rc;
7678e98037cSdan     }
7688e98037cSdan   }
769c7991bdfSdan   return sqlite3OsFullPathname(PARENTVFS(pVfs), zPath, nOut, zOut);
770c7991bdfSdan }
771c7991bdfSdan 
772c7991bdfSdan #ifndef SQLITE_OMIT_LOAD_EXTENSION
773c7991bdfSdan /*
774c7991bdfSdan ** Open the dynamic library located at zPath and return a handle.
775c7991bdfSdan */
tvfsDlOpen(sqlite3_vfs * pVfs,const char * zPath)776c7991bdfSdan static void *tvfsDlOpen(sqlite3_vfs *pVfs, const char *zPath){
777c7991bdfSdan   return sqlite3OsDlOpen(PARENTVFS(pVfs), zPath);
778c7991bdfSdan }
779c7991bdfSdan 
780c7991bdfSdan /*
781c7991bdfSdan ** Populate the buffer zErrMsg (size nByte bytes) with a human readable
782c7991bdfSdan ** utf-8 string describing the most recent error encountered associated
783c7991bdfSdan ** with dynamic libraries.
784c7991bdfSdan */
tvfsDlError(sqlite3_vfs * pVfs,int nByte,char * zErrMsg)785c7991bdfSdan static void tvfsDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
786c7991bdfSdan   sqlite3OsDlError(PARENTVFS(pVfs), nByte, zErrMsg);
787c7991bdfSdan }
788c7991bdfSdan 
789c7991bdfSdan /*
790c7991bdfSdan ** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
791c7991bdfSdan */
tvfsDlSym(sqlite3_vfs * pVfs,void * p,const char * zSym)792c7991bdfSdan static void (*tvfsDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
793c7991bdfSdan   return sqlite3OsDlSym(PARENTVFS(pVfs), p, zSym);
794c7991bdfSdan }
795c7991bdfSdan 
796c7991bdfSdan /*
797c7991bdfSdan ** Close the dynamic library handle pHandle.
798c7991bdfSdan */
tvfsDlClose(sqlite3_vfs * pVfs,void * pHandle)799c7991bdfSdan static void tvfsDlClose(sqlite3_vfs *pVfs, void *pHandle){
800c7991bdfSdan   sqlite3OsDlClose(PARENTVFS(pVfs), pHandle);
801c7991bdfSdan }
802c7991bdfSdan #endif /* SQLITE_OMIT_LOAD_EXTENSION */
803c7991bdfSdan 
804c7991bdfSdan /*
805c7991bdfSdan ** Populate the buffer pointed to by zBufOut with nByte bytes of
806c7991bdfSdan ** random data.
807c7991bdfSdan */
tvfsRandomness(sqlite3_vfs * pVfs,int nByte,char * zBufOut)808c7991bdfSdan static int tvfsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
809c7991bdfSdan   return sqlite3OsRandomness(PARENTVFS(pVfs), nByte, zBufOut);
810c7991bdfSdan }
811c7991bdfSdan 
812c7991bdfSdan /*
813c7991bdfSdan ** Sleep for nMicro microseconds. Return the number of microseconds
814c7991bdfSdan ** actually slept.
815c7991bdfSdan */
tvfsSleep(sqlite3_vfs * pVfs,int nMicro)816c7991bdfSdan static int tvfsSleep(sqlite3_vfs *pVfs, int nMicro){
817c7991bdfSdan   return sqlite3OsSleep(PARENTVFS(pVfs), nMicro);
818c7991bdfSdan }
819c7991bdfSdan 
820c7991bdfSdan /*
821c7991bdfSdan ** Return the current time as a Julian Day number in *pTimeOut.
822c7991bdfSdan */
tvfsCurrentTime(sqlite3_vfs * pVfs,double * pTimeOut)823c7991bdfSdan static int tvfsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
824c7991bdfSdan   return PARENTVFS(pVfs)->xCurrentTime(PARENTVFS(pVfs), pTimeOut);
825c7991bdfSdan }
826c7991bdfSdan 
tvfsShmOpen(sqlite3_file * pFile)827c8ce3972Sdan static int tvfsShmOpen(sqlite3_file *pFile){
8287fd555a6Sdan   Testvfs *p;
829c7991bdfSdan   int rc = SQLITE_OK;             /* Return code */
830c7991bdfSdan   TestvfsBuffer *pBuffer;         /* Buffer to open connection to */
831c8ce3972Sdan   TestvfsFd *pFd;                 /* The testvfs file structure */
832d9e5c4f6Sdrh 
833c8ce3972Sdan   pFd = tvfsGetFd(pFile);
8347fd555a6Sdan   p = (Testvfs *)pFd->pVfs->pAppData;
8355373b76bSdan   assert( 0==p->isFullshm );
836ef4ee8f2Sdan   assert( pFd->pShmId && pFd->pShm==0 && pFd->pNext==0 );
837c7991bdfSdan 
838c7991bdfSdan   /* Evaluate the Tcl script:
839c7991bdfSdan   **
840c7991bdfSdan   **   SCRIPT xShmOpen FILENAME
841c7991bdfSdan   */
8420235ab99Sdan   Tcl_ResetResult(p->interp);
8431f55e28dSdan   if( p->pScript && p->mask&TESTVFS_SHMOPEN_MASK ){
844b88e24fdSdan     tvfsExecTcl(p, "xShmOpen", Tcl_NewStringObj(pFd->zFilename, -1), 0, 0, 0);
845c7991bdfSdan     if( tvfsResultCode(p, &rc) ){
846c7991bdfSdan       if( rc!=SQLITE_OK ) return rc;
847c7991bdfSdan     }
8481f55e28dSdan   }
8491f55e28dSdan 
8501f55e28dSdan   assert( rc==SQLITE_OK );
8511f55e28dSdan   if( p->mask&TESTVFS_SHMOPEN_MASK && tvfsInjectIoerr(p) ){
8521f55e28dSdan     return SQLITE_IOERR;
8531f55e28dSdan   }
8541f55e28dSdan 
855c7991bdfSdan   /* Search for a TestvfsBuffer. Create a new one if required. */
856c7991bdfSdan   for(pBuffer=p->pBuffer; pBuffer; pBuffer=pBuffer->pNext){
8577fd555a6Sdan     if( 0==strcmp(pFd->zFilename, pBuffer->zFile) ) break;
858c7991bdfSdan   }
859c7991bdfSdan   if( !pBuffer ){
86065545b59Sdrh     int szName = (int)strlen(pFd->zFilename);
86165545b59Sdrh     int nByte = sizeof(TestvfsBuffer) + szName + 1;
862c7991bdfSdan     pBuffer = (TestvfsBuffer *)ckalloc(nByte);
863c7991bdfSdan     memset(pBuffer, 0, nByte);
864c7991bdfSdan     pBuffer->zFile = (char *)&pBuffer[1];
86565545b59Sdrh     memcpy(pBuffer->zFile, pFd->zFilename, szName+1);
866c7991bdfSdan     pBuffer->pNext = p->pBuffer;
867c7991bdfSdan     p->pBuffer = pBuffer;
868c7991bdfSdan   }
869c7991bdfSdan 
870c7991bdfSdan   /* Connect the TestvfsBuffer to the new TestvfsShm handle and return. */
871ef4ee8f2Sdan   pFd->pNext = pBuffer->pFile;
872ef4ee8f2Sdan   pBuffer->pFile = pFd;
8737fd555a6Sdan   pFd->pShm = pBuffer;
8748d69a581Sdan   return rc;
875c7991bdfSdan }
876c7991bdfSdan 
tvfsAllocPage(TestvfsBuffer * p,int iPage,int pgsz)87713a3cb82Sdan static void tvfsAllocPage(TestvfsBuffer *p, int iPage, int pgsz){
87813a3cb82Sdan   assert( iPage<TESTVFS_MAX_PAGES );
87913a3cb82Sdan   if( p->aPage[iPage]==0 ){
8804280eb30Sdan     p->aPage[iPage] = (u8 *)ckalloc(pgsz);
88113a3cb82Sdan     memset(p->aPage[iPage], 0, pgsz);
88213a3cb82Sdan     p->pgsz = pgsz;
88313a3cb82Sdan   }
88413a3cb82Sdan }
88513a3cb82Sdan 
tvfsShmMap(sqlite3_file * pFile,int iPage,int pgsz,int isWrite,void volatile ** pp)8866b017cc6Sdrh static int tvfsShmMap(
88713a3cb82Sdan   sqlite3_file *pFile,            /* Handle open on database file */
88813a3cb82Sdan   int iPage,                      /* Page to retrieve */
88913a3cb82Sdan   int pgsz,                       /* Size of pages */
89013a3cb82Sdan   int isWrite,                    /* True to extend file if necessary */
89113a3cb82Sdan   void volatile **pp              /* OUT: Mapped memory */
89213a3cb82Sdan ){
893c7991bdfSdan   int rc = SQLITE_OK;
894c8ce3972Sdan   TestvfsFd *pFd = tvfsGetFd(pFile);
8957fd555a6Sdan   Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);
896c7991bdfSdan 
8975373b76bSdan   if( p->isFullshm ){
898*7093a3beSdan     sqlite3_file *pReal = pFd->pReal;
899*7093a3beSdan     return pReal->pMethods->xShmMap(pReal, iPage, pgsz, isWrite, pp);
9005373b76bSdan   }
9015373b76bSdan 
902da9fe0c3Sdan   if( 0==pFd->pShm ){
903da9fe0c3Sdan     rc = tvfsShmOpen(pFile);
904da9fe0c3Sdan     if( rc!=SQLITE_OK ){
905da9fe0c3Sdan       return rc;
906da9fe0c3Sdan     }
907da9fe0c3Sdan   }
908da9fe0c3Sdan 
9096b017cc6Sdrh   if( p->pScript && p->mask&TESTVFS_SHMMAP_MASK ){
91013a3cb82Sdan     Tcl_Obj *pArg = Tcl_NewObj();
9115d656852Sdan     Tcl_IncrRefCount(pArg);
91213a3cb82Sdan     Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(iPage));
91313a3cb82Sdan     Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(pgsz));
91413a3cb82Sdan     Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(isWrite));
9156b017cc6Sdrh     tvfsExecTcl(p, "xShmMap",
916b88e24fdSdan         Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, pArg, 0
917c7991bdfSdan     );
918c7991bdfSdan     tvfsResultCode(p, &rc);
9195d656852Sdan     Tcl_DecrRefCount(pArg);
9200235ab99Sdan   }
9216b017cc6Sdrh   if( rc==SQLITE_OK && p->mask&TESTVFS_SHMMAP_MASK && tvfsInjectIoerr(p) ){
9221f55e28dSdan     rc = SQLITE_IOERR;
9231f55e28dSdan   }
924d03f523dSdan 
92513a3cb82Sdan   if( rc==SQLITE_OK && isWrite && !pFd->pShm->aPage[iPage] ){
92613a3cb82Sdan     tvfsAllocPage(pFd->pShm, iPage, pgsz);
927c7991bdfSdan   }
92889dec01eSdan   if( rc==SQLITE_OK || rc==SQLITE_READONLY ){
92913a3cb82Sdan     *pp = (void volatile *)pFd->pShm->aPage[iPage];
93089dec01eSdan   }
931c7991bdfSdan 
932c7991bdfSdan   return rc;
933c7991bdfSdan }
934c7991bdfSdan 
93513a3cb82Sdan 
tvfsShmLock(sqlite3_file * pFile,int ofst,int n,int flags)936c7991bdfSdan static int tvfsShmLock(
9377fd555a6Sdan   sqlite3_file *pFile,
93873b64e4dSdrh   int ofst,
93973b64e4dSdrh   int n,
94073b64e4dSdrh   int flags
941c7991bdfSdan ){
942c7991bdfSdan   int rc = SQLITE_OK;
943c8ce3972Sdan   TestvfsFd *pFd = tvfsGetFd(pFile);
9447fd555a6Sdan   Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);
94573b64e4dSdrh   int nLock;
94673b64e4dSdrh   char zLock[80];
947c7991bdfSdan 
9485373b76bSdan   if( p->isFullshm ){
949*7093a3beSdan     sqlite3_file *pReal = pFd->pReal;
950*7093a3beSdan     return pReal->pMethods->xShmLock(pReal, ofst, n, flags);
9515373b76bSdan   }
9525373b76bSdan 
9531f55e28dSdan   if( p->pScript && p->mask&TESTVFS_SHMLOCK_MASK ){
95473b64e4dSdrh     sqlite3_snprintf(sizeof(zLock), zLock, "%d %d", ofst, n);
95583cc1392Sdrh     nLock = (int)strlen(zLock);
95673b64e4dSdrh     if( flags & SQLITE_SHM_LOCK ){
95773b64e4dSdrh       strcpy(&zLock[nLock], " lock");
95873b64e4dSdrh     }else{
95973b64e4dSdrh       strcpy(&zLock[nLock], " unlock");
96073b64e4dSdrh     }
96183cc1392Sdrh     nLock += (int)strlen(&zLock[nLock]);
96273b64e4dSdrh     if( flags & SQLITE_SHM_SHARED ){
96373b64e4dSdrh       strcpy(&zLock[nLock], " shared");
96473b64e4dSdrh     }else{
96573b64e4dSdrh       strcpy(&zLock[nLock], " exclusive");
966c7991bdfSdan     }
967c7991bdfSdan     tvfsExecTcl(p, "xShmLock",
9687fd555a6Sdan         Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId,
969b88e24fdSdan         Tcl_NewStringObj(zLock, -1), 0
970c7991bdfSdan     );
971c7991bdfSdan     tvfsResultCode(p, &rc);
9720235ab99Sdan   }
973961ff451Sdan 
974961ff451Sdan   if( rc==SQLITE_OK && p->mask&TESTVFS_SHMLOCK_MASK && tvfsInjectIoerr(p) ){
975961ff451Sdan     rc = SQLITE_IOERR;
976961ff451Sdan   }
977ef4ee8f2Sdan 
978ef4ee8f2Sdan   if( rc==SQLITE_OK ){
979ef4ee8f2Sdan     int isLock = (flags & SQLITE_SHM_LOCK);
980ef4ee8f2Sdan     int isExcl = (flags & SQLITE_SHM_EXCLUSIVE);
981ef4ee8f2Sdan     u32 mask = (((1<<n)-1) << ofst);
982ef4ee8f2Sdan     if( isLock ){
983c8ce3972Sdan       TestvfsFd *p2;
984ef4ee8f2Sdan       for(p2=pFd->pShm->pFile; p2; p2=p2->pNext){
985ef4ee8f2Sdan         if( p2==pFd ) continue;
986ef4ee8f2Sdan         if( (p2->excllock&mask) || (isExcl && p2->sharedlock&mask) ){
987ef4ee8f2Sdan           rc = SQLITE_BUSY;
988ef4ee8f2Sdan           break;
989ef4ee8f2Sdan         }
990ef4ee8f2Sdan       }
991ef4ee8f2Sdan       if( rc==SQLITE_OK ){
992ef4ee8f2Sdan         if( isExcl )  pFd->excllock |= mask;
993ef4ee8f2Sdan         if( !isExcl ) pFd->sharedlock |= mask;
994ef4ee8f2Sdan       }
995ef4ee8f2Sdan     }else{
996ef4ee8f2Sdan       if( isExcl )  pFd->excllock &= (~mask);
997ef4ee8f2Sdan       if( !isExcl ) pFd->sharedlock &= (~mask);
998ef4ee8f2Sdan     }
999ef4ee8f2Sdan   }
1000ef4ee8f2Sdan 
1001c7991bdfSdan   return rc;
1002c7991bdfSdan }
1003c7991bdfSdan 
tvfsShmBarrier(sqlite3_file * pFile)1004286a2884Sdrh static void tvfsShmBarrier(sqlite3_file *pFile){
1005c8ce3972Sdan   TestvfsFd *pFd = tvfsGetFd(pFile);
1006286a2884Sdrh   Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);
1007286a2884Sdrh 
1008363fc9e7Sdan   if( p->pScript && p->mask&TESTVFS_SHMBARRIER_MASK ){
1009363fc9e7Sdan     const char *z = pFd->pShm ? pFd->pShm->zFile : "";
1010363fc9e7Sdan     tvfsExecTcl(p, "xShmBarrier", Tcl_NewStringObj(z, -1), pFd->pShmId, 0, 0);
1011363fc9e7Sdan   }
1012363fc9e7Sdan 
10135373b76bSdan   if( p->isFullshm ){
1014*7093a3beSdan     sqlite3_file *pReal = pFd->pReal;
1015*7093a3beSdan     pReal->pMethods->xShmBarrier(pReal);
10165373b76bSdan     return;
10175373b76bSdan   }
1018286a2884Sdrh }
1019286a2884Sdrh 
tvfsShmUnmap(sqlite3_file * pFile,int deleteFlag)1020da9fe0c3Sdan static int tvfsShmUnmap(
10217fd555a6Sdan   sqlite3_file *pFile,
1022c7991bdfSdan   int deleteFlag
1023c7991bdfSdan ){
1024c7991bdfSdan   int rc = SQLITE_OK;
1025c8ce3972Sdan   TestvfsFd *pFd = tvfsGetFd(pFile);
10267fd555a6Sdan   Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);
10277fd555a6Sdan   TestvfsBuffer *pBuffer = pFd->pShm;
1028c8ce3972Sdan   TestvfsFd **ppFd;
1029c7991bdfSdan 
10305373b76bSdan   if( p->isFullshm ){
1031*7093a3beSdan     sqlite3_file *pReal = pFd->pReal;
1032*7093a3beSdan     return pReal->pMethods->xShmUnmap(pReal, deleteFlag);
10335373b76bSdan   }
10345373b76bSdan 
1035da9fe0c3Sdan   if( !pBuffer ) return SQLITE_OK;
10367fd555a6Sdan   assert( pFd->pShmId && pFd->pShm );
1037c7991bdfSdan 
10381f55e28dSdan   if( p->pScript && p->mask&TESTVFS_SHMCLOSE_MASK ){
1039da9fe0c3Sdan     tvfsExecTcl(p, "xShmUnmap",
1040b88e24fdSdan         Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0, 0
1041c7991bdfSdan     );
1042c7991bdfSdan     tvfsResultCode(p, &rc);
10430235ab99Sdan   }
1044c7991bdfSdan 
1045ef4ee8f2Sdan   for(ppFd=&pBuffer->pFile; *ppFd!=pFd; ppFd=&((*ppFd)->pNext));
1046ef4ee8f2Sdan   assert( (*ppFd)==pFd );
1047ef4ee8f2Sdan   *ppFd = pFd->pNext;
10485257af7dSdan   pFd->pNext = 0;
1049ef4ee8f2Sdan 
1050ef4ee8f2Sdan   if( pBuffer->pFile==0 ){
105113a3cb82Sdan     int i;
1052c7991bdfSdan     TestvfsBuffer **pp;
1053c7991bdfSdan     for(pp=&p->pBuffer; *pp!=pBuffer; pp=&((*pp)->pNext));
1054c7991bdfSdan     *pp = (*pp)->pNext;
105513a3cb82Sdan     for(i=0; pBuffer->aPage[i]; i++){
105613a3cb82Sdan       ckfree((char *)pBuffer->aPage[i]);
105713a3cb82Sdan     }
1058c7991bdfSdan     ckfree((char *)pBuffer);
1059c7991bdfSdan   }
10607fd555a6Sdan   pFd->pShm = 0;
1061c7991bdfSdan 
1062c7991bdfSdan   return rc;
1063c7991bdfSdan }
1064c7991bdfSdan 
tvfsFetch(sqlite3_file * pFile,sqlite3_int64 iOfst,int iAmt,void ** pp)1065789efdb9Sdan static int tvfsFetch(
1066789efdb9Sdan     sqlite3_file *pFile,
1067789efdb9Sdan     sqlite3_int64 iOfst,
1068789efdb9Sdan     int iAmt,
1069789efdb9Sdan     void **pp
1070789efdb9Sdan ){
1071789efdb9Sdan   TestvfsFd *pFd = tvfsGetFd(pFile);
1072789efdb9Sdan   return sqlite3OsFetch(pFd->pReal, iOfst, iAmt, pp);
1073789efdb9Sdan }
1074789efdb9Sdan 
tvfsUnfetch(sqlite3_file * pFile,sqlite3_int64 iOfst,void * p)1075789efdb9Sdan static int tvfsUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *p){
1076789efdb9Sdan   TestvfsFd *pFd = tvfsGetFd(pFile);
1077789efdb9Sdan   return sqlite3OsUnfetch(pFd->pReal, iOfst, p);
1078789efdb9Sdan }
1079789efdb9Sdan 
testvfs_obj_cmd(ClientData cd,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])10807617e4a8Smistachkin static int SQLITE_TCLAPI testvfs_obj_cmd(
1081c7991bdfSdan   ClientData cd,
1082c7991bdfSdan   Tcl_Interp *interp,
1083c7991bdfSdan   int objc,
1084c7991bdfSdan   Tcl_Obj *CONST objv[]
1085c7991bdfSdan ){
1086c7991bdfSdan   Testvfs *p = (Testvfs *)cd;
1087c7991bdfSdan 
10881f55e28dSdan   enum DB_enum {
10892a321c75Sdan     CMD_SHM, CMD_DELETE, CMD_FILTER, CMD_IOERR, CMD_SCRIPT,
1090f9b4419dSdan     CMD_DEVCHAR, CMD_SECTORSIZE, CMD_FULLERR, CMD_CANTOPENERR
10911f55e28dSdan   };
10922a321c75Sdan   struct TestvfsSubcmd {
10932a321c75Sdan     char *zName;
10942a321c75Sdan     enum DB_enum eCmd;
10952a321c75Sdan   } aSubcmd[] = {
10962a321c75Sdan     { "shm",         CMD_SHM         },
10972a321c75Sdan     { "delete",      CMD_DELETE      },
10982a321c75Sdan     { "filter",      CMD_FILTER      },
10992a321c75Sdan     { "ioerr",       CMD_IOERR       },
1100146ed78bSdan     { "fullerr",     CMD_FULLERR     },
1101f9b4419dSdan     { "cantopenerr", CMD_CANTOPENERR },
11022a321c75Sdan     { "script",      CMD_SCRIPT      },
11032a321c75Sdan     { "devchar",     CMD_DEVCHAR     },
11042a321c75Sdan     { "sectorsize",  CMD_SECTORSIZE  },
11052a321c75Sdan     { 0, 0 }
11062a321c75Sdan   };
1107c7991bdfSdan   int i;
1108c7991bdfSdan 
1109c7991bdfSdan   if( objc<2 ){
1110c7991bdfSdan     Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
1111c7991bdfSdan     return TCL_ERROR;
1112c7991bdfSdan   }
11132a321c75Sdan   if( Tcl_GetIndexFromObjStruct(
11142a321c75Sdan         interp, objv[1], aSubcmd, sizeof(aSubcmd[0]), "subcommand", 0, &i)
11152a321c75Sdan   ){
1116c7991bdfSdan     return TCL_ERROR;
1117c7991bdfSdan   }
1118c7991bdfSdan   Tcl_ResetResult(interp);
1119c7991bdfSdan 
11202a321c75Sdan   switch( aSubcmd[i].eCmd ){
1121c7991bdfSdan     case CMD_SHM: {
112213a3cb82Sdan       Tcl_Obj *pObj;
11238e18922fSmistachkin       int rc;
1124c7991bdfSdan       TestvfsBuffer *pBuffer;
1125c7991bdfSdan       char *zName;
1126c7991bdfSdan       if( objc!=3 && objc!=4 ){
1127c7991bdfSdan         Tcl_WrongNumArgs(interp, 2, objv, "FILE ?VALUE?");
1128c7991bdfSdan         return TCL_ERROR;
1129c7991bdfSdan       }
11305d656852Sdan       zName = ckalloc(p->pParent->mxPathname);
11316c3c1a09Smistachkin       rc = p->pParent->xFullPathname(
11325d656852Sdan           p->pParent, Tcl_GetString(objv[2]),
11335d656852Sdan           p->pParent->mxPathname, zName
11345d656852Sdan       );
11356c3c1a09Smistachkin       if( rc!=SQLITE_OK ){
11366c3c1a09Smistachkin         Tcl_AppendResult(interp, "failed to get full path: ",
11376c3c1a09Smistachkin                          Tcl_GetString(objv[2]), 0);
11386c3c1a09Smistachkin         ckfree(zName);
11396c3c1a09Smistachkin         return TCL_ERROR;
11406c3c1a09Smistachkin       }
1141c7991bdfSdan       for(pBuffer=p->pBuffer; pBuffer; pBuffer=pBuffer->pNext){
1142c7991bdfSdan         if( 0==strcmp(pBuffer->zFile, zName) ) break;
1143c7991bdfSdan       }
11445d656852Sdan       ckfree(zName);
1145c7991bdfSdan       if( !pBuffer ){
11465d656852Sdan         Tcl_AppendResult(interp, "no such file: ", Tcl_GetString(objv[2]), 0);
1147c7991bdfSdan         return TCL_ERROR;
1148c7991bdfSdan       }
1149c7991bdfSdan       if( objc==4 ){
1150c7991bdfSdan         int n;
1151c7991bdfSdan         u8 *a = Tcl_GetByteArrayFromObj(objv[3], &n);
11520e986f51Sdan         int pgsz = pBuffer->pgsz;
1153b2eced5dSdrh         if( pgsz==0 ) pgsz = 65536;
11540e986f51Sdan         for(i=0; i*pgsz<n; i++){
11550e986f51Sdan           int nByte = pgsz;
11560e986f51Sdan           tvfsAllocPage(pBuffer, i, pgsz);
11570e986f51Sdan           if( n-i*pgsz<pgsz ){
115813a3cb82Sdan             nByte = n;
1159c7991bdfSdan           }
11600e986f51Sdan           memcpy(pBuffer->aPage[i], &a[i*pgsz], nByte);
116113a3cb82Sdan         }
116213a3cb82Sdan       }
116313a3cb82Sdan 
116413a3cb82Sdan       pObj = Tcl_NewObj();
116513a3cb82Sdan       for(i=0; pBuffer->aPage[i]; i++){
11660e986f51Sdan         int pgsz = pBuffer->pgsz;
1167b2eced5dSdrh         if( pgsz==0 ) pgsz = 65536;
11680e986f51Sdan         Tcl_AppendObjToObj(pObj, Tcl_NewByteArrayObj(pBuffer->aPage[i], pgsz));
116913a3cb82Sdan       }
117013a3cb82Sdan       Tcl_SetObjResult(interp, pObj);
1171c7991bdfSdan       break;
1172c7991bdfSdan     }
11730235ab99Sdan 
117462e603a9Sdrh     /*  TESTVFS filter METHOD-LIST
117562e603a9Sdrh     **
117662e603a9Sdrh     **     Activate special processing for those methods contained in the list
117762e603a9Sdrh     */
11780235ab99Sdan     case CMD_FILTER: {
11790235ab99Sdan       static struct VfsMethod {
11800235ab99Sdan         char *zName;
11810235ab99Sdan         int mask;
11820235ab99Sdan       } vfsmethod [] = {
11830235ab99Sdan         { "xShmOpen",           TESTVFS_SHMOPEN_MASK },
11840235ab99Sdan         { "xShmLock",           TESTVFS_SHMLOCK_MASK },
11850235ab99Sdan         { "xShmBarrier",        TESTVFS_SHMBARRIER_MASK },
1186da9fe0c3Sdan         { "xShmUnmap",          TESTVFS_SHMCLOSE_MASK },
11876b017cc6Sdrh         { "xShmMap",            TESTVFS_SHMMAP_MASK },
1188d764c7deSdan         { "xSync",              TESTVFS_SYNC_MASK },
1189b0ac3e3aSdan         { "xDelete",            TESTVFS_DELETE_MASK },
11902a321c75Sdan         { "xWrite",             TESTVFS_WRITE_MASK },
1191262765a7Sdan         { "xRead",              TESTVFS_READ_MASK },
11922a321c75Sdan         { "xTruncate",          TESTVFS_TRUNCATE_MASK },
1193d764c7deSdan         { "xOpen",              TESTVFS_OPEN_MASK },
11942a321c75Sdan         { "xClose",             TESTVFS_CLOSE_MASK },
1195f9b4419dSdan         { "xAccess",            TESTVFS_ACCESS_MASK },
11968e98037cSdan         { "xFullPathname",      TESTVFS_FULLPATHNAME_MASK },
1197d7a558a9Sdan         { "xUnlock",            TESTVFS_UNLOCK_MASK },
119862e603a9Sdrh         { "xLock",              TESTVFS_LOCK_MASK },
119962e603a9Sdrh         { "xCheckReservedLock", TESTVFS_CKLOCK_MASK },
1200140a5987Sdan         { "xFileControl",       TESTVFS_FCNTL_MASK },
12010235ab99Sdan       };
12020235ab99Sdan       Tcl_Obj **apElem = 0;
12030235ab99Sdan       int nElem = 0;
12040235ab99Sdan       int mask = 0;
12050235ab99Sdan       if( objc!=3 ){
12060235ab99Sdan         Tcl_WrongNumArgs(interp, 2, objv, "LIST");
12070235ab99Sdan         return TCL_ERROR;
12080235ab99Sdan       }
12090235ab99Sdan       if( Tcl_ListObjGetElements(interp, objv[2], &nElem, &apElem) ){
12100235ab99Sdan         return TCL_ERROR;
12110235ab99Sdan       }
12120235ab99Sdan       Tcl_ResetResult(interp);
12130235ab99Sdan       for(i=0; i<nElem; i++){
12140235ab99Sdan         int iMethod;
12150235ab99Sdan         char *zElem = Tcl_GetString(apElem[i]);
12160235ab99Sdan         for(iMethod=0; iMethod<ArraySize(vfsmethod); iMethod++){
12170235ab99Sdan           if( strcmp(zElem, vfsmethod[iMethod].zName)==0 ){
12180235ab99Sdan             mask |= vfsmethod[iMethod].mask;
12190235ab99Sdan             break;
12200235ab99Sdan           }
12210235ab99Sdan         }
12220235ab99Sdan         if( iMethod==ArraySize(vfsmethod) ){
12230235ab99Sdan           Tcl_AppendResult(interp, "unknown method: ", zElem, 0);
12240235ab99Sdan           return TCL_ERROR;
12250235ab99Sdan         }
12260235ab99Sdan       }
12270235ab99Sdan       p->mask = mask;
12280235ab99Sdan       break;
12290235ab99Sdan     }
12300235ab99Sdan 
123162e603a9Sdrh     /*
123262e603a9Sdrh     **  TESTVFS script ?SCRIPT?
123362e603a9Sdrh     **
123462e603a9Sdrh     **  Query or set the script to be run when filtered VFS events
123562e603a9Sdrh     **  occur.
123662e603a9Sdrh     */
12371f55e28dSdan     case CMD_SCRIPT: {
12381f55e28dSdan       if( objc==3 ){
12391f55e28dSdan         int nByte;
12401f55e28dSdan         if( p->pScript ){
12411f55e28dSdan           Tcl_DecrRefCount(p->pScript);
12425d656852Sdan           p->pScript = 0;
12431f55e28dSdan         }
12441f55e28dSdan         Tcl_GetStringFromObj(objv[2], &nByte);
12451f55e28dSdan         if( nByte>0 ){
12461f55e28dSdan           p->pScript = Tcl_DuplicateObj(objv[2]);
12471f55e28dSdan           Tcl_IncrRefCount(p->pScript);
12481f55e28dSdan         }
12491f55e28dSdan       }else if( objc!=2 ){
12501f55e28dSdan         Tcl_WrongNumArgs(interp, 2, objv, "?SCRIPT?");
12511f55e28dSdan         return TCL_ERROR;
12521f55e28dSdan       }
12531f55e28dSdan 
12541f55e28dSdan       Tcl_ResetResult(interp);
12551f55e28dSdan       if( p->pScript ) Tcl_SetObjResult(interp, p->pScript);
12561f55e28dSdan 
12571f55e28dSdan       break;
12581f55e28dSdan     }
12591f55e28dSdan 
12601f55e28dSdan     /*
12611f55e28dSdan     ** TESTVFS ioerr ?IFAIL PERSIST?
12621f55e28dSdan     **
12631f55e28dSdan     **   Where IFAIL is an integer and PERSIST is boolean.
12641f55e28dSdan     */
1265f9b4419dSdan     case CMD_CANTOPENERR:
1266f9b4419dSdan     case CMD_IOERR:
1267f9b4419dSdan     case CMD_FULLERR: {
12687bb22ac7Smistachkin       TestFaultInject *pTest = 0;
1269f9b4419dSdan       int iRet;
12701f55e28dSdan 
1271f9b4419dSdan       switch( aSubcmd[i].eCmd ){
1272f9b4419dSdan         case CMD_IOERR: pTest = &p->ioerr_err; break;
1273f9b4419dSdan         case CMD_FULLERR: pTest = &p->full_err; break;
1274f9b4419dSdan         case CMD_CANTOPENERR: pTest = &p->cantopen_err; break;
1275f9b4419dSdan         default: assert(0);
1276f9b4419dSdan       }
1277f9b4419dSdan       iRet = pTest->nFail;
1278f9b4419dSdan       pTest->nFail = 0;
1279f9b4419dSdan       pTest->eFault = 0;
1280f9b4419dSdan       pTest->iCnt = 0;
12811f55e28dSdan 
12821f55e28dSdan       if( objc==4 ){
12830235ab99Sdan         int iCnt, iPersist;
12840235ab99Sdan         if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iCnt)
12850235ab99Sdan          || TCL_OK!=Tcl_GetBooleanFromObj(interp, objv[3], &iPersist)
12860235ab99Sdan         ){
12870235ab99Sdan           return TCL_ERROR;
12880235ab99Sdan         }
1289f9b4419dSdan         pTest->eFault = iPersist?FAULT_INJECT_PERSISTENT:FAULT_INJECT_TRANSIENT;
1290f9b4419dSdan         pTest->iCnt = iCnt;
12911f55e28dSdan       }else if( objc!=2 ){
1292f9b4419dSdan         Tcl_WrongNumArgs(interp, 2, objv, "?CNT PERSIST?");
12931f55e28dSdan         return TCL_ERROR;
12940235ab99Sdan       }
12950235ab99Sdan       Tcl_SetObjResult(interp, Tcl_NewIntObj(iRet));
12960235ab99Sdan       break;
12970235ab99Sdan     }
12980235ab99Sdan 
1299c7991bdfSdan     case CMD_DELETE: {
1300c7991bdfSdan       Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
1301c7991bdfSdan       break;
1302c7991bdfSdan     }
13032a321c75Sdan 
13042a321c75Sdan     case CMD_DEVCHAR: {
13052a321c75Sdan       struct DeviceFlag {
13062a321c75Sdan         char *zName;
13072a321c75Sdan         int iValue;
13082a321c75Sdan       } aFlag[] = {
13092a321c75Sdan         { "default",               -1 },
13102a321c75Sdan         { "atomic",                SQLITE_IOCAP_ATOMIC                },
13112a321c75Sdan         { "atomic512",             SQLITE_IOCAP_ATOMIC512             },
13122a321c75Sdan         { "atomic1k",              SQLITE_IOCAP_ATOMIC1K              },
13132a321c75Sdan         { "atomic2k",              SQLITE_IOCAP_ATOMIC2K              },
13142a321c75Sdan         { "atomic4k",              SQLITE_IOCAP_ATOMIC4K              },
13152a321c75Sdan         { "atomic8k",              SQLITE_IOCAP_ATOMIC8K              },
13162a321c75Sdan         { "atomic16k",             SQLITE_IOCAP_ATOMIC16K             },
13172a321c75Sdan         { "atomic32k",             SQLITE_IOCAP_ATOMIC32K             },
13182a321c75Sdan         { "atomic64k",             SQLITE_IOCAP_ATOMIC64K             },
13192a321c75Sdan         { "sequential",            SQLITE_IOCAP_SEQUENTIAL            },
13202a321c75Sdan         { "safe_append",           SQLITE_IOCAP_SAFE_APPEND           },
13218ce49d6aSdan         { "undeletable_when_open", SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN },
1322cb15f35fSdrh         { "powersafe_overwrite",   SQLITE_IOCAP_POWERSAFE_OVERWRITE   },
132362e603a9Sdrh         { "immutable",             SQLITE_IOCAP_IMMUTABLE             },
13242a321c75Sdan         { 0, 0 }
13252a321c75Sdan       };
13262a321c75Sdan       Tcl_Obj *pRet;
13272a321c75Sdan       int iFlag;
13282a321c75Sdan 
13292a321c75Sdan       if( objc>3 ){
13302a321c75Sdan         Tcl_WrongNumArgs(interp, 2, objv, "?ATTR-LIST?");
13312a321c75Sdan         return TCL_ERROR;
13322a321c75Sdan       }
13332a321c75Sdan       if( objc==3 ){
13342a321c75Sdan         int j;
13352a321c75Sdan         int iNew = 0;
13362a321c75Sdan         Tcl_Obj **flags = 0;
13372a321c75Sdan         int nFlags = 0;
13382a321c75Sdan 
13392a321c75Sdan         if( Tcl_ListObjGetElements(interp, objv[2], &nFlags, &flags) ){
13402a321c75Sdan           return TCL_ERROR;
13412a321c75Sdan         }
13422a321c75Sdan 
13432a321c75Sdan         for(j=0; j<nFlags; j++){
13442a321c75Sdan           int idx = 0;
13452a321c75Sdan           if( Tcl_GetIndexFromObjStruct(interp, flags[j], aFlag,
13462a321c75Sdan                 sizeof(aFlag[0]), "flag", 0, &idx)
13472a321c75Sdan           ){
13482a321c75Sdan             return TCL_ERROR;
13492a321c75Sdan           }
13502a321c75Sdan           if( aFlag[idx].iValue<0 && nFlags>1 ){
13512a321c75Sdan             Tcl_AppendResult(interp, "bad flags: ", Tcl_GetString(objv[2]), 0);
13522a321c75Sdan             return TCL_ERROR;
13532a321c75Sdan           }
13542a321c75Sdan           iNew |= aFlag[idx].iValue;
13552a321c75Sdan         }
13562a321c75Sdan 
13571eaaf93aSdrh         p->iDevchar = iNew| 0x10000000;
13582a321c75Sdan       }
13592a321c75Sdan 
13602a321c75Sdan       pRet = Tcl_NewObj();
13612a321c75Sdan       for(iFlag=0; iFlag<sizeof(aFlag)/sizeof(aFlag[0]); iFlag++){
13622a321c75Sdan         if( p->iDevchar & aFlag[iFlag].iValue ){
13632a321c75Sdan           Tcl_ListObjAppendElement(
13642a321c75Sdan               interp, pRet, Tcl_NewStringObj(aFlag[iFlag].zName, -1)
13652a321c75Sdan           );
13662a321c75Sdan         }
13672a321c75Sdan       }
13682a321c75Sdan       Tcl_SetObjResult(interp, pRet);
13692a321c75Sdan 
13702a321c75Sdan       break;
13712a321c75Sdan     }
13722a321c75Sdan 
13732a321c75Sdan     case CMD_SECTORSIZE: {
13742a321c75Sdan       if( objc>3 ){
13752a321c75Sdan         Tcl_WrongNumArgs(interp, 2, objv, "?VALUE?");
13762a321c75Sdan         return TCL_ERROR;
13772a321c75Sdan       }
13782a321c75Sdan       if( objc==3 ){
13792a321c75Sdan         int iNew = 0;
13802a321c75Sdan         if( Tcl_GetIntFromObj(interp, objv[2], &iNew) ){
13812a321c75Sdan           return TCL_ERROR;
13822a321c75Sdan         }
13832a321c75Sdan         p->iSectorsize = iNew;
13842a321c75Sdan       }
13852a321c75Sdan       Tcl_SetObjResult(interp, Tcl_NewIntObj(p->iSectorsize));
13862a321c75Sdan       break;
13872a321c75Sdan     }
1388c7991bdfSdan   }
1389c7991bdfSdan 
1390c7991bdfSdan   return TCL_OK;
1391c7991bdfSdan }
1392c7991bdfSdan 
testvfs_obj_del(ClientData cd)13937617e4a8Smistachkin static void SQLITE_TCLAPI testvfs_obj_del(ClientData cd){
1394c7991bdfSdan   Testvfs *p = (Testvfs *)cd;
13951f55e28dSdan   if( p->pScript ) Tcl_DecrRefCount(p->pScript);
1396c7991bdfSdan   sqlite3_vfs_unregister(p->pVfs);
139783420823Sdan   memset(p->pVfs, 0, sizeof(sqlite3_vfs));
1398c7991bdfSdan   ckfree((char *)p->pVfs);
13999c9c7092Sdan   memset(p, 0, sizeof(Testvfs));
1400c7991bdfSdan   ckfree((char *)p);
1401c7991bdfSdan }
1402c7991bdfSdan 
1403c7991bdfSdan /*
14041f55e28dSdan ** Usage:  testvfs VFSNAME ?SWITCHES?
14051f55e28dSdan **
14061f55e28dSdan ** Switches are:
14071f55e28dSdan **
14081f55e28dSdan **   -noshm   BOOLEAN             (True to omit shm methods. Default false)
14091f55e28dSdan **   -default BOOLEAN             (True to make the vfs default. Default false)
1410c7991bdfSdan **
1411c7991bdfSdan ** This command creates two things when it is invoked: an SQLite VFS, and
1412c7991bdfSdan ** a Tcl command. Both are named VFSNAME. The VFS is installed. It is not
1413c7991bdfSdan ** installed as the default VFS.
1414c7991bdfSdan **
1415c7991bdfSdan ** The VFS passes all file I/O calls through to the underlying VFS.
1416c7991bdfSdan **
14176b017cc6Sdrh ** Whenever the xShmMap method of the VFS
14186b017cc6Sdrh ** is invoked, the SCRIPT is executed as follows:
1419c7991bdfSdan **
14206b017cc6Sdrh **   SCRIPT xShmMap    FILENAME ID
1421c7991bdfSdan **
1422c7991bdfSdan ** The value returned by the invocation of SCRIPT above is interpreted as
1423c7991bdfSdan ** an SQLite error code and returned to SQLite. Either a symbolic
1424c7991bdfSdan ** "SQLITE_OK" or numeric "0" value may be returned.
1425c7991bdfSdan **
1426c7991bdfSdan ** The contents of the shared-memory buffer associated with a given file
1427c7991bdfSdan ** may be read and set using the following command:
1428c7991bdfSdan **
1429c7991bdfSdan **   VFSNAME shm FILENAME ?NEWVALUE?
1430c7991bdfSdan **
1431c7991bdfSdan ** When the xShmLock method is invoked by SQLite, the following script is
1432c7991bdfSdan ** run:
1433c7991bdfSdan **
1434c7991bdfSdan **   SCRIPT xShmLock    FILENAME ID LOCK
1435c7991bdfSdan **
143673b64e4dSdrh ** where LOCK is of the form "OFFSET NBYTE lock/unlock shared/exclusive"
1437c7991bdfSdan */
testvfs_cmd(ClientData cd,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])14387617e4a8Smistachkin static int SQLITE_TCLAPI testvfs_cmd(
1439c7991bdfSdan   ClientData cd,
1440c7991bdfSdan   Tcl_Interp *interp,
1441c7991bdfSdan   int objc,
1442c7991bdfSdan   Tcl_Obj *CONST objv[]
1443c7991bdfSdan ){
1444c7991bdfSdan   static sqlite3_vfs tvfs_vfs = {
1445789efdb9Sdan     3,                            /* iVersion */
1446c8ce3972Sdan     0,                            /* szOsFile */
1447c7991bdfSdan     0,                            /* mxPathname */
1448c7991bdfSdan     0,                            /* pNext */
1449c7991bdfSdan     0,                            /* zName */
1450c7991bdfSdan     0,                            /* pAppData */
1451c7991bdfSdan     tvfsOpen,                     /* xOpen */
1452c7991bdfSdan     tvfsDelete,                   /* xDelete */
1453c7991bdfSdan     tvfsAccess,                   /* xAccess */
1454c7991bdfSdan     tvfsFullPathname,             /* xFullPathname */
1455c7991bdfSdan #ifndef SQLITE_OMIT_LOAD_EXTENSION
1456c7991bdfSdan     tvfsDlOpen,                   /* xDlOpen */
1457c7991bdfSdan     tvfsDlError,                  /* xDlError */
1458c7991bdfSdan     tvfsDlSym,                    /* xDlSym */
1459c7991bdfSdan     tvfsDlClose,                  /* xDlClose */
1460c7991bdfSdan #else
1461c7991bdfSdan     0,                            /* xDlOpen */
1462c7991bdfSdan     0,                            /* xDlError */
1463c7991bdfSdan     0,                            /* xDlSym */
1464c7991bdfSdan     0,                            /* xDlClose */
1465c7991bdfSdan #endif /* SQLITE_OMIT_LOAD_EXTENSION */
1466c7991bdfSdan     tvfsRandomness,               /* xRandomness */
1467c7991bdfSdan     tvfsSleep,                    /* xSleep */
1468c7991bdfSdan     tvfsCurrentTime,              /* xCurrentTime */
1469c7991bdfSdan     0,                            /* xGetLastError */
14702667be5eSdrh     0,                            /* xCurrentTimeInt64 */
1471789efdb9Sdan     0,                            /* xSetSystemCall */
1472789efdb9Sdan     0,                            /* xGetSystemCall */
1473789efdb9Sdan     0,                            /* xNextSystemCall */
1474c7991bdfSdan   };
1475c7991bdfSdan 
1476c7991bdfSdan   Testvfs *p;                     /* New object */
1477c7991bdfSdan   sqlite3_vfs *pVfs;              /* New VFS */
1478c7991bdfSdan   char *zVfs;
1479c7991bdfSdan   int nByte;                      /* Bytes of space to allocate at p */
14801f55e28dSdan 
14811f55e28dSdan   int i;
1482576bc329Sdan   int isNoshm = 0;                /* True if -noshm is passed */
14835373b76bSdan   int isFullshm = 0;              /* True if -fullshm is passed */
14841f55e28dSdan   int isDefault = 0;              /* True if -default is passed */
1485c8ce3972Sdan   int szOsFile = 0;               /* Value passed to -szosfile */
1486c8ce3972Sdan   int mxPathname = -1;            /* Value passed to -mxpathname */
1487789efdb9Sdan   int iVersion = 3;               /* Value passed to -iversion */
1488c7991bdfSdan 
14891f55e28dSdan   if( objc<2 || 0!=(objc%2) ) goto bad_args;
14901f55e28dSdan   for(i=2; i<objc; i += 2){
14911f55e28dSdan     int nSwitch;
14921f55e28dSdan     char *zSwitch;
14931f55e28dSdan     zSwitch = Tcl_GetStringFromObj(objv[i], &nSwitch);
1494c8ce3972Sdan 
14951f55e28dSdan     if( nSwitch>2 && 0==strncmp("-noshm", zSwitch, nSwitch) ){
14961f55e28dSdan       if( Tcl_GetBooleanFromObj(interp, objv[i+1], &isNoshm) ){
1497c7991bdfSdan         return TCL_ERROR;
1498c7991bdfSdan       }
14995373b76bSdan       if( isNoshm ) isFullshm = 0;
15001f55e28dSdan     }
15011f55e28dSdan     else if( nSwitch>2 && 0==strncmp("-default", zSwitch, nSwitch) ){
15021f55e28dSdan       if( Tcl_GetBooleanFromObj(interp, objv[i+1], &isDefault) ){
15031f55e28dSdan         return TCL_ERROR;
15041f55e28dSdan       }
15051f55e28dSdan     }
1506c8ce3972Sdan     else if( nSwitch>2 && 0==strncmp("-szosfile", zSwitch, nSwitch) ){
1507c8ce3972Sdan       if( Tcl_GetIntFromObj(interp, objv[i+1], &szOsFile) ){
1508c8ce3972Sdan         return TCL_ERROR;
1509c8ce3972Sdan       }
1510c8ce3972Sdan     }
1511c8ce3972Sdan     else if( nSwitch>2 && 0==strncmp("-mxpathname", zSwitch, nSwitch) ){
1512c8ce3972Sdan       if( Tcl_GetIntFromObj(interp, objv[i+1], &mxPathname) ){
1513c8ce3972Sdan         return TCL_ERROR;
1514c8ce3972Sdan       }
1515c8ce3972Sdan     }
151689ccf448Sdan     else if( nSwitch>2 && 0==strncmp("-iversion", zSwitch, nSwitch) ){
151789ccf448Sdan       if( Tcl_GetIntFromObj(interp, objv[i+1], &iVersion) ){
151889ccf448Sdan         return TCL_ERROR;
151989ccf448Sdan       }
152089ccf448Sdan     }
15215373b76bSdan     else if( nSwitch>2 && 0==strncmp("-fullshm", zSwitch, nSwitch) ){
15225373b76bSdan       if( Tcl_GetBooleanFromObj(interp, objv[i+1], &isFullshm) ){
15235373b76bSdan         return TCL_ERROR;
15245373b76bSdan       }
15255373b76bSdan       if( isFullshm ) isNoshm = 0;
15265373b76bSdan     }
15271f55e28dSdan     else{
15281f55e28dSdan       goto bad_args;
15291f55e28dSdan     }
15301f55e28dSdan   }
1531c7991bdfSdan 
1532c8ce3972Sdan   if( szOsFile<sizeof(TestvfsFile) ){
1533c8ce3972Sdan     szOsFile = sizeof(TestvfsFile);
1534c8ce3972Sdan   }
1535c8ce3972Sdan 
15361f55e28dSdan   zVfs = Tcl_GetString(objv[1]);
153783cc1392Sdrh   nByte = sizeof(Testvfs) + (int)strlen(zVfs)+1;
1538c7991bdfSdan   p = (Testvfs *)ckalloc(nByte);
1539c7991bdfSdan   memset(p, 0, nByte);
15402a321c75Sdan   p->iDevchar = -1;
15412a321c75Sdan   p->iSectorsize = -1;
1542c7991bdfSdan 
15435d656852Sdan   /* Create the new object command before querying SQLite for a default VFS
15445d656852Sdan   ** to use for 'real' IO operations. This is because creating the new VFS
15455d656852Sdan   ** may delete an existing [testvfs] VFS of the same name. If such a VFS
15465d656852Sdan   ** is currently the default, the new [testvfs] may end up calling the
15475d656852Sdan   ** methods of a deleted object.
15485d656852Sdan   */
15495d656852Sdan   Tcl_CreateObjCommand(interp, zVfs, testvfs_obj_cmd, p, testvfs_obj_del);
1550c7991bdfSdan   p->pParent = sqlite3_vfs_find(0);
1551c7991bdfSdan   p->interp = interp;
15521f55e28dSdan 
15531f55e28dSdan   p->zName = (char *)&p[1];
15541f55e28dSdan   memcpy(p->zName, zVfs, strlen(zVfs)+1);
1555c7991bdfSdan 
1556c7991bdfSdan   pVfs = (sqlite3_vfs *)ckalloc(sizeof(sqlite3_vfs));
1557c7991bdfSdan   memcpy(pVfs, &tvfs_vfs, sizeof(sqlite3_vfs));
1558c7991bdfSdan   pVfs->pAppData = (void *)p;
155989ccf448Sdan   pVfs->iVersion = iVersion;
1560c7991bdfSdan   pVfs->zName = p->zName;
1561c7991bdfSdan   pVfs->mxPathname = p->pParent->mxPathname;
1562c8ce3972Sdan   if( mxPathname>=0 && mxPathname<pVfs->mxPathname ){
1563c8ce3972Sdan     pVfs->mxPathname = mxPathname;
1564c8ce3972Sdan   }
1565c8ce3972Sdan   pVfs->szOsFile = szOsFile;
15668f6097c2Sdan   p->pVfs = pVfs;
15677fd555a6Sdan   p->isNoshm = isNoshm;
15685373b76bSdan   p->isFullshm = isFullshm;
15690235ab99Sdan   p->mask = TESTVFS_ALL_MASK;
1570c7991bdfSdan 
15711f55e28dSdan   sqlite3_vfs_register(pVfs, isDefault);
1572c7991bdfSdan 
1573c7991bdfSdan   return TCL_OK;
1574576bc329Sdan 
1575576bc329Sdan  bad_args:
1576363fc9e7Sdan   Tcl_WrongNumArgs(interp, 1, objv, "VFSNAME ?-noshm BOOL? ?-fullshm BOOL? ?-default BOOL? ?-mxpathname INT? ?-szosfile INT? ?-iversion INT?");
1577576bc329Sdan   return TCL_ERROR;
1578c7991bdfSdan }
1579c7991bdfSdan 
158094e95ea4Sdan extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
158194e95ea4Sdan extern const char *sqlite3ErrName(int);
158294e95ea4Sdan 
158394e95ea4Sdan /*
158494e95ea4Sdan ** tclcmd: vfs_shmlock DB DBNAME (shared|exclusive) (lock|unlock) OFFSET N
158594e95ea4Sdan */
test_vfs_shmlock(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])158694e95ea4Sdan static int SQLITE_TCLAPI test_vfs_shmlock(
158794e95ea4Sdan   void * clientData,
158894e95ea4Sdan   Tcl_Interp *interp,
158994e95ea4Sdan   int objc,
159094e95ea4Sdan   Tcl_Obj *CONST objv[]
159194e95ea4Sdan ){
159294e95ea4Sdan   const char *azArg1[] = {"shared", "exclusive", 0};
159394e95ea4Sdan   const char *azArg2[] = {"lock", "unlock", 0};
159494e95ea4Sdan   sqlite3 *db = 0;
159594e95ea4Sdan   int rc = SQLITE_OK;
159694e95ea4Sdan   const char *zDbname = 0;
159794e95ea4Sdan   int iArg1 = 0;
159894e95ea4Sdan   int iArg2 = 0;
159994e95ea4Sdan   int iOffset = 0;
160094e95ea4Sdan   int n = 0;
160194e95ea4Sdan   sqlite3_file *pFd;
160294e95ea4Sdan 
160394e95ea4Sdan   if( objc!=7 ){
160494e95ea4Sdan     Tcl_WrongNumArgs(interp, 1, objv,
160594e95ea4Sdan         "DB DBNAME (shared|exclusive) (lock|unlock) OFFSET N"
160694e95ea4Sdan     );
160794e95ea4Sdan     return TCL_ERROR;
160894e95ea4Sdan   }
160994e95ea4Sdan 
161094e95ea4Sdan   zDbname = Tcl_GetString(objv[2]);
161194e95ea4Sdan   if( getDbPointer(interp, Tcl_GetString(objv[1]), &db)
161294e95ea4Sdan    || Tcl_GetIndexFromObj(interp, objv[3], azArg1, "ARG", 0, &iArg1)
161394e95ea4Sdan    || Tcl_GetIndexFromObj(interp, objv[4], azArg2, "ARG", 0, &iArg2)
161494e95ea4Sdan    || Tcl_GetIntFromObj(interp, objv[5], &iOffset)
161594e95ea4Sdan    || Tcl_GetIntFromObj(interp, objv[6], &n)
161694e95ea4Sdan   ){
161794e95ea4Sdan     return TCL_ERROR;
161894e95ea4Sdan   }
161994e95ea4Sdan 
162094e95ea4Sdan   sqlite3_file_control(db, zDbname, SQLITE_FCNTL_FILE_POINTER, (void*)&pFd);
162194e95ea4Sdan   if( pFd==0 ){
162294e95ea4Sdan     return TCL_ERROR;
162394e95ea4Sdan   }
162494e95ea4Sdan   rc = pFd->pMethods->xShmLock(pFd, iOffset, n,
162594e95ea4Sdan       (iArg1==0 ? SQLITE_SHM_SHARED : SQLITE_SHM_EXCLUSIVE)
162694e95ea4Sdan     | (iArg2==0 ? SQLITE_SHM_LOCK : SQLITE_SHM_UNLOCK)
162794e95ea4Sdan   );
162894e95ea4Sdan   Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
162994e95ea4Sdan   return TCL_OK;
163094e95ea4Sdan }
163194e95ea4Sdan 
test_vfs_set_readmark(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])163276e4990bSdan static int SQLITE_TCLAPI test_vfs_set_readmark(
163376e4990bSdan   void * clientData,
163476e4990bSdan   Tcl_Interp *interp,
163576e4990bSdan   int objc,
163676e4990bSdan   Tcl_Obj *CONST objv[]
163776e4990bSdan ){
163876e4990bSdan   sqlite3 *db = 0;
163976e4990bSdan   int rc = SQLITE_OK;
164076e4990bSdan   const char *zDbname = 0;
164176e4990bSdan   int iSlot = 0;
164276e4990bSdan   int iVal = -1;
164376e4990bSdan   sqlite3_file *pFd;
164476e4990bSdan   void volatile *pShm = 0;
164576e4990bSdan   u32 *aShm;
164676e4990bSdan   int iOff;
164776e4990bSdan 
164876e4990bSdan   if( objc!=4 && objc!=5 ){
164976e4990bSdan     Tcl_WrongNumArgs(interp, 1, objv, "DB DBNAME SLOT ?VALUE?");
165076e4990bSdan     return TCL_ERROR;
165176e4990bSdan   }
165276e4990bSdan 
165376e4990bSdan   zDbname = Tcl_GetString(objv[2]);
165476e4990bSdan   if( getDbPointer(interp, Tcl_GetString(objv[1]), &db)
165576e4990bSdan    || Tcl_GetIntFromObj(interp, objv[3], &iSlot)
165676e4990bSdan    || (objc==5 && Tcl_GetIntFromObj(interp, objv[4], &iVal))
165776e4990bSdan   ){
165876e4990bSdan     return TCL_ERROR;
165976e4990bSdan   }
166076e4990bSdan 
166176e4990bSdan   sqlite3_file_control(db, zDbname, SQLITE_FCNTL_FILE_POINTER, (void*)&pFd);
166276e4990bSdan   if( pFd==0 ){
166376e4990bSdan     return TCL_ERROR;
166476e4990bSdan   }
166576e4990bSdan   rc = pFd->pMethods->xShmMap(pFd, 0, 32*1024, 0, &pShm);
166676e4990bSdan   if( rc!=SQLITE_OK ){
166776e4990bSdan     Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
166876e4990bSdan     return TCL_ERROR;
166976e4990bSdan   }
167076e4990bSdan   if( pShm==0 ){
167176e4990bSdan     Tcl_AppendResult(interp, "*-shm is not yet mapped", 0);
167276e4990bSdan     return TCL_ERROR;
167376e4990bSdan   }
167476e4990bSdan   aShm = (u32*)pShm;
167576e4990bSdan   iOff = 12*2+1+iSlot;
167676e4990bSdan 
167776e4990bSdan   if( objc==5 ){
167876e4990bSdan     aShm[iOff] = iVal;
167976e4990bSdan   }
168076e4990bSdan   Tcl_SetObjResult(interp, Tcl_NewIntObj(aShm[iOff]));
168176e4990bSdan 
168276e4990bSdan   return TCL_OK;
168376e4990bSdan }
168494e95ea4Sdan 
Sqlitetestvfs_Init(Tcl_Interp * interp)1685c7991bdfSdan int Sqlitetestvfs_Init(Tcl_Interp *interp){
16867fd555a6Sdan   Tcl_CreateObjCommand(interp, "testvfs", testvfs_cmd, 0, 0);
168794e95ea4Sdan   Tcl_CreateObjCommand(interp, "vfs_shmlock", test_vfs_shmlock, 0, 0);
168876e4990bSdan   Tcl_CreateObjCommand(interp, "vfs_set_readmark", test_vfs_set_readmark, 0, 0);
1689c7991bdfSdan   return TCL_OK;
1690c7991bdfSdan }
1691c7991bdfSdan 
1692c7991bdfSdan #endif
1693