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