blob: 46f3a0630a16f7c0078713a512a293d2472a02c9 [file] [log] [blame]
danc7991bd2010-05-05 19:04:59 +00001/*
2** 2010 May 05
3**
4** The author disclaims copyright to this source code. In place of
5** a legal notice, here is a blessing:
6**
7** May you do good and not evil.
8** May you find forgiveness for yourself and forgive others.
9** May you share freely, never taking more than you give.
10**
11******************************************************************************
12**
13*/
14#if SQLITE_TEST /* This file is used for testing only */
15
16#include "sqlite3.h"
17#include "sqliteInt.h"
18
danc7991bd2010-05-05 19:04:59 +000019typedef struct Testvfs Testvfs;
20typedef struct TestvfsShm TestvfsShm;
21typedef struct TestvfsBuffer TestvfsBuffer;
dan7fd555a2010-05-13 06:19:37 +000022typedef struct TestvfsFile TestvfsFile;
23
24/*
25** An open file handle.
26*/
27struct TestvfsFile {
28 sqlite3_file base; /* Base class. Must be first */
29 sqlite3_vfs *pVfs; /* The VFS */
30 const char *zFilename; /* Filename as passed to xOpen() */
31 sqlite3_file *pReal; /* The real, underlying file descriptor */
32 Tcl_Obj *pShmId; /* Shared memory id for Tcl callbacks */
danef4ee8f2010-06-05 11:53:34 +000033
dan7fd555a2010-05-13 06:19:37 +000034 TestvfsBuffer *pShm; /* Shared memory buffer */
danef4ee8f2010-06-05 11:53:34 +000035 u32 excllock; /* Mask of exclusive locks */
36 u32 sharedlock; /* Mask of shared locks */
37 TestvfsFile *pNext; /* Next handle opened on the same file */
drhd9e5c4f2010-05-12 18:01:39 +000038};
39
danc7991bd2010-05-05 19:04:59 +000040
41/*
42** An instance of this structure is allocated for each VFS created. The
43** sqlite3_vfs.pAppData field of the VFS structure registered with SQLite
44** is set to point to it.
45*/
46struct Testvfs {
47 char *zName; /* Name of this VFS */
48 sqlite3_vfs *pParent; /* The VFS to use for file IO */
49 sqlite3_vfs *pVfs; /* The testvfs registered with SQLite */
50 Tcl_Interp *interp; /* Interpreter to run script in */
dan1f55e282010-06-03 09:25:10 +000051 Tcl_Obj *pScript; /* Script to execute */
danc7991bd2010-05-05 19:04:59 +000052 int nScript; /* Number of elements in array apScript */
dan1f55e282010-06-03 09:25:10 +000053 Tcl_Obj **apScript; /* Array version of pScript */
danc7991bd2010-05-05 19:04:59 +000054 TestvfsBuffer *pBuffer; /* List of shared buffers */
dan7fd555a2010-05-13 06:19:37 +000055 int isNoshm;
dan0235ab92010-06-01 19:15:18 +000056
57 int mask;
58 int iIoerrCnt;
59 int ioerr;
dan1f55e282010-06-03 09:25:10 +000060 int nIoerrFail;
dan2a321c72010-06-16 19:04:23 +000061
dan146ed782010-06-19 17:26:37 +000062 int iFullCnt;
63 int fullerr;
64 int nFullFail;
65
dan2a321c72010-06-16 19:04:23 +000066 int iDevchar;
67 int iSectorsize;
danc7991bd2010-05-05 19:04:59 +000068};
69
70/*
dan1f55e282010-06-03 09:25:10 +000071** The Testvfs.mask variable is set to a combination of the following.
72** If a bit is clear in Testvfs.mask, then calls made by SQLite to the
73** corresponding VFS method is ignored for purposes of:
74**
75** + Simulating IO errors, and
76** + Invoking the Tcl callback script.
77*/
78#define TESTVFS_SHMOPEN_MASK 0x00000001
dan1f55e282010-06-03 09:25:10 +000079#define TESTVFS_SHMLOCK_MASK 0x00000010
drh6b017cc2010-06-14 18:01:46 +000080#define TESTVFS_SHMMAP_MASK 0x00000020
81#define TESTVFS_SHMBARRIER_MASK 0x00000040
82#define TESTVFS_SHMCLOSE_MASK 0x00000080
dan1f55e282010-06-03 09:25:10 +000083
dan13a3cb82010-06-11 19:04:21 +000084#define TESTVFS_OPEN_MASK 0x00000100
85#define TESTVFS_SYNC_MASK 0x00000200
danb0ac3e32010-06-16 10:55:42 +000086#define TESTVFS_DELETE_MASK 0x00000400
dan2a321c72010-06-16 19:04:23 +000087#define TESTVFS_CLOSE_MASK 0x00000800
88#define TESTVFS_WRITE_MASK 0x00001000
89#define TESTVFS_TRUNCATE_MASK 0x00002000
90#define TESTVFS_ALL_MASK 0x00003FFF
dan13a3cb82010-06-11 19:04:21 +000091
92
93#define TESTVFS_MAX_PAGES 256
dan1f55e282010-06-03 09:25:10 +000094
95/*
danef4ee8f2010-06-05 11:53:34 +000096** A shared-memory buffer. There is one of these objects for each shared
97** memory region opened by clients. If two clients open the same file,
98** there are two TestvfsFile structures but only one TestvfsBuffer structure.
danc7991bd2010-05-05 19:04:59 +000099*/
100struct TestvfsBuffer {
101 char *zFile; /* Associated file name */
dan13a3cb82010-06-11 19:04:21 +0000102 int pgsz; /* Page size */
103 u8 *aPage[TESTVFS_MAX_PAGES]; /* Array of ckalloc'd pages */
danef4ee8f2010-06-05 11:53:34 +0000104 TestvfsFile *pFile; /* List of open handles */
danc7991bd2010-05-05 19:04:59 +0000105 TestvfsBuffer *pNext; /* Next in linked list of all buffers */
106};
107
danc7991bd2010-05-05 19:04:59 +0000108
109#define PARENTVFS(x) (((Testvfs *)((x)->pAppData))->pParent)
110
dan1f55e282010-06-03 09:25:10 +0000111#define TESTVFS_MAX_ARGS 12
112
danc7991bd2010-05-05 19:04:59 +0000113
114/*
dan7fd555a2010-05-13 06:19:37 +0000115** Method declarations for TestvfsFile.
danc7991bd2010-05-05 19:04:59 +0000116*/
117static int tvfsClose(sqlite3_file*);
118static int tvfsRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
119static int tvfsWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
120static int tvfsTruncate(sqlite3_file*, sqlite3_int64 size);
121static int tvfsSync(sqlite3_file*, int flags);
122static int tvfsFileSize(sqlite3_file*, sqlite3_int64 *pSize);
123static int tvfsLock(sqlite3_file*, int);
124static int tvfsUnlock(sqlite3_file*, int);
125static int tvfsCheckReservedLock(sqlite3_file*, int *);
126static int tvfsFileControl(sqlite3_file*, int op, void *pArg);
127static int tvfsSectorSize(sqlite3_file*);
128static int tvfsDeviceCharacteristics(sqlite3_file*);
129
130/*
131** Method declarations for tvfs_vfs.
132*/
133static int tvfsOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
134static int tvfsDelete(sqlite3_vfs*, const char *zName, int syncDir);
135static int tvfsAccess(sqlite3_vfs*, const char *zName, int flags, int *);
136static int tvfsFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
137#ifndef SQLITE_OMIT_LOAD_EXTENSION
138static void *tvfsDlOpen(sqlite3_vfs*, const char *zFilename);
139static void tvfsDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
140static void (*tvfsDlSym(sqlite3_vfs*,void*, const char *zSymbol))(void);
141static void tvfsDlClose(sqlite3_vfs*, void*);
142#endif /* SQLITE_OMIT_LOAD_EXTENSION */
143static int tvfsRandomness(sqlite3_vfs*, int nByte, char *zOut);
144static int tvfsSleep(sqlite3_vfs*, int microseconds);
145static int tvfsCurrentTime(sqlite3_vfs*, double*);
146
drhd9e5c4f2010-05-12 18:01:39 +0000147static int tvfsShmOpen(sqlite3_file*);
drh73b64e42010-05-30 19:55:15 +0000148static int tvfsShmLock(sqlite3_file*, int , int, int);
drh6b017cc2010-06-14 18:01:46 +0000149static int tvfsShmMap(sqlite3_file*,int,int,int, void volatile **);
drh286a2882010-05-20 23:51:06 +0000150static void tvfsShmBarrier(sqlite3_file*);
drhd9e5c4f2010-05-12 18:01:39 +0000151static int tvfsShmClose(sqlite3_file*, int);
danc7991bd2010-05-05 19:04:59 +0000152
153static sqlite3_io_methods tvfs_io_methods = {
drhd9e5c4f2010-05-12 18:01:39 +0000154 2, /* iVersion */
danc7991bd2010-05-05 19:04:59 +0000155 tvfsClose, /* xClose */
156 tvfsRead, /* xRead */
157 tvfsWrite, /* xWrite */
158 tvfsTruncate, /* xTruncate */
159 tvfsSync, /* xSync */
160 tvfsFileSize, /* xFileSize */
161 tvfsLock, /* xLock */
162 tvfsUnlock, /* xUnlock */
163 tvfsCheckReservedLock, /* xCheckReservedLock */
164 tvfsFileControl, /* xFileControl */
165 tvfsSectorSize, /* xSectorSize */
drhd9e5c4f2010-05-12 18:01:39 +0000166 tvfsDeviceCharacteristics, /* xDeviceCharacteristics */
167 tvfsShmOpen, /* xShmOpen */
drhd9e5c4f2010-05-12 18:01:39 +0000168 tvfsShmLock, /* xShmLock */
drh6b017cc2010-06-14 18:01:46 +0000169 tvfsShmMap, /* xShmMap */
drh286a2882010-05-20 23:51:06 +0000170 tvfsShmBarrier, /* xShmBarrier */
drh6b017cc2010-06-14 18:01:46 +0000171 tvfsShmClose /* xShmClose */
danc7991bd2010-05-05 19:04:59 +0000172};
173
dand764c7d2010-06-04 11:56:22 +0000174static int tvfsResultCode(Testvfs *p, int *pRc){
175 struct errcode {
176 int eCode;
177 const char *zCode;
178 } aCode[] = {
179 { SQLITE_OK, "SQLITE_OK" },
180 { SQLITE_ERROR, "SQLITE_ERROR" },
181 { SQLITE_IOERR, "SQLITE_IOERR" },
182 { SQLITE_LOCKED, "SQLITE_LOCKED" },
183 { SQLITE_BUSY, "SQLITE_BUSY" },
184 };
185
186 const char *z;
187 int i;
188
189 z = Tcl_GetStringResult(p->interp);
190 for(i=0; i<ArraySize(aCode); i++){
191 if( 0==strcmp(z, aCode[i].zCode) ){
192 *pRc = aCode[i].eCode;
193 return 1;
194 }
195 }
196
197 return 0;
198}
199
dan146ed782010-06-19 17:26:37 +0000200static int tvfsInjectIoerr(Testvfs *p){
201 int ret = 0;
202 if( p->ioerr ){
203 p->iIoerrCnt--;
204 if( p->iIoerrCnt==0 || (p->iIoerrCnt<0 && p->ioerr==2) ){
205 ret = 1;
206 p->nIoerrFail++;
207 }
208 }
209 return ret;
210}
211
212static int tvfsInjectFullerr(Testvfs *p){
213 int ret = 0;
214 if( p->fullerr ){
215 p->iFullCnt--;
216 if( p->iFullCnt<=0 ){
217 ret = 1;
218 p->nFullFail++;
219 }
220 }
221 return ret;
222}
223
dand764c7d2010-06-04 11:56:22 +0000224
225static void tvfsExecTcl(
226 Testvfs *p,
227 const char *zMethod,
228 Tcl_Obj *arg1,
229 Tcl_Obj *arg2,
230 Tcl_Obj *arg3
231){
232 int rc; /* Return code from Tcl_EvalObj() */
233 int nArg; /* Elements in eval'd list */
234 int nScript;
235 Tcl_Obj ** ap;
236
237 assert( p->pScript );
238
239 if( !p->apScript ){
240 int nByte;
241 int i;
242 if( TCL_OK!=Tcl_ListObjGetElements(p->interp, p->pScript, &nScript, &ap) ){
243 Tcl_BackgroundError(p->interp);
244 Tcl_ResetResult(p->interp);
245 return;
246 }
247 p->nScript = nScript;
248 nByte = (nScript+TESTVFS_MAX_ARGS)*sizeof(Tcl_Obj *);
249 p->apScript = (Tcl_Obj **)ckalloc(nByte);
250 memset(p->apScript, 0, nByte);
251 for(i=0; i<nScript; i++){
252 p->apScript[i] = ap[i];
253 }
254 }
255
256 p->apScript[p->nScript] = Tcl_NewStringObj(zMethod, -1);
257 p->apScript[p->nScript+1] = arg1;
258 p->apScript[p->nScript+2] = arg2;
259 p->apScript[p->nScript+3] = arg3;
260
261 for(nArg=p->nScript; p->apScript[nArg]; nArg++){
262 Tcl_IncrRefCount(p->apScript[nArg]);
263 }
264
265 rc = Tcl_EvalObjv(p->interp, nArg, p->apScript, TCL_EVAL_GLOBAL);
266 if( rc!=TCL_OK ){
267 Tcl_BackgroundError(p->interp);
268 Tcl_ResetResult(p->interp);
269 }
270
271 for(nArg=p->nScript; p->apScript[nArg]; nArg++){
272 Tcl_DecrRefCount(p->apScript[nArg]);
273 p->apScript[nArg] = 0;
274 }
275}
276
277
danc7991bd2010-05-05 19:04:59 +0000278/*
279** Close an tvfs-file.
280*/
281static int tvfsClose(sqlite3_file *pFile){
dan2a321c72010-06-16 19:04:23 +0000282 TestvfsFile *pFd = (TestvfsFile *)pFile;
283 Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
284
285 if( p->pScript && p->mask&TESTVFS_CLOSE_MASK ){
286 tvfsExecTcl(p, "xClose",
287 Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0
288 );
289 }
290
291 if( pFd->pShmId ){
292 Tcl_DecrRefCount(pFd->pShmId);
293 pFd->pShmId = 0;
dan7fd555a2010-05-13 06:19:37 +0000294 }
295 if( pFile->pMethods ){
296 ckfree((char *)pFile->pMethods);
297 }
dan2a321c72010-06-16 19:04:23 +0000298 return sqlite3OsClose(pFd->pReal);
danc7991bd2010-05-05 19:04:59 +0000299}
300
301/*
302** Read data from an tvfs-file.
303*/
304static int tvfsRead(
305 sqlite3_file *pFile,
306 void *zBuf,
307 int iAmt,
308 sqlite_int64 iOfst
309){
dan7fd555a2010-05-13 06:19:37 +0000310 TestvfsFile *p = (TestvfsFile *)pFile;
danc7991bd2010-05-05 19:04:59 +0000311 return sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst);
312}
313
314/*
315** Write data to an tvfs-file.
316*/
317static int tvfsWrite(
318 sqlite3_file *pFile,
319 const void *zBuf,
320 int iAmt,
321 sqlite_int64 iOfst
322){
dan2a321c72010-06-16 19:04:23 +0000323 int rc = SQLITE_OK;
324 TestvfsFile *pFd = (TestvfsFile *)pFile;
325 Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
326
327 if( p->pScript && p->mask&TESTVFS_WRITE_MASK ){
328 tvfsExecTcl(p, "xWrite",
329 Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0
330 );
331 tvfsResultCode(p, &rc);
332 }
dan146ed782010-06-19 17:26:37 +0000333
334 if( rc==SQLITE_OK && tvfsInjectFullerr(p) ) rc = SQLITE_FULL;
dan2a321c72010-06-16 19:04:23 +0000335
336 if( rc==SQLITE_OK ){
337 rc = sqlite3OsWrite(pFd->pReal, zBuf, iAmt, iOfst);
338 }
339 return rc;
danc7991bd2010-05-05 19:04:59 +0000340}
341
342/*
343** Truncate an tvfs-file.
344*/
345static int tvfsTruncate(sqlite3_file *pFile, sqlite_int64 size){
dan2a321c72010-06-16 19:04:23 +0000346 int rc = SQLITE_OK;
347 TestvfsFile *pFd = (TestvfsFile *)pFile;
348 Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
349
350 if( p->pScript && p->mask&TESTVFS_TRUNCATE_MASK ){
351 tvfsExecTcl(p, "xTruncate",
352 Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0
353 );
354 tvfsResultCode(p, &rc);
355 }
356
357 if( rc==SQLITE_OK ){
358 rc = sqlite3OsTruncate(pFd->pReal, size);
359 }
360 return rc;
danc7991bd2010-05-05 19:04:59 +0000361}
362
363/*
364** Sync an tvfs-file.
365*/
366static int tvfsSync(sqlite3_file *pFile, int flags){
dand764c7d2010-06-04 11:56:22 +0000367 int rc = SQLITE_OK;
368 TestvfsFile *pFd = (TestvfsFile *)pFile;
369 Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
370
371 if( p->pScript && p->mask&TESTVFS_SYNC_MASK ){
372 char *zFlags;
373
374 switch( flags ){
375 case SQLITE_SYNC_NORMAL:
376 zFlags = "normal";
377 break;
378 case SQLITE_SYNC_FULL:
379 zFlags = "full";
380 break;
381 case SQLITE_SYNC_NORMAL|SQLITE_SYNC_DATAONLY:
382 zFlags = "normal|dataonly";
383 break;
384 case SQLITE_SYNC_FULL|SQLITE_SYNC_DATAONLY:
385 zFlags = "full|dataonly";
386 break;
387 default:
388 assert(0);
389 }
390
391 tvfsExecTcl(p, "xSync",
392 Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId,
393 Tcl_NewStringObj(zFlags, -1)
394 );
395 tvfsResultCode(p, &rc);
396 }
397
dan146ed782010-06-19 17:26:37 +0000398 if( rc==SQLITE_OK && tvfsInjectFullerr(p) ) rc = SQLITE_FULL;
399
dand764c7d2010-06-04 11:56:22 +0000400 if( rc==SQLITE_OK ){
401 rc = sqlite3OsSync(pFd->pReal, flags);
402 }
403
404 return rc;
danc7991bd2010-05-05 19:04:59 +0000405}
406
407/*
408** Return the current file-size of an tvfs-file.
409*/
410static int tvfsFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
dan7fd555a2010-05-13 06:19:37 +0000411 TestvfsFile *p = (TestvfsFile *)pFile;
danc7991bd2010-05-05 19:04:59 +0000412 return sqlite3OsFileSize(p->pReal, pSize);
413}
414
415/*
416** Lock an tvfs-file.
417*/
418static int tvfsLock(sqlite3_file *pFile, int eLock){
dan7fd555a2010-05-13 06:19:37 +0000419 TestvfsFile *p = (TestvfsFile *)pFile;
danc7991bd2010-05-05 19:04:59 +0000420 return sqlite3OsLock(p->pReal, eLock);
421}
422
423/*
424** Unlock an tvfs-file.
425*/
426static int tvfsUnlock(sqlite3_file *pFile, int eLock){
dan7fd555a2010-05-13 06:19:37 +0000427 TestvfsFile *p = (TestvfsFile *)pFile;
danc7991bd2010-05-05 19:04:59 +0000428 return sqlite3OsUnlock(p->pReal, eLock);
429}
430
431/*
432** Check if another file-handle holds a RESERVED lock on an tvfs-file.
433*/
434static int tvfsCheckReservedLock(sqlite3_file *pFile, int *pResOut){
dan7fd555a2010-05-13 06:19:37 +0000435 TestvfsFile *p = (TestvfsFile *)pFile;
danc7991bd2010-05-05 19:04:59 +0000436 return sqlite3OsCheckReservedLock(p->pReal, pResOut);
437}
438
439/*
440** File control method. For custom operations on an tvfs-file.
441*/
442static int tvfsFileControl(sqlite3_file *pFile, int op, void *pArg){
dan7fd555a2010-05-13 06:19:37 +0000443 TestvfsFile *p = (TestvfsFile *)pFile;
danc7991bd2010-05-05 19:04:59 +0000444 return sqlite3OsFileControl(p->pReal, op, pArg);
445}
446
447/*
448** Return the sector-size in bytes for an tvfs-file.
449*/
450static int tvfsSectorSize(sqlite3_file *pFile){
dan2a321c72010-06-16 19:04:23 +0000451 TestvfsFile *pFd = (TestvfsFile *)pFile;
452 Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
453 if( p->iSectorsize>=0 ){
454 return p->iSectorsize;
455 }
456 return sqlite3OsSectorSize(pFd->pReal);
danc7991bd2010-05-05 19:04:59 +0000457}
458
459/*
460** Return the device characteristic flags supported by an tvfs-file.
461*/
462static int tvfsDeviceCharacteristics(sqlite3_file *pFile){
dan2a321c72010-06-16 19:04:23 +0000463 TestvfsFile *pFd = (TestvfsFile *)pFile;
464 Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
465 if( p->iDevchar>=0 ){
466 return p->iDevchar;
467 }
468 return sqlite3OsDeviceCharacteristics(pFd->pReal);
danc7991bd2010-05-05 19:04:59 +0000469}
470
471/*
472** Open an tvfs file handle.
473*/
474static int tvfsOpen(
475 sqlite3_vfs *pVfs,
476 const char *zName,
477 sqlite3_file *pFile,
478 int flags,
479 int *pOutFlags
480){
481 int rc;
dand764c7d2010-06-04 11:56:22 +0000482 TestvfsFile *pFd = (TestvfsFile *)pFile;
483 Tcl_Obj *pId = 0;
484 Testvfs *p = (Testvfs *)pVfs->pAppData;
485
486 pFd->pShm = 0;
487 pFd->pShmId = 0;
488 pFd->zFilename = zName;
489 pFd->pVfs = pVfs;
490 pFd->pReal = (sqlite3_file *)&pFd[1];
491
492 /* Evaluate the Tcl script:
493 **
494 ** SCRIPT xOpen FILENAME
495 **
496 ** If the script returns an SQLite error code other than SQLITE_OK, an
497 ** error is returned to the caller. If it returns SQLITE_OK, the new
498 ** connection is named "anon". Otherwise, the value returned by the
499 ** script is used as the connection name.
500 */
501 Tcl_ResetResult(p->interp);
502 if( p->pScript && p->mask&TESTVFS_OPEN_MASK ){
503 tvfsExecTcl(p, "xOpen", Tcl_NewStringObj(pFd->zFilename, -1), 0, 0);
504 if( tvfsResultCode(p, &rc) ){
505 if( rc!=SQLITE_OK ) return rc;
506 }else{
507 pId = Tcl_GetObjResult(p->interp);
508 }
509 }
510 if( !pId ){
511 pId = Tcl_NewStringObj("anon", -1);
512 }
513 Tcl_IncrRefCount(pId);
514 pFd->pShmId = pId;
515 Tcl_ResetResult(p->interp);
516
517 rc = sqlite3OsOpen(PARENTVFS(pVfs), zName, pFd->pReal, flags, pOutFlags);
518 if( pFd->pReal->pMethods ){
dan7fd555a2010-05-13 06:19:37 +0000519 sqlite3_io_methods *pMethods;
520 pMethods = (sqlite3_io_methods *)ckalloc(sizeof(sqlite3_io_methods));
521 memcpy(pMethods, &tvfs_io_methods, sizeof(sqlite3_io_methods));
522 if( ((Testvfs *)pVfs->pAppData)->isNoshm ){
523 pMethods->xShmOpen = 0;
dan7fd555a2010-05-13 06:19:37 +0000524 pMethods->xShmClose = 0;
525 pMethods->xShmLock = 0;
drh286a2882010-05-20 23:51:06 +0000526 pMethods->xShmBarrier = 0;
dan18801912010-06-14 14:07:50 +0000527 pMethods->xShmMap = 0;
dan7fd555a2010-05-13 06:19:37 +0000528 }
529 pFile->pMethods = pMethods;
danc7991bd2010-05-05 19:04:59 +0000530 }
dan7fd555a2010-05-13 06:19:37 +0000531
danc7991bd2010-05-05 19:04:59 +0000532 return rc;
533}
534
535/*
536** Delete the file located at zPath. If the dirSync argument is true,
537** ensure the file-system modifications are synced to disk before
538** returning.
539*/
540static int tvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
danb0ac3e32010-06-16 10:55:42 +0000541 int rc = SQLITE_OK;
542 Testvfs *p = (Testvfs *)pVfs->pAppData;
543
544 if( p->pScript && p->mask&TESTVFS_DELETE_MASK ){
545 tvfsExecTcl(p, "xDelete",
546 Tcl_NewStringObj(zPath, -1), Tcl_NewIntObj(dirSync), 0
547 );
548 tvfsResultCode(p, &rc);
549 }
550 if( rc==SQLITE_OK ){
551 rc = sqlite3OsDelete(PARENTVFS(pVfs), zPath, dirSync);
552 }
553 return rc;
danc7991bd2010-05-05 19:04:59 +0000554}
555
556/*
557** Test for access permissions. Return true if the requested permission
558** is available, or false otherwise.
559*/
560static int tvfsAccess(
561 sqlite3_vfs *pVfs,
562 const char *zPath,
563 int flags,
564 int *pResOut
565){
566 return sqlite3OsAccess(PARENTVFS(pVfs), zPath, flags, pResOut);
567}
568
569/*
570** Populate buffer zOut with the full canonical pathname corresponding
571** to the pathname in zPath. zOut is guaranteed to point to a buffer
572** of at least (DEVSYM_MAX_PATHNAME+1) bytes.
573*/
574static int tvfsFullPathname(
575 sqlite3_vfs *pVfs,
576 const char *zPath,
577 int nOut,
578 char *zOut
579){
580 return sqlite3OsFullPathname(PARENTVFS(pVfs), zPath, nOut, zOut);
581}
582
583#ifndef SQLITE_OMIT_LOAD_EXTENSION
584/*
585** Open the dynamic library located at zPath and return a handle.
586*/
587static void *tvfsDlOpen(sqlite3_vfs *pVfs, const char *zPath){
588 return sqlite3OsDlOpen(PARENTVFS(pVfs), zPath);
589}
590
591/*
592** Populate the buffer zErrMsg (size nByte bytes) with a human readable
593** utf-8 string describing the most recent error encountered associated
594** with dynamic libraries.
595*/
596static void tvfsDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
597 sqlite3OsDlError(PARENTVFS(pVfs), nByte, zErrMsg);
598}
599
600/*
601** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
602*/
603static void (*tvfsDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
604 return sqlite3OsDlSym(PARENTVFS(pVfs), p, zSym);
605}
606
607/*
608** Close the dynamic library handle pHandle.
609*/
610static void tvfsDlClose(sqlite3_vfs *pVfs, void *pHandle){
611 sqlite3OsDlClose(PARENTVFS(pVfs), pHandle);
612}
613#endif /* SQLITE_OMIT_LOAD_EXTENSION */
614
615/*
616** Populate the buffer pointed to by zBufOut with nByte bytes of
617** random data.
618*/
619static int tvfsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
620 return sqlite3OsRandomness(PARENTVFS(pVfs), nByte, zBufOut);
621}
622
623/*
624** Sleep for nMicro microseconds. Return the number of microseconds
625** actually slept.
626*/
627static int tvfsSleep(sqlite3_vfs *pVfs, int nMicro){
628 return sqlite3OsSleep(PARENTVFS(pVfs), nMicro);
629}
630
631/*
632** Return the current time as a Julian Day number in *pTimeOut.
633*/
634static int tvfsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
635 return PARENTVFS(pVfs)->xCurrentTime(PARENTVFS(pVfs), pTimeOut);
636}
637
danc7991bd2010-05-05 19:04:59 +0000638static int tvfsShmOpen(
drhd9e5c4f2010-05-12 18:01:39 +0000639 sqlite3_file *pFileDes
danc7991bd2010-05-05 19:04:59 +0000640){
dan7fd555a2010-05-13 06:19:37 +0000641 Testvfs *p;
danc7991bd2010-05-05 19:04:59 +0000642 int rc = SQLITE_OK; /* Return code */
danc7991bd2010-05-05 19:04:59 +0000643 TestvfsBuffer *pBuffer; /* Buffer to open connection to */
dan7fd555a2010-05-13 06:19:37 +0000644 TestvfsFile *pFd; /* The testvfs file structure */
drhd9e5c4f2010-05-12 18:01:39 +0000645
dan7fd555a2010-05-13 06:19:37 +0000646 pFd = (TestvfsFile*)pFileDes;
647 p = (Testvfs *)pFd->pVfs->pAppData;
danef4ee8f2010-06-05 11:53:34 +0000648 assert( pFd->pShmId && pFd->pShm==0 && pFd->pNext==0 );
danc7991bd2010-05-05 19:04:59 +0000649
650 /* Evaluate the Tcl script:
651 **
652 ** SCRIPT xShmOpen FILENAME
danc7991bd2010-05-05 19:04:59 +0000653 */
dan0235ab92010-06-01 19:15:18 +0000654 Tcl_ResetResult(p->interp);
dan1f55e282010-06-03 09:25:10 +0000655 if( p->pScript && p->mask&TESTVFS_SHMOPEN_MASK ){
dan0235ab92010-06-01 19:15:18 +0000656 tvfsExecTcl(p, "xShmOpen", Tcl_NewStringObj(pFd->zFilename, -1), 0, 0);
dan1f55e282010-06-03 09:25:10 +0000657 if( tvfsResultCode(p, &rc) ){
658 if( rc!=SQLITE_OK ) return rc;
dan1f55e282010-06-03 09:25:10 +0000659 }
dan0235ab92010-06-01 19:15:18 +0000660 }
dan1f55e282010-06-03 09:25:10 +0000661
662 assert( rc==SQLITE_OK );
663 if( p->mask&TESTVFS_SHMOPEN_MASK && tvfsInjectIoerr(p) ){
dan1f55e282010-06-03 09:25:10 +0000664 return SQLITE_IOERR;
665 }
666
danc7991bd2010-05-05 19:04:59 +0000667 /* Search for a TestvfsBuffer. Create a new one if required. */
668 for(pBuffer=p->pBuffer; pBuffer; pBuffer=pBuffer->pNext){
dan7fd555a2010-05-13 06:19:37 +0000669 if( 0==strcmp(pFd->zFilename, pBuffer->zFile) ) break;
danc7991bd2010-05-05 19:04:59 +0000670 }
671 if( !pBuffer ){
dan7fd555a2010-05-13 06:19:37 +0000672 int nByte = sizeof(TestvfsBuffer) + strlen(pFd->zFilename) + 1;
danc7991bd2010-05-05 19:04:59 +0000673 pBuffer = (TestvfsBuffer *)ckalloc(nByte);
674 memset(pBuffer, 0, nByte);
675 pBuffer->zFile = (char *)&pBuffer[1];
dan7fd555a2010-05-13 06:19:37 +0000676 strcpy(pBuffer->zFile, pFd->zFilename);
danc7991bd2010-05-05 19:04:59 +0000677 pBuffer->pNext = p->pBuffer;
678 p->pBuffer = pBuffer;
679 }
680
681 /* Connect the TestvfsBuffer to the new TestvfsShm handle and return. */
danef4ee8f2010-06-05 11:53:34 +0000682 pFd->pNext = pBuffer->pFile;
683 pBuffer->pFile = pFd;
dan7fd555a2010-05-13 06:19:37 +0000684 pFd->pShm = pBuffer;
danc7991bd2010-05-05 19:04:59 +0000685 return SQLITE_OK;
686}
687
dan13a3cb82010-06-11 19:04:21 +0000688static void tvfsAllocPage(TestvfsBuffer *p, int iPage, int pgsz){
689 assert( iPage<TESTVFS_MAX_PAGES );
690 if( p->aPage[iPage]==0 ){
dan4280eb32010-06-12 12:02:35 +0000691 p->aPage[iPage] = (u8 *)ckalloc(pgsz);
dan13a3cb82010-06-11 19:04:21 +0000692 memset(p->aPage[iPage], 0, pgsz);
693 p->pgsz = pgsz;
694 }
695}
696
drh6b017cc2010-06-14 18:01:46 +0000697static int tvfsShmMap(
dan13a3cb82010-06-11 19:04:21 +0000698 sqlite3_file *pFile, /* Handle open on database file */
699 int iPage, /* Page to retrieve */
700 int pgsz, /* Size of pages */
701 int isWrite, /* True to extend file if necessary */
702 void volatile **pp /* OUT: Mapped memory */
703){
danc7991bd2010-05-05 19:04:59 +0000704 int rc = SQLITE_OK;
dan7fd555a2010-05-13 06:19:37 +0000705 TestvfsFile *pFd = (TestvfsFile *)pFile;
706 Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);
danc7991bd2010-05-05 19:04:59 +0000707
drh6b017cc2010-06-14 18:01:46 +0000708 if( p->pScript && p->mask&TESTVFS_SHMMAP_MASK ){
dan13a3cb82010-06-11 19:04:21 +0000709 Tcl_Obj *pArg = Tcl_NewObj();
dan5d656852010-06-14 07:53:26 +0000710 Tcl_IncrRefCount(pArg);
dan13a3cb82010-06-11 19:04:21 +0000711 Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(iPage));
712 Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(pgsz));
713 Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(isWrite));
drh6b017cc2010-06-14 18:01:46 +0000714 tvfsExecTcl(p, "xShmMap",
dan13a3cb82010-06-11 19:04:21 +0000715 Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, pArg
dan0235ab92010-06-01 19:15:18 +0000716 );
717 tvfsResultCode(p, &rc);
dan5d656852010-06-14 07:53:26 +0000718 Tcl_DecrRefCount(pArg);
dan0235ab92010-06-01 19:15:18 +0000719 }
drh6b017cc2010-06-14 18:01:46 +0000720 if( rc==SQLITE_OK && p->mask&TESTVFS_SHMMAP_MASK && tvfsInjectIoerr(p) ){
dan1f55e282010-06-03 09:25:10 +0000721 rc = SQLITE_IOERR;
722 }
dand03f5232010-06-03 19:10:08 +0000723
dan13a3cb82010-06-11 19:04:21 +0000724 if( rc==SQLITE_OK && isWrite && !pFd->pShm->aPage[iPage] ){
725 tvfsAllocPage(pFd->pShm, iPage, pgsz);
dan0235ab92010-06-01 19:15:18 +0000726 }
dan13a3cb82010-06-11 19:04:21 +0000727 *pp = (void volatile *)pFd->pShm->aPage[iPage];
danc7991bd2010-05-05 19:04:59 +0000728
729 return rc;
730}
731
dan13a3cb82010-06-11 19:04:21 +0000732
danc7991bd2010-05-05 19:04:59 +0000733static int tvfsShmLock(
dan7fd555a2010-05-13 06:19:37 +0000734 sqlite3_file *pFile,
drh73b64e42010-05-30 19:55:15 +0000735 int ofst,
736 int n,
737 int flags
danc7991bd2010-05-05 19:04:59 +0000738){
739 int rc = SQLITE_OK;
dan7fd555a2010-05-13 06:19:37 +0000740 TestvfsFile *pFd = (TestvfsFile *)pFile;
741 Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);
drh73b64e42010-05-30 19:55:15 +0000742 int nLock;
743 char zLock[80];
danc7991bd2010-05-05 19:04:59 +0000744
dan1f55e282010-06-03 09:25:10 +0000745 if( p->pScript && p->mask&TESTVFS_SHMLOCK_MASK ){
dan0235ab92010-06-01 19:15:18 +0000746 sqlite3_snprintf(sizeof(zLock), zLock, "%d %d", ofst, n);
747 nLock = strlen(zLock);
748 if( flags & SQLITE_SHM_LOCK ){
749 strcpy(&zLock[nLock], " lock");
750 }else{
751 strcpy(&zLock[nLock], " unlock");
752 }
753 nLock += strlen(&zLock[nLock]);
754 if( flags & SQLITE_SHM_SHARED ){
755 strcpy(&zLock[nLock], " shared");
756 }else{
757 strcpy(&zLock[nLock], " exclusive");
758 }
759 tvfsExecTcl(p, "xShmLock",
760 Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId,
761 Tcl_NewStringObj(zLock, -1)
762 );
763 tvfsResultCode(p, &rc);
drh73b64e42010-05-30 19:55:15 +0000764 }
dan961ff452010-06-03 18:20:19 +0000765
766 if( rc==SQLITE_OK && p->mask&TESTVFS_SHMLOCK_MASK && tvfsInjectIoerr(p) ){
767 rc = SQLITE_IOERR;
768 }
danef4ee8f2010-06-05 11:53:34 +0000769
770 if( rc==SQLITE_OK ){
771 int isLock = (flags & SQLITE_SHM_LOCK);
772 int isExcl = (flags & SQLITE_SHM_EXCLUSIVE);
773 u32 mask = (((1<<n)-1) << ofst);
774 if( isLock ){
775 TestvfsFile *p2;
776 for(p2=pFd->pShm->pFile; p2; p2=p2->pNext){
777 if( p2==pFd ) continue;
778 if( (p2->excllock&mask) || (isExcl && p2->sharedlock&mask) ){
779 rc = SQLITE_BUSY;
780 break;
781 }
782 }
783 if( rc==SQLITE_OK ){
784 if( isExcl ) pFd->excllock |= mask;
785 if( !isExcl ) pFd->sharedlock |= mask;
786 }
787 }else{
788 if( isExcl ) pFd->excllock &= (~mask);
789 if( !isExcl ) pFd->sharedlock &= (~mask);
790 }
791 }
792
danc7991bd2010-05-05 19:04:59 +0000793 return rc;
794}
795
drh286a2882010-05-20 23:51:06 +0000796static void tvfsShmBarrier(sqlite3_file *pFile){
drh286a2882010-05-20 23:51:06 +0000797 TestvfsFile *pFd = (TestvfsFile *)pFile;
798 Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);
799
dan1f55e282010-06-03 09:25:10 +0000800 if( p->pScript && p->mask&TESTVFS_SHMBARRIER_MASK ){
dan0235ab92010-06-01 19:15:18 +0000801 tvfsExecTcl(p, "xShmBarrier",
802 Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0
803 );
804 }
drh286a2882010-05-20 23:51:06 +0000805}
806
danc7991bd2010-05-05 19:04:59 +0000807static int tvfsShmClose(
dan7fd555a2010-05-13 06:19:37 +0000808 sqlite3_file *pFile,
danc7991bd2010-05-05 19:04:59 +0000809 int deleteFlag
810){
811 int rc = SQLITE_OK;
dan7fd555a2010-05-13 06:19:37 +0000812 TestvfsFile *pFd = (TestvfsFile *)pFile;
813 Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);
814 TestvfsBuffer *pBuffer = pFd->pShm;
danef4ee8f2010-06-05 11:53:34 +0000815 TestvfsFile **ppFd;
danc7991bd2010-05-05 19:04:59 +0000816
dan7fd555a2010-05-13 06:19:37 +0000817 assert( pFd->pShmId && pFd->pShm );
danc7991bd2010-05-05 19:04:59 +0000818
dan1f55e282010-06-03 09:25:10 +0000819 if( p->pScript && p->mask&TESTVFS_SHMCLOSE_MASK ){
dan0235ab92010-06-01 19:15:18 +0000820 tvfsExecTcl(p, "xShmClose",
821 Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0
822 );
823 tvfsResultCode(p, &rc);
824 }
danc7991bd2010-05-05 19:04:59 +0000825
danef4ee8f2010-06-05 11:53:34 +0000826 for(ppFd=&pBuffer->pFile; *ppFd!=pFd; ppFd=&((*ppFd)->pNext));
827 assert( (*ppFd)==pFd );
828 *ppFd = pFd->pNext;
829
830 if( pBuffer->pFile==0 ){
dan13a3cb82010-06-11 19:04:21 +0000831 int i;
danc7991bd2010-05-05 19:04:59 +0000832 TestvfsBuffer **pp;
833 for(pp=&p->pBuffer; *pp!=pBuffer; pp=&((*pp)->pNext));
834 *pp = (*pp)->pNext;
dan13a3cb82010-06-11 19:04:21 +0000835 for(i=0; pBuffer->aPage[i]; i++){
836 ckfree((char *)pBuffer->aPage[i]);
837 }
danc7991bd2010-05-05 19:04:59 +0000838 ckfree((char *)pBuffer);
839 }
dan7fd555a2010-05-13 06:19:37 +0000840 pFd->pShm = 0;
danc7991bd2010-05-05 19:04:59 +0000841
842 return rc;
843}
844
845static int testvfs_obj_cmd(
846 ClientData cd,
847 Tcl_Interp *interp,
848 int objc,
849 Tcl_Obj *CONST objv[]
850){
851 Testvfs *p = (Testvfs *)cd;
852
dan1f55e282010-06-03 09:25:10 +0000853 enum DB_enum {
dan2a321c72010-06-16 19:04:23 +0000854 CMD_SHM, CMD_DELETE, CMD_FILTER, CMD_IOERR, CMD_SCRIPT,
dan146ed782010-06-19 17:26:37 +0000855 CMD_DEVCHAR, CMD_SECTORSIZE, CMD_FULLERR
dan1f55e282010-06-03 09:25:10 +0000856 };
dan2a321c72010-06-16 19:04:23 +0000857 struct TestvfsSubcmd {
858 char *zName;
859 enum DB_enum eCmd;
860 } aSubcmd[] = {
861 { "shm", CMD_SHM },
862 { "delete", CMD_DELETE },
863 { "filter", CMD_FILTER },
864 { "ioerr", CMD_IOERR },
dan146ed782010-06-19 17:26:37 +0000865 { "fullerr", CMD_FULLERR },
dan2a321c72010-06-16 19:04:23 +0000866 { "script", CMD_SCRIPT },
867 { "devchar", CMD_DEVCHAR },
868 { "sectorsize", CMD_SECTORSIZE },
869 { 0, 0 }
870 };
danc7991bd2010-05-05 19:04:59 +0000871 int i;
872
873 if( objc<2 ){
874 Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
875 return TCL_ERROR;
876 }
dan2a321c72010-06-16 19:04:23 +0000877 if( Tcl_GetIndexFromObjStruct(
878 interp, objv[1], aSubcmd, sizeof(aSubcmd[0]), "subcommand", 0, &i)
879 ){
danc7991bd2010-05-05 19:04:59 +0000880 return TCL_ERROR;
881 }
882 Tcl_ResetResult(interp);
883
dan2a321c72010-06-16 19:04:23 +0000884 switch( aSubcmd[i].eCmd ){
danc7991bd2010-05-05 19:04:59 +0000885 case CMD_SHM: {
dan13a3cb82010-06-11 19:04:21 +0000886 Tcl_Obj *pObj;
887 int i;
danc7991bd2010-05-05 19:04:59 +0000888 TestvfsBuffer *pBuffer;
889 char *zName;
890 if( objc!=3 && objc!=4 ){
891 Tcl_WrongNumArgs(interp, 2, objv, "FILE ?VALUE?");
892 return TCL_ERROR;
893 }
dan5d656852010-06-14 07:53:26 +0000894 zName = ckalloc(p->pParent->mxPathname);
895 p->pParent->xFullPathname(
896 p->pParent, Tcl_GetString(objv[2]),
897 p->pParent->mxPathname, zName
898 );
danc7991bd2010-05-05 19:04:59 +0000899 for(pBuffer=p->pBuffer; pBuffer; pBuffer=pBuffer->pNext){
900 if( 0==strcmp(pBuffer->zFile, zName) ) break;
901 }
dan5d656852010-06-14 07:53:26 +0000902 ckfree(zName);
danc7991bd2010-05-05 19:04:59 +0000903 if( !pBuffer ){
dan5d656852010-06-14 07:53:26 +0000904 Tcl_AppendResult(interp, "no such file: ", Tcl_GetString(objv[2]), 0);
danc7991bd2010-05-05 19:04:59 +0000905 return TCL_ERROR;
906 }
907 if( objc==4 ){
908 int n;
909 u8 *a = Tcl_GetByteArrayFromObj(objv[3], &n);
dan13a3cb82010-06-11 19:04:21 +0000910 assert( pBuffer->pgsz==0 || pBuffer->pgsz==32768 );
911 for(i=0; i*32768<n; i++){
912 int nByte = 32768;
913 tvfsAllocPage(pBuffer, i, 32768);
914 if( n-i*32768<32768 ){
915 nByte = n;
916 }
917 memcpy(pBuffer->aPage[i], &a[i*32768], nByte);
918 }
danc7991bd2010-05-05 19:04:59 +0000919 }
dan13a3cb82010-06-11 19:04:21 +0000920
921 pObj = Tcl_NewObj();
922 for(i=0; pBuffer->aPage[i]; i++){
923 Tcl_AppendObjToObj(pObj, Tcl_NewByteArrayObj(pBuffer->aPage[i], 32768));
924 }
925 Tcl_SetObjResult(interp, pObj);
danc7991bd2010-05-05 19:04:59 +0000926 break;
927 }
dan0235ab92010-06-01 19:15:18 +0000928
929 case CMD_FILTER: {
930 static struct VfsMethod {
931 char *zName;
932 int mask;
933 } vfsmethod [] = {
934 { "xShmOpen", TESTVFS_SHMOPEN_MASK },
dan0235ab92010-06-01 19:15:18 +0000935 { "xShmLock", TESTVFS_SHMLOCK_MASK },
936 { "xShmBarrier", TESTVFS_SHMBARRIER_MASK },
937 { "xShmClose", TESTVFS_SHMCLOSE_MASK },
drh6b017cc2010-06-14 18:01:46 +0000938 { "xShmMap", TESTVFS_SHMMAP_MASK },
dand764c7d2010-06-04 11:56:22 +0000939 { "xSync", TESTVFS_SYNC_MASK },
danb0ac3e32010-06-16 10:55:42 +0000940 { "xDelete", TESTVFS_DELETE_MASK },
dan2a321c72010-06-16 19:04:23 +0000941 { "xWrite", TESTVFS_WRITE_MASK },
942 { "xTruncate", TESTVFS_TRUNCATE_MASK },
dand764c7d2010-06-04 11:56:22 +0000943 { "xOpen", TESTVFS_OPEN_MASK },
dan2a321c72010-06-16 19:04:23 +0000944 { "xClose", TESTVFS_CLOSE_MASK },
dan0235ab92010-06-01 19:15:18 +0000945 };
946 Tcl_Obj **apElem = 0;
947 int nElem = 0;
948 int i;
949 int mask = 0;
950 if( objc!=3 ){
951 Tcl_WrongNumArgs(interp, 2, objv, "LIST");
952 return TCL_ERROR;
953 }
954 if( Tcl_ListObjGetElements(interp, objv[2], &nElem, &apElem) ){
955 return TCL_ERROR;
956 }
957 Tcl_ResetResult(interp);
958 for(i=0; i<nElem; i++){
959 int iMethod;
960 char *zElem = Tcl_GetString(apElem[i]);
961 for(iMethod=0; iMethod<ArraySize(vfsmethod); iMethod++){
962 if( strcmp(zElem, vfsmethod[iMethod].zName)==0 ){
963 mask |= vfsmethod[iMethod].mask;
964 break;
965 }
966 }
967 if( iMethod==ArraySize(vfsmethod) ){
968 Tcl_AppendResult(interp, "unknown method: ", zElem, 0);
969 return TCL_ERROR;
970 }
971 }
972 p->mask = mask;
973 break;
974 }
975
dan1f55e282010-06-03 09:25:10 +0000976 case CMD_SCRIPT: {
977 if( objc==3 ){
978 int nByte;
979 if( p->pScript ){
980 Tcl_DecrRefCount(p->pScript);
981 ckfree((char *)p->apScript);
982 p->apScript = 0;
983 p->nScript = 0;
dan5d656852010-06-14 07:53:26 +0000984 p->pScript = 0;
dan1f55e282010-06-03 09:25:10 +0000985 }
986 Tcl_GetStringFromObj(objv[2], &nByte);
987 if( nByte>0 ){
988 p->pScript = Tcl_DuplicateObj(objv[2]);
989 Tcl_IncrRefCount(p->pScript);
990 }
991 }else if( objc!=2 ){
992 Tcl_WrongNumArgs(interp, 2, objv, "?SCRIPT?");
993 return TCL_ERROR;
994 }
995
996 Tcl_ResetResult(interp);
997 if( p->pScript ) Tcl_SetObjResult(interp, p->pScript);
998
999 break;
1000 }
1001
1002 /*
dan146ed782010-06-19 17:26:37 +00001003 ** TESTVFS fullerr ?IFAIL?
1004 **
1005 ** Where IFAIL is an integer.
1006 */
1007 case CMD_FULLERR: {
1008 int iRet = p->nFullFail;
1009
1010 p->nFullFail = 0;
1011 p->fullerr = 0;
1012 p->iFullCnt = 0;
1013
1014 if( objc==3 ){
1015 int iCnt;
1016 if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iCnt) ){
1017 return TCL_ERROR;
1018 }
1019 p->fullerr = (iCnt>0);
1020 p->iFullCnt = iCnt;
1021 }else if( objc!=2 ){
1022 Tcl_AppendResult(interp, "Bad args", 0);
1023 return TCL_ERROR;
1024 }
1025
1026 Tcl_SetObjResult(interp, Tcl_NewIntObj(iRet));
1027 break;
1028 }
1029
1030 /*
dan1f55e282010-06-03 09:25:10 +00001031 ** TESTVFS ioerr ?IFAIL PERSIST?
1032 **
1033 ** Where IFAIL is an integer and PERSIST is boolean.
1034 */
dan0235ab92010-06-01 19:15:18 +00001035 case CMD_IOERR: {
dan1f55e282010-06-03 09:25:10 +00001036 int iRet = p->nIoerrFail;
1037
1038 p->nIoerrFail = 0;
1039 p->ioerr = 0;
1040 p->iIoerrCnt = 0;
1041
1042 if( objc==4 ){
dan0235ab92010-06-01 19:15:18 +00001043 int iCnt, iPersist;
1044 if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iCnt)
1045 || TCL_OK!=Tcl_GetBooleanFromObj(interp, objv[3], &iPersist)
1046 ){
1047 return TCL_ERROR;
1048 }
dan1f55e282010-06-03 09:25:10 +00001049 p->ioerr = (iCnt>0) + iPersist;
dan0235ab92010-06-01 19:15:18 +00001050 p->iIoerrCnt = iCnt;
dan1f55e282010-06-03 09:25:10 +00001051 }else if( objc!=2 ){
dan0235ab92010-06-01 19:15:18 +00001052 Tcl_AppendResult(interp, "Bad args", 0);
dan1f55e282010-06-03 09:25:10 +00001053 return TCL_ERROR;
dan0235ab92010-06-01 19:15:18 +00001054 }
1055 Tcl_SetObjResult(interp, Tcl_NewIntObj(iRet));
1056 break;
1057 }
1058
danc7991bd2010-05-05 19:04:59 +00001059 case CMD_DELETE: {
1060 Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
1061 break;
1062 }
dan2a321c72010-06-16 19:04:23 +00001063
1064 case CMD_DEVCHAR: {
1065 struct DeviceFlag {
1066 char *zName;
1067 int iValue;
1068 } aFlag[] = {
dan8ce49d62010-06-19 18:12:02 +00001069 { "default", -1 },
1070 { "atomic", SQLITE_IOCAP_ATOMIC },
1071 { "atomic512", SQLITE_IOCAP_ATOMIC512 },
1072 { "atomic1k", SQLITE_IOCAP_ATOMIC1K },
1073 { "atomic2k", SQLITE_IOCAP_ATOMIC2K },
1074 { "atomic4k", SQLITE_IOCAP_ATOMIC4K },
1075 { "atomic8k", SQLITE_IOCAP_ATOMIC8K },
1076 { "atomic16k", SQLITE_IOCAP_ATOMIC16K },
1077 { "atomic32k", SQLITE_IOCAP_ATOMIC32K },
1078 { "atomic64k", SQLITE_IOCAP_ATOMIC64K },
1079 { "sequential", SQLITE_IOCAP_SEQUENTIAL },
1080 { "safe_append", SQLITE_IOCAP_SAFE_APPEND },
1081 { "undeletable_when_open", SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN },
dan2a321c72010-06-16 19:04:23 +00001082 { 0, 0 }
1083 };
1084 Tcl_Obj *pRet;
1085 int iFlag;
1086
1087 if( objc>3 ){
1088 Tcl_WrongNumArgs(interp, 2, objv, "?ATTR-LIST?");
1089 return TCL_ERROR;
1090 }
1091 if( objc==3 ){
1092 int j;
1093 int iNew = 0;
1094 Tcl_Obj **flags = 0;
1095 int nFlags = 0;
1096
1097 if( Tcl_ListObjGetElements(interp, objv[2], &nFlags, &flags) ){
1098 return TCL_ERROR;
1099 }
1100
1101 for(j=0; j<nFlags; j++){
1102 int idx = 0;
1103 if( Tcl_GetIndexFromObjStruct(interp, flags[j], aFlag,
1104 sizeof(aFlag[0]), "flag", 0, &idx)
1105 ){
1106 return TCL_ERROR;
1107 }
1108 if( aFlag[idx].iValue<0 && nFlags>1 ){
1109 Tcl_AppendResult(interp, "bad flags: ", Tcl_GetString(objv[2]), 0);
1110 return TCL_ERROR;
1111 }
1112 iNew |= aFlag[idx].iValue;
1113 }
1114
1115 p->iDevchar = iNew;
1116 }
1117
1118 pRet = Tcl_NewObj();
1119 for(iFlag=0; iFlag<sizeof(aFlag)/sizeof(aFlag[0]); iFlag++){
1120 if( p->iDevchar & aFlag[iFlag].iValue ){
1121 Tcl_ListObjAppendElement(
1122 interp, pRet, Tcl_NewStringObj(aFlag[iFlag].zName, -1)
1123 );
1124 }
1125 }
1126 Tcl_SetObjResult(interp, pRet);
1127
1128 break;
1129 }
1130
1131 case CMD_SECTORSIZE: {
1132 if( objc>3 ){
1133 Tcl_WrongNumArgs(interp, 2, objv, "?VALUE?");
1134 return TCL_ERROR;
1135 }
1136 if( objc==3 ){
1137 int iNew = 0;
1138 if( Tcl_GetIntFromObj(interp, objv[2], &iNew) ){
1139 return TCL_ERROR;
1140 }
1141 p->iSectorsize = iNew;
1142 }
1143 Tcl_SetObjResult(interp, Tcl_NewIntObj(p->iSectorsize));
1144 break;
1145 }
danc7991bd2010-05-05 19:04:59 +00001146 }
1147
1148 return TCL_OK;
1149}
1150
1151static void testvfs_obj_del(ClientData cd){
danc7991bd2010-05-05 19:04:59 +00001152 Testvfs *p = (Testvfs *)cd;
dan1f55e282010-06-03 09:25:10 +00001153 if( p->pScript ) Tcl_DecrRefCount(p->pScript);
danc7991bd2010-05-05 19:04:59 +00001154 sqlite3_vfs_unregister(p->pVfs);
dan1f55e282010-06-03 09:25:10 +00001155 ckfree((char *)p->apScript);
danc7991bd2010-05-05 19:04:59 +00001156 ckfree((char *)p->pVfs);
1157 ckfree((char *)p);
1158}
1159
danc7991bd2010-05-05 19:04:59 +00001160/*
dan1f55e282010-06-03 09:25:10 +00001161** Usage: testvfs VFSNAME ?SWITCHES?
1162**
1163** Switches are:
1164**
1165** -noshm BOOLEAN (True to omit shm methods. Default false)
1166** -default BOOLEAN (True to make the vfs default. Default false)
danc7991bd2010-05-05 19:04:59 +00001167**
1168** This command creates two things when it is invoked: an SQLite VFS, and
1169** a Tcl command. Both are named VFSNAME. The VFS is installed. It is not
1170** installed as the default VFS.
1171**
1172** The VFS passes all file I/O calls through to the underlying VFS.
1173**
drh6b017cc2010-06-14 18:01:46 +00001174** Whenever the xShmMap method of the VFS
1175** is invoked, the SCRIPT is executed as follows:
danc7991bd2010-05-05 19:04:59 +00001176**
drh6b017cc2010-06-14 18:01:46 +00001177** SCRIPT xShmMap FILENAME ID
danc7991bd2010-05-05 19:04:59 +00001178**
1179** The value returned by the invocation of SCRIPT above is interpreted as
1180** an SQLite error code and returned to SQLite. Either a symbolic
1181** "SQLITE_OK" or numeric "0" value may be returned.
1182**
1183** The contents of the shared-memory buffer associated with a given file
1184** may be read and set using the following command:
1185**
1186** VFSNAME shm FILENAME ?NEWVALUE?
1187**
1188** When the xShmLock method is invoked by SQLite, the following script is
1189** run:
1190**
1191** SCRIPT xShmLock FILENAME ID LOCK
1192**
drh73b64e42010-05-30 19:55:15 +00001193** where LOCK is of the form "OFFSET NBYTE lock/unlock shared/exclusive"
danc7991bd2010-05-05 19:04:59 +00001194*/
1195static int testvfs_cmd(
1196 ClientData cd,
1197 Tcl_Interp *interp,
1198 int objc,
1199 Tcl_Obj *CONST objv[]
1200){
danc7991bd2010-05-05 19:04:59 +00001201 static sqlite3_vfs tvfs_vfs = {
1202 2, /* iVersion */
dan7fd555a2010-05-13 06:19:37 +00001203 sizeof(TestvfsFile), /* szOsFile */
danc7991bd2010-05-05 19:04:59 +00001204 0, /* mxPathname */
1205 0, /* pNext */
1206 0, /* zName */
1207 0, /* pAppData */
1208 tvfsOpen, /* xOpen */
1209 tvfsDelete, /* xDelete */
1210 tvfsAccess, /* xAccess */
1211 tvfsFullPathname, /* xFullPathname */
1212#ifndef SQLITE_OMIT_LOAD_EXTENSION
1213 tvfsDlOpen, /* xDlOpen */
1214 tvfsDlError, /* xDlError */
1215 tvfsDlSym, /* xDlSym */
1216 tvfsDlClose, /* xDlClose */
1217#else
1218 0, /* xDlOpen */
1219 0, /* xDlError */
1220 0, /* xDlSym */
1221 0, /* xDlClose */
1222#endif /* SQLITE_OMIT_LOAD_EXTENSION */
1223 tvfsRandomness, /* xRandomness */
1224 tvfsSleep, /* xSleep */
1225 tvfsCurrentTime, /* xCurrentTime */
1226 0, /* xGetLastError */
danc7991bd2010-05-05 19:04:59 +00001227 0,
1228 0,
1229 };
1230
1231 Testvfs *p; /* New object */
1232 sqlite3_vfs *pVfs; /* New VFS */
1233 char *zVfs;
danc7991bd2010-05-05 19:04:59 +00001234 int nByte; /* Bytes of space to allocate at p */
dan1f55e282010-06-03 09:25:10 +00001235
1236 int i;
dan576bc322010-05-06 18:04:50 +00001237 int isNoshm = 0; /* True if -noshm is passed */
dan1f55e282010-06-03 09:25:10 +00001238 int isDefault = 0; /* True if -default is passed */
danc7991bd2010-05-05 19:04:59 +00001239
dan1f55e282010-06-03 09:25:10 +00001240 if( objc<2 || 0!=(objc%2) ) goto bad_args;
1241 for(i=2; i<objc; i += 2){
1242 int nSwitch;
1243 char *zSwitch;
danc7991bd2010-05-05 19:04:59 +00001244
dan1f55e282010-06-03 09:25:10 +00001245 zSwitch = Tcl_GetStringFromObj(objv[i], &nSwitch);
1246 if( nSwitch>2 && 0==strncmp("-noshm", zSwitch, nSwitch) ){
1247 if( Tcl_GetBooleanFromObj(interp, objv[i+1], &isNoshm) ){
1248 return TCL_ERROR;
1249 }
1250 }
1251 else if( nSwitch>2 && 0==strncmp("-default", zSwitch, nSwitch) ){
1252 if( Tcl_GetBooleanFromObj(interp, objv[i+1], &isDefault) ){
1253 return TCL_ERROR;
1254 }
1255 }
1256 else{
1257 goto bad_args;
1258 }
danc7991bd2010-05-05 19:04:59 +00001259 }
1260
dan1f55e282010-06-03 09:25:10 +00001261 zVfs = Tcl_GetString(objv[1]);
1262 nByte = sizeof(Testvfs) + strlen(zVfs)+1;
danc7991bd2010-05-05 19:04:59 +00001263 p = (Testvfs *)ckalloc(nByte);
1264 memset(p, 0, nByte);
dan2a321c72010-06-16 19:04:23 +00001265 p->iDevchar = -1;
1266 p->iSectorsize = -1;
danc7991bd2010-05-05 19:04:59 +00001267
dan5d656852010-06-14 07:53:26 +00001268 /* Create the new object command before querying SQLite for a default VFS
1269 ** to use for 'real' IO operations. This is because creating the new VFS
1270 ** may delete an existing [testvfs] VFS of the same name. If such a VFS
1271 ** is currently the default, the new [testvfs] may end up calling the
1272 ** methods of a deleted object.
1273 */
1274 Tcl_CreateObjCommand(interp, zVfs, testvfs_obj_cmd, p, testvfs_obj_del);
danc7991bd2010-05-05 19:04:59 +00001275 p->pParent = sqlite3_vfs_find(0);
1276 p->interp = interp;
dan1f55e282010-06-03 09:25:10 +00001277
1278 p->zName = (char *)&p[1];
1279 memcpy(p->zName, zVfs, strlen(zVfs)+1);
danc7991bd2010-05-05 19:04:59 +00001280
1281 pVfs = (sqlite3_vfs *)ckalloc(sizeof(sqlite3_vfs));
1282 memcpy(pVfs, &tvfs_vfs, sizeof(sqlite3_vfs));
1283 pVfs->pAppData = (void *)p;
1284 pVfs->zName = p->zName;
1285 pVfs->mxPathname = p->pParent->mxPathname;
1286 pVfs->szOsFile += p->pParent->szOsFile;
dan8f6097c2010-05-06 07:43:58 +00001287 p->pVfs = pVfs;
dan7fd555a2010-05-13 06:19:37 +00001288 p->isNoshm = isNoshm;
dan0235ab92010-06-01 19:15:18 +00001289 p->mask = TESTVFS_ALL_MASK;
danc7991bd2010-05-05 19:04:59 +00001290
dan1f55e282010-06-03 09:25:10 +00001291 sqlite3_vfs_register(pVfs, isDefault);
danc7991bd2010-05-05 19:04:59 +00001292
1293 return TCL_OK;
dan576bc322010-05-06 18:04:50 +00001294
1295 bad_args:
dan1f55e282010-06-03 09:25:10 +00001296 Tcl_WrongNumArgs(interp, 1, objv, "VFSNAME ?-noshm BOOL? ?-default BOOL?");
dan576bc322010-05-06 18:04:50 +00001297 return TCL_ERROR;
danc7991bd2010-05-05 19:04:59 +00001298}
1299
1300int Sqlitetestvfs_Init(Tcl_Interp *interp){
dan7fd555a2010-05-13 06:19:37 +00001301 Tcl_CreateObjCommand(interp, "testvfs", testvfs_cmd, 0, 0);
danc7991bd2010-05-05 19:04:59 +00001302 return TCL_OK;
1303}
1304
1305#endif