AryaWu/sqlite
0
1/*2** 2010 May 053**4** The author disclaims copyright to this source code. In place of5** 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** This file contains the implementation of the Tcl [testvfs] command,14** used to create SQLite VFS implementations with various properties and15** instrumentation to support testing SQLite.16**17** testvfs VFSNAME ?OPTIONS?18**19** Available options are:20**21** -noshm BOOLEAN (True to omit shm methods. Default false)22** -default BOOLEAN (True to make the vfs default. Default false)23** -szosfile INTEGER (Value for sqlite3_vfs.szOsFile)24** -mxpathname INTEGER (Value for sqlite3_vfs.mxPathname)25** -iversion INTEGER (Value for sqlite3_vfs.iVersion)26*/27#if SQLITE_TEST /* This file is used for testing only */28 29#include "sqlite3.h"30#include "sqliteInt.h"31#include "tclsqlite.h"32 33typedef struct Testvfs Testvfs;34typedef struct TestvfsShm TestvfsShm;35typedef struct TestvfsBuffer TestvfsBuffer;36typedef struct TestvfsFile TestvfsFile;37typedef struct TestvfsFd TestvfsFd;38 39/*40** An open file handle.41*/42struct TestvfsFile {43 sqlite3_file base; /* Base class. Must be first */44 TestvfsFd *pFd; /* File data */45};46#define tvfsGetFd(pFile) (((TestvfsFile *)pFile)->pFd)47 48struct TestvfsFd {49 sqlite3_vfs *pVfs; /* The VFS */50 const char *zFilename; /* Filename as passed to xOpen() */51 sqlite3_file *pReal; /* The real, underlying file descriptor */52 Tcl_Obj *pShmId; /* Shared memory id for Tcl callbacks */53 54 TestvfsBuffer *pShm; /* Shared memory buffer */55 u32 excllock; /* Mask of exclusive locks */56 u32 sharedlock; /* Mask of shared locks */57 TestvfsFd *pNext; /* Next handle opened on the same file */58};59 60 61#define FAULT_INJECT_NONE 062#define FAULT_INJECT_TRANSIENT 163#define FAULT_INJECT_PERSISTENT 264 65typedef struct TestFaultInject TestFaultInject;66struct TestFaultInject {67 int iCnt; /* Remaining calls before fault injection */68 int eFault; /* A FAULT_INJECT_* value */69 int nFail; /* Number of faults injected */70};71 72/*73** An instance of this structure is allocated for each VFS created. The74** sqlite3_vfs.pAppData field of the VFS structure registered with SQLite75** is set to point to it.76*/77struct Testvfs {78 char *zName; /* Name of this VFS */79 sqlite3_vfs *pParent; /* The VFS to use for file IO */80 sqlite3_vfs *pVfs; /* The testvfs registered with SQLite */81 Tcl_Interp *interp; /* Interpreter to run script in */82 Tcl_Obj *pScript; /* Script to execute */83 TestvfsBuffer *pBuffer; /* List of shared buffers */84 int isNoshm;85 int isFullshm;86 87 int mask; /* Mask controlling [script] and [ioerr] */88 89 TestFaultInject ioerr_err;90 TestFaultInject full_err;91 TestFaultInject cantopen_err;92 93#if 094 int iIoerrCnt;95 int ioerr;96 int nIoerrFail;97 int iFullCnt;98 int fullerr;99 int nFullFail;100#endif101 102 int iDevchar;103 int iSectorsize;104};105 106/*107** The Testvfs.mask variable is set to a combination of the following.108** If a bit is clear in Testvfs.mask, then calls made by SQLite to the 109** corresponding VFS method is ignored for purposes of:110**111** + Simulating IO errors, and112** + Invoking the Tcl callback script.113*/114#define TESTVFS_SHMOPEN_MASK 0x00000001115#define TESTVFS_SHMLOCK_MASK 0x00000010116#define TESTVFS_SHMMAP_MASK 0x00000020117#define TESTVFS_SHMBARRIER_MASK 0x00000040118#define TESTVFS_SHMCLOSE_MASK 0x00000080119 120#define TESTVFS_OPEN_MASK 0x00000100121#define TESTVFS_SYNC_MASK 0x00000200122#define TESTVFS_DELETE_MASK 0x00000400123#define TESTVFS_CLOSE_MASK 0x00000800124#define TESTVFS_WRITE_MASK 0x00001000125#define TESTVFS_TRUNCATE_MASK 0x00002000126#define TESTVFS_ACCESS_MASK 0x00004000127#define TESTVFS_FULLPATHNAME_MASK 0x00008000128#define TESTVFS_READ_MASK 0x00010000129#define TESTVFS_UNLOCK_MASK 0x00020000130#define TESTVFS_LOCK_MASK 0x00040000131#define TESTVFS_CKLOCK_MASK 0x00080000132#define TESTVFS_FCNTL_MASK 0x00100000133#define TESTVFS_SLEEP_MASK 0x00200000134 135#define TESTVFS_ALL_MASK 0x003FFFFF136 137 138#define TESTVFS_MAX_PAGES 1024139 140/*141** A shared-memory buffer. There is one of these objects for each shared142** memory region opened by clients. If two clients open the same file,143** there are two TestvfsFile structures but only one TestvfsBuffer structure.144*/145struct TestvfsBuffer {146 char *zFile; /* Associated file name */147 int pgsz; /* Page size */148 u8 *aPage[TESTVFS_MAX_PAGES]; /* Array of ckalloc'd pages */149 TestvfsFd *pFile; /* List of open handles */150 TestvfsBuffer *pNext; /* Next in linked list of all buffers */151};152 153 154#define PARENTVFS(x) (((Testvfs *)((x)->pAppData))->pParent)155 156#define TESTVFS_MAX_ARGS 12157 158 159/*160** Method declarations for TestvfsFile.161*/162static int tvfsClose(sqlite3_file*);163static int tvfsRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);164static int tvfsWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);165static int tvfsTruncate(sqlite3_file*, sqlite3_int64 size);166static int tvfsSync(sqlite3_file*, int flags);167static int tvfsFileSize(sqlite3_file*, sqlite3_int64 *pSize);168static int tvfsLock(sqlite3_file*, int);169static int tvfsUnlock(sqlite3_file*, int);170static int tvfsCheckReservedLock(sqlite3_file*, int *);171static int tvfsFileControl(sqlite3_file*, int op, void *pArg);172static int tvfsSectorSize(sqlite3_file*);173static int tvfsDeviceCharacteristics(sqlite3_file*);174 175/*176** Method declarations for tvfs_vfs.177*/178static int tvfsOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);179static int tvfsDelete(sqlite3_vfs*, const char *zName, int syncDir);180static int tvfsAccess(sqlite3_vfs*, const char *zName, int flags, int *);181static int tvfsFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);182#ifndef SQLITE_OMIT_LOAD_EXTENSION183static void *tvfsDlOpen(sqlite3_vfs*, const char *zFilename);184static void tvfsDlError(sqlite3_vfs*, int nByte, char *zErrMsg);185static void (*tvfsDlSym(sqlite3_vfs*,void*, const char *zSymbol))(void);186static void tvfsDlClose(sqlite3_vfs*, void*);187#endif /* SQLITE_OMIT_LOAD_EXTENSION */188static int tvfsRandomness(sqlite3_vfs*, int nByte, char *zOut);189static int tvfsSleep(sqlite3_vfs*, int microseconds);190static int tvfsCurrentTime(sqlite3_vfs*, double*);191 192static int tvfsShmOpen(sqlite3_file*);193static int tvfsShmLock(sqlite3_file*, int , int, int);194static int tvfsShmMap(sqlite3_file*,int,int,int, void volatile **);195static void tvfsShmBarrier(sqlite3_file*);196static int tvfsShmUnmap(sqlite3_file*, int);197 198static int tvfsFetch(sqlite3_file*, sqlite3_int64, int, void**);199static int tvfsUnfetch(sqlite3_file*, sqlite3_int64, void*);200 201static sqlite3_io_methods tvfs_io_methods = {202 3, /* iVersion */203 tvfsClose, /* xClose */204 tvfsRead, /* xRead */205 tvfsWrite, /* xWrite */206 tvfsTruncate, /* xTruncate */207 tvfsSync, /* xSync */208 tvfsFileSize, /* xFileSize */209 tvfsLock, /* xLock */210 tvfsUnlock, /* xUnlock */211 tvfsCheckReservedLock, /* xCheckReservedLock */212 tvfsFileControl, /* xFileControl */213 tvfsSectorSize, /* xSectorSize */214 tvfsDeviceCharacteristics, /* xDeviceCharacteristics */215 tvfsShmMap, /* xShmMap */216 tvfsShmLock, /* xShmLock */217 tvfsShmBarrier, /* xShmBarrier */218 tvfsShmUnmap, /* xShmUnmap */219 tvfsFetch,220 tvfsUnfetch221};222 223static int tvfsResultCode(Testvfs *p, int *pRc){224 struct errcode {225 int eCode;226 const char *zCode;227 } aCode[] = {228 { SQLITE_OK, "SQLITE_OK" },229 { SQLITE_ERROR, "SQLITE_ERROR" },230 { SQLITE_IOERR, "SQLITE_IOERR" },231 { SQLITE_LOCKED, "SQLITE_LOCKED" },232 { SQLITE_BUSY, "SQLITE_BUSY" },233 { SQLITE_READONLY, "SQLITE_READONLY" },234 { SQLITE_READONLY_CANTINIT, "SQLITE_READONLY_CANTINIT" },235 { SQLITE_NOTFOUND, "SQLITE_NOTFOUND" },236 { -1, "SQLITE_OMIT" },237 };238 239 const char *z;240 int i;241 242 z = Tcl_GetStringResult(p->interp);243 for(i=0; i<ArraySize(aCode); i++){244 if( 0==strcmp(z, aCode[i].zCode) ){245 *pRc = aCode[i].eCode;246 return 1;247 }248 }249 250 return 0;251}252 253static int tvfsInjectFault(TestFaultInject *p){254 int ret = 0;255 if( p->eFault ){256 p->iCnt--;257 if( p->iCnt==0 || (p->iCnt<0 && p->eFault==FAULT_INJECT_PERSISTENT ) ){258 ret = 1;259 p->nFail++;260 }261 }262 return ret;263}264 265 266static int tvfsInjectIoerr(Testvfs *p){267 return tvfsInjectFault(&p->ioerr_err);268}269 270static int tvfsInjectFullerr(Testvfs *p){271 return tvfsInjectFault(&p->full_err);272}273static int tvfsInjectCantopenerr(Testvfs *p){274 return tvfsInjectFault(&p->cantopen_err);275}276 277 278static void tvfsExecTcl(279 Testvfs *p, 280 const char *zMethod,281 Tcl_Obj *arg1,282 Tcl_Obj *arg2,283 Tcl_Obj *arg3,284 Tcl_Obj *arg4285){286 int rc; /* Return code from Tcl_EvalObj() */287 Tcl_Obj *pEval;288 assert( p->pScript );289 290 assert( zMethod );291 assert( p );292 assert( arg2==0 || arg1!=0 );293 assert( arg3==0 || arg2!=0 );294 295 pEval = Tcl_DuplicateObj(p->pScript);296 Tcl_IncrRefCount(p->pScript);297 Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewStringObj(zMethod, -1));298 if( arg1 ) Tcl_ListObjAppendElement(p->interp, pEval, arg1);299 if( arg2 ) Tcl_ListObjAppendElement(p->interp, pEval, arg2);300 if( arg3 ) Tcl_ListObjAppendElement(p->interp, pEval, arg3);301 if( arg4 ) Tcl_ListObjAppendElement(p->interp, pEval, arg4);302 303 rc = Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL);304 if( rc!=TCL_OK ){305 Tcl_BackgroundError(p->interp);306 Tcl_ResetResult(p->interp);307 }308}309 310 311/*312** Close an tvfs-file.313*/314static int tvfsClose(sqlite3_file *pFile){315 TestvfsFile *pTestfile = (TestvfsFile *)pFile;316 TestvfsFd *pFd = pTestfile->pFd;317 Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;318 319 if( p->pScript && p->mask&TESTVFS_CLOSE_MASK ){320 tvfsExecTcl(p, "xClose", 321 Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0, 0322 );323 }324 325 if( pFd->pShmId ){326 Tcl_DecrRefCount(pFd->pShmId);327 pFd->pShmId = 0;328 }329 if( pFile->pMethods ){330 ckfree((char *)pFile->pMethods);331 }332 sqlite3OsClose(pFd->pReal);333 ckfree((char *)pFd);334 pTestfile->pFd = 0;335 return SQLITE_OK;336}337 338/*339** Read data from an tvfs-file.340*/341static int tvfsRead(342 sqlite3_file *pFile, 343 void *zBuf, 344 int iAmt, 345 sqlite_int64 iOfst346){347 int rc = SQLITE_OK;348 TestvfsFd *pFd = tvfsGetFd(pFile);349 Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;350 if( p->pScript && p->mask&TESTVFS_READ_MASK ){351 tvfsExecTcl(p, "xRead", 352 Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0, 0353 );354 tvfsResultCode(p, &rc);355 }356 if( rc==SQLITE_OK && p->mask&TESTVFS_READ_MASK && tvfsInjectIoerr(p) ){357 rc = SQLITE_IOERR;358 }359 if( rc==SQLITE_OK ){360 rc = sqlite3OsRead(pFd->pReal, zBuf, iAmt, iOfst);361 }362 return rc;363}364 365/*366** Write data to an tvfs-file.367*/368static int tvfsWrite(369 sqlite3_file *pFile, 370 const void *zBuf, 371 int iAmt, 372 sqlite_int64 iOfst373){374 int rc = SQLITE_OK;375 TestvfsFd *pFd = tvfsGetFd(pFile);376 Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;377 378 if( p->pScript && p->mask&TESTVFS_WRITE_MASK ){379 tvfsExecTcl(p, "xWrite", 380 Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 381 Tcl_NewWideIntObj(iOfst), Tcl_NewIntObj(iAmt)382 );383 tvfsResultCode(p, &rc);384 if( rc<0 ) return SQLITE_OK;385 }386 387 if( rc==SQLITE_OK && tvfsInjectFullerr(p) ){388 rc = SQLITE_FULL;389 }390 if( rc==SQLITE_OK && p->mask&TESTVFS_WRITE_MASK && tvfsInjectIoerr(p) ){391 rc = SQLITE_IOERR;392 }393 394 if( rc==SQLITE_OK ){395 rc = sqlite3OsWrite(pFd->pReal, zBuf, iAmt, iOfst);396 }397 return rc;398}399 400/*401** Truncate an tvfs-file.402*/403static int tvfsTruncate(sqlite3_file *pFile, sqlite_int64 size){404 int rc = SQLITE_OK;405 TestvfsFd *pFd = tvfsGetFd(pFile);406 Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;407 408 if( p->pScript && p->mask&TESTVFS_TRUNCATE_MASK ){409 tvfsExecTcl(p, "xTruncate", 410 Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0, 0411 );412 tvfsResultCode(p, &rc);413 }414 415 if( rc==SQLITE_OK ){416 rc = sqlite3OsTruncate(pFd->pReal, size);417 }418 return rc;419}420 421/*422** Sync an tvfs-file.423*/424static int tvfsSync(sqlite3_file *pFile, int flags){425 int rc = SQLITE_OK;426 TestvfsFd *pFd = tvfsGetFd(pFile);427 Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;428 429 if( p->pScript && p->mask&TESTVFS_SYNC_MASK ){430 char *zFlags = 0;431 432 switch( flags ){433 case SQLITE_SYNC_NORMAL:434 zFlags = "normal";435 break;436 case SQLITE_SYNC_FULL:437 zFlags = "full";438 break;439 case SQLITE_SYNC_NORMAL|SQLITE_SYNC_DATAONLY:440 zFlags = "normal|dataonly";441 break;442 case SQLITE_SYNC_FULL|SQLITE_SYNC_DATAONLY:443 zFlags = "full|dataonly";444 break;445 default:446 assert(0);447 }448 449 tvfsExecTcl(p, "xSync", 450 Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId,451 Tcl_NewStringObj(zFlags, -1), 0452 );453 tvfsResultCode(p, &rc);454 }455 456 if( rc==SQLITE_OK && tvfsInjectFullerr(p) ) rc = SQLITE_FULL;457 458 if( rc==SQLITE_OK ){459 rc = sqlite3OsSync(pFd->pReal, flags);460 }461 462 return rc;463}464 465/*466** Return the current file-size of an tvfs-file.467*/468static int tvfsFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){469 TestvfsFd *p = tvfsGetFd(pFile);470 return sqlite3OsFileSize(p->pReal, pSize);471}472 473/*474** Lock an tvfs-file.475*/476static int tvfsLock(sqlite3_file *pFile, int eLock){477 TestvfsFd *pFd = tvfsGetFd(pFile);478 Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;479 if( p->pScript && p->mask&TESTVFS_LOCK_MASK ){480 char zLock[30];481 sqlite3_snprintf(sizeof(zLock),zLock,"%d",eLock);482 tvfsExecTcl(p, "xLock", Tcl_NewStringObj(pFd->zFilename, -1), 483 Tcl_NewStringObj(zLock, -1), 0, 0);484 }485 if( p->mask&TESTVFS_LOCK_MASK && tvfsInjectIoerr(p) ){486 return SQLITE_IOERR_LOCK;487 }488 return sqlite3OsLock(pFd->pReal, eLock);489}490 491/*492** Unlock an tvfs-file.493*/494static int tvfsUnlock(sqlite3_file *pFile, int eLock){495 TestvfsFd *pFd = tvfsGetFd(pFile);496 Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;497 if( p->pScript && p->mask&TESTVFS_UNLOCK_MASK ){498 char zLock[30];499 sqlite3_snprintf(sizeof(zLock),zLock,"%d",eLock);500 tvfsExecTcl(p, "xUnlock", Tcl_NewStringObj(pFd->zFilename, -1), 501 Tcl_NewStringObj(zLock, -1), 0, 0);502 }503 if( p->mask&TESTVFS_UNLOCK_MASK && tvfsInjectIoerr(p) ){504 return SQLITE_IOERR_UNLOCK;505 }506 return sqlite3OsUnlock(pFd->pReal, eLock);507}508 509/*510** Check if another file-handle holds a RESERVED lock on an tvfs-file.511*/512static int tvfsCheckReservedLock(sqlite3_file *pFile, int *pResOut){513 TestvfsFd *pFd = tvfsGetFd(pFile);514 Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;515 if( p->pScript && p->mask&TESTVFS_CKLOCK_MASK ){516 tvfsExecTcl(p, "xCheckReservedLock", Tcl_NewStringObj(pFd->zFilename, -1),517 0, 0, 0);518 }519 return sqlite3OsCheckReservedLock(pFd->pReal, pResOut);520}521 522/*523** File control method. For custom operations on an tvfs-file.524*/525static int tvfsFileControl(sqlite3_file *pFile, int op, void *pArg){526 TestvfsFd *pFd = tvfsGetFd(pFile);527 Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;528 if( op==SQLITE_FCNTL_PRAGMA ){529 char **argv = (char**)pArg;530 if( sqlite3_stricmp(argv[1],"error")==0 ){531 int rc = SQLITE_ERROR;532 if( argv[2] ){533 const char *z = argv[2];534 int x = atoi(z);535 if( x ){536 rc = x;537 while( sqlite3Isdigit(z[0]) ){ z++; }538 while( sqlite3Isspace(z[0]) ){ z++; }539 }540 if( z[0] ) argv[0] = sqlite3_mprintf("%s", z);541 }542 return rc;543 }544 if( sqlite3_stricmp(argv[1], "filename")==0 ){545 argv[0] = sqlite3_mprintf("%s", pFd->zFilename);546 return SQLITE_OK;547 }548 }549 if( p->pScript && (p->mask&TESTVFS_FCNTL_MASK) ){550 struct Fcntl {551 int iFnctl;552 const char *zFnctl;553 } aF[] = {554 { SQLITE_FCNTL_BEGIN_ATOMIC_WRITE, "BEGIN_ATOMIC_WRITE" },555 { SQLITE_FCNTL_COMMIT_ATOMIC_WRITE, "COMMIT_ATOMIC_WRITE" },556 { SQLITE_FCNTL_ZIPVFS, "ZIPVFS" },557 };558 int i;559 for(i=0; i<sizeof(aF)/sizeof(aF[0]); i++){560 if( op==aF[i].iFnctl ) break;561 }562 if( i<sizeof(aF)/sizeof(aF[0]) ){563 int rc = 0;564 tvfsExecTcl(p, "xFileControl", 565 Tcl_NewStringObj(pFd->zFilename, -1), 566 Tcl_NewStringObj(aF[i].zFnctl, -1),567 0, 0568 );569 tvfsResultCode(p, &rc);570 if( rc ) return (rc<0 ? SQLITE_OK : rc);571 }572 }573 return sqlite3OsFileControl(pFd->pReal, op, pArg);574}575 576/*577** Return the sector-size in bytes for an tvfs-file.578*/579static int tvfsSectorSize(sqlite3_file *pFile){580 TestvfsFd *pFd = tvfsGetFd(pFile);581 Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;582 if( p->iSectorsize>=0 ){583 return p->iSectorsize;584 }585 return sqlite3OsSectorSize(pFd->pReal);586}587 588/*589** Return the device characteristic flags supported by an tvfs-file.590*/591static int tvfsDeviceCharacteristics(sqlite3_file *pFile){592 TestvfsFd *pFd = tvfsGetFd(pFile);593 Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;594 if( p->iDevchar>=0 ){595 return p->iDevchar;596 }597 return sqlite3OsDeviceCharacteristics(pFd->pReal);598}599 600/*601** Open an tvfs file handle.602*/603static int tvfsOpen(604 sqlite3_vfs *pVfs,605 const char *zName,606 sqlite3_file *pFile,607 int flags,608 int *pOutFlags609){610 int rc;611 TestvfsFile *pTestfile = (TestvfsFile *)pFile;612 TestvfsFd *pFd;613 Tcl_Obj *pId = 0;614 Testvfs *p = (Testvfs *)pVfs->pAppData;615 616 pFd = (TestvfsFd *)ckalloc(sizeof(TestvfsFd) + PARENTVFS(pVfs)->szOsFile);617 memset(pFd, 0, sizeof(TestvfsFd) + PARENTVFS(pVfs)->szOsFile);618 pFd->pShm = 0;619 pFd->pShmId = 0;620 pFd->zFilename = zName;621 pFd->pVfs = pVfs;622 pFd->pReal = (sqlite3_file *)&pFd[1];623 memset(pTestfile, 0, sizeof(TestvfsFile));624 pTestfile->pFd = pFd;625 626 /* Evaluate the Tcl script: 627 **628 ** SCRIPT xOpen FILENAME KEY-VALUE-ARGS629 **630 ** If the script returns an SQLite error code other than SQLITE_OK, an631 ** error is returned to the caller. If it returns SQLITE_OK, the new632 ** connection is named "anon". Otherwise, the value returned by the633 ** script is used as the connection name.634 */635 Tcl_ResetResult(p->interp);636 if( p->pScript && p->mask&TESTVFS_OPEN_MASK ){637 Tcl_Obj *pArg = Tcl_NewObj();638 Tcl_IncrRefCount(pArg);639 if( flags&SQLITE_OPEN_MAIN_DB ){640 const char *z = &zName[strlen(zName)+1];641 while( *z ){642 Tcl_ListObjAppendElement(0, pArg, Tcl_NewStringObj(z, -1));643 z += strlen(z) + 1;644 Tcl_ListObjAppendElement(0, pArg, Tcl_NewStringObj(z, -1));645 z += strlen(z) + 1;646 }647 }648 tvfsExecTcl(p, "xOpen", Tcl_NewStringObj(pFd->zFilename, -1), pArg, 0, 0);649 Tcl_DecrRefCount(pArg);650 if( tvfsResultCode(p, &rc) ){651 if( rc!=SQLITE_OK ) return rc;652 }else{653 pId = Tcl_GetObjResult(p->interp);654 }655 }656 657 if( (p->mask&TESTVFS_OPEN_MASK) && tvfsInjectIoerr(p) ) return SQLITE_IOERR;658 if( tvfsInjectCantopenerr(p) ) return SQLITE_CANTOPEN;659 if( tvfsInjectFullerr(p) ) return SQLITE_FULL;660 661 if( !pId ){662 pId = Tcl_NewStringObj("anon", -1);663 }664 Tcl_IncrRefCount(pId);665 pFd->pShmId = pId;666 Tcl_ResetResult(p->interp);667 668 rc = sqlite3OsOpen(PARENTVFS(pVfs), zName, pFd->pReal, flags, pOutFlags);669 if( pFd->pReal->pMethods ){670 sqlite3_io_methods *pMethods;671 int nByte;672 673 if( pVfs->iVersion>1 ){674 nByte = sizeof(sqlite3_io_methods);675 }else{676 nByte = offsetof(sqlite3_io_methods, xShmMap);677 }678 679 pMethods = (sqlite3_io_methods *)ckalloc(nByte);680 memcpy(pMethods, &tvfs_io_methods, nByte);681 pMethods->iVersion = pFd->pReal->pMethods->iVersion;682 if( pMethods->iVersion>pVfs->iVersion ){683 pMethods->iVersion = pVfs->iVersion;684 }685 if( pVfs->iVersion>1 && ((Testvfs *)pVfs->pAppData)->isNoshm ){686 pMethods->xShmUnmap = 0;687 pMethods->xShmLock = 0;688 pMethods->xShmBarrier = 0;689 pMethods->xShmMap = 0;690 }691 pFile->pMethods = pMethods;692 }693 694 return rc;695}696 697/*698** Delete the file located at zPath. If the dirSync argument is true,699** ensure the file-system modifications are synced to disk before700** returning.701*/702static int tvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){703 int rc = SQLITE_OK;704 Testvfs *p = (Testvfs *)pVfs->pAppData;705 706 if( p->pScript && p->mask&TESTVFS_DELETE_MASK ){707 tvfsExecTcl(p, "xDelete", 708 Tcl_NewStringObj(zPath, -1), Tcl_NewIntObj(dirSync), 0, 0709 );710 tvfsResultCode(p, &rc);711 }712 if( rc==SQLITE_OK ){713 rc = sqlite3OsDelete(PARENTVFS(pVfs), zPath, dirSync);714 }715 return rc;716}717 718/*719** Test for access permissions. Return true if the requested permission720** is available, or false otherwise.721*/722static int tvfsAccess(723 sqlite3_vfs *pVfs, 724 const char *zPath, 725 int flags, 726 int *pResOut727){728 Testvfs *p = (Testvfs *)pVfs->pAppData;729 if( p->pScript && p->mask&TESTVFS_ACCESS_MASK ){730 int rc;731 char *zArg = 0;732 if( flags==SQLITE_ACCESS_EXISTS ) zArg = "SQLITE_ACCESS_EXISTS";733 if( flags==SQLITE_ACCESS_READWRITE ) zArg = "SQLITE_ACCESS_READWRITE";734 if( flags==SQLITE_ACCESS_READ ) zArg = "SQLITE_ACCESS_READ";735 tvfsExecTcl(p, "xAccess", 736 Tcl_NewStringObj(zPath, -1), Tcl_NewStringObj(zArg, -1), 0, 0737 );738 if( tvfsResultCode(p, &rc) ){739 if( rc!=SQLITE_OK ) return rc;740 }else{741 Tcl_Interp *interp = p->interp;742 if( TCL_OK==Tcl_GetBooleanFromObj(0, Tcl_GetObjResult(interp), pResOut) ){743 return SQLITE_OK;744 }745 }746 }747 return sqlite3OsAccess(PARENTVFS(pVfs), zPath, flags, pResOut);748}749 750/*751** Populate buffer zOut with the full canonical pathname corresponding752** to the pathname in zPath. zOut is guaranteed to point to a buffer753** of at least (DEVSYM_MAX_PATHNAME+1) bytes.754*/755static int tvfsFullPathname(756 sqlite3_vfs *pVfs, 757 const char *zPath, 758 int nOut, 759 char *zOut760){761 Testvfs *p = (Testvfs *)pVfs->pAppData;762 if( p->pScript && p->mask&TESTVFS_FULLPATHNAME_MASK ){763 int rc;764 tvfsExecTcl(p, "xFullPathname", Tcl_NewStringObj(zPath, -1), 0, 0, 0);765 if( tvfsResultCode(p, &rc) ){766 if( rc!=SQLITE_OK ) return rc;767 }768 }769 return sqlite3OsFullPathname(PARENTVFS(pVfs), zPath, nOut, zOut);770}771 772#ifndef SQLITE_OMIT_LOAD_EXTENSION773/*774** Open the dynamic library located at zPath and return a handle.775*/776static void *tvfsDlOpen(sqlite3_vfs *pVfs, const char *zPath){777 return sqlite3OsDlOpen(PARENTVFS(pVfs), zPath);778}779 780/*781** Populate the buffer zErrMsg (size nByte bytes) with a human readable782** utf-8 string describing the most recent error encountered associated 783** with dynamic libraries.784*/785static void tvfsDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){786 sqlite3OsDlError(PARENTVFS(pVfs), nByte, zErrMsg);787}788 789/*790** Return a pointer to the symbol zSymbol in the dynamic library pHandle.791*/792static void (*tvfsDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){793 return sqlite3OsDlSym(PARENTVFS(pVfs), p, zSym);794}795 796/*797** Close the dynamic library handle pHandle.798*/799static void tvfsDlClose(sqlite3_vfs *pVfs, void *pHandle){800 sqlite3OsDlClose(PARENTVFS(pVfs), pHandle);801}802#endif /* SQLITE_OMIT_LOAD_EXTENSION */803 804/*805** Populate the buffer pointed to by zBufOut with nByte bytes of 806** random data.807*/808static int tvfsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){809 return sqlite3OsRandomness(PARENTVFS(pVfs), nByte, zBufOut);810}811 812/*813** Sleep for nMicro microseconds. Return the number of microseconds 814** actually slept.815*/816static int tvfsSleep(sqlite3_vfs *pVfs, int nMicro){817 Testvfs *p = (Testvfs *)pVfs->pAppData;818 if( p->pScript && (p->mask&TESTVFS_SLEEP_MASK) ){819 tvfsExecTcl(p, "xSleep", Tcl_NewIntObj(nMicro), 0, 0, 0);820 }821 return sqlite3OsSleep(PARENTVFS(pVfs), nMicro);822}823 824/*825** Return the current time as a Julian Day number in *pTimeOut.826*/827static int tvfsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){828 return PARENTVFS(pVfs)->xCurrentTime(PARENTVFS(pVfs), pTimeOut);829}830 831static int tvfsShmOpen(sqlite3_file *pFile){832 Testvfs *p;833 int rc = SQLITE_OK; /* Return code */834 TestvfsBuffer *pBuffer; /* Buffer to open connection to */835 TestvfsFd *pFd; /* The testvfs file structure */836 837 pFd = tvfsGetFd(pFile);838 p = (Testvfs *)pFd->pVfs->pAppData;839 assert( 0==p->isFullshm );840 assert( pFd->pShmId && pFd->pShm==0 && pFd->pNext==0 );841 842 /* Evaluate the Tcl script: 843 **844 ** SCRIPT xShmOpen FILENAME845 */846 Tcl_ResetResult(p->interp);847 if( p->pScript && p->mask&TESTVFS_SHMOPEN_MASK ){848 tvfsExecTcl(p, "xShmOpen", Tcl_NewStringObj(pFd->zFilename, -1), 0, 0, 0);849 if( tvfsResultCode(p, &rc) ){850 if( rc!=SQLITE_OK ) return rc;851 }852 }853 854 assert( rc==SQLITE_OK );855 if( p->mask&TESTVFS_SHMOPEN_MASK && tvfsInjectIoerr(p) ){856 return SQLITE_IOERR;857 }858 859 /* Search for a TestvfsBuffer. Create a new one if required. */860 for(pBuffer=p->pBuffer; pBuffer; pBuffer=pBuffer->pNext){861 if( 0==strcmp(pFd->zFilename, pBuffer->zFile) ) break;862 }863 if( !pBuffer ){864 int szName = (int)strlen(pFd->zFilename);865 int nByte = sizeof(TestvfsBuffer) + szName + 1;866 pBuffer = (TestvfsBuffer *)ckalloc(nByte);867 memset(pBuffer, 0, nByte);868 pBuffer->zFile = (char *)&pBuffer[1];869 memcpy(pBuffer->zFile, pFd->zFilename, szName+1);870 pBuffer->pNext = p->pBuffer;871 p->pBuffer = pBuffer;872 }873 874 /* Connect the TestvfsBuffer to the new TestvfsShm handle and return. */875 pFd->pNext = pBuffer->pFile;876 pBuffer->pFile = pFd;877 pFd->pShm = pBuffer;878 return rc;879}880 881static void tvfsAllocPage(TestvfsBuffer *p, int iPage, int pgsz){882 assert( iPage<TESTVFS_MAX_PAGES );883 if( p->aPage[iPage]==0 ){884 p->aPage[iPage] = (u8 *)ckalloc(pgsz);885 memset(p->aPage[iPage], 0, pgsz);886 p->pgsz = pgsz;887 }888}889 890static int tvfsShmMap(891 sqlite3_file *pFile, /* Handle open on database file */892 int iPage, /* Page to retrieve */893 int pgsz, /* Size of pages */894 int isWrite, /* True to extend file if necessary */895 void volatile **pp /* OUT: Mapped memory */896){897 int rc = SQLITE_OK;898 TestvfsFd *pFd = tvfsGetFd(pFile);899 Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);900 901 if( p->isFullshm ){902 sqlite3_file *pReal = pFd->pReal;903 return pReal->pMethods->xShmMap(pReal, iPage, pgsz, isWrite, pp);904 }905 906 if( 0==pFd->pShm ){907 rc = tvfsShmOpen(pFile);908 if( rc!=SQLITE_OK ){909 return rc;910 }911 }912 913 if( p->pScript && p->mask&TESTVFS_SHMMAP_MASK ){914 Tcl_Obj *pArg = Tcl_NewObj();915 Tcl_IncrRefCount(pArg);916 Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(iPage));917 Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(pgsz));918 Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(isWrite));919 tvfsExecTcl(p, "xShmMap", 920 Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, pArg, 0921 );922 tvfsResultCode(p, &rc);923 Tcl_DecrRefCount(pArg);924 }925 if( rc==SQLITE_OK && p->mask&TESTVFS_SHMMAP_MASK && tvfsInjectIoerr(p) ){926 rc = SQLITE_IOERR;927 }928 929 if( rc==SQLITE_OK && isWrite && !pFd->pShm->aPage[iPage] ){930 tvfsAllocPage(pFd->pShm, iPage, pgsz);931 }932 if( rc==SQLITE_OK || rc==SQLITE_READONLY ){933 *pp = (void volatile *)pFd->pShm->aPage[iPage];934 }935 936 return rc;937}938 939 940static int tvfsShmLock(941 sqlite3_file *pFile,942 int ofst,943 int n,944 int flags945){946 int rc = SQLITE_OK;947 TestvfsFd *pFd = tvfsGetFd(pFile);948 Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);949 int nLock;950 char zLock[80];951 952 if( p->isFullshm ){953 sqlite3_file *pReal = pFd->pReal;954 return pReal->pMethods->xShmLock(pReal, ofst, n, flags);955 }956 957 if( p->pScript && p->mask&TESTVFS_SHMLOCK_MASK ){958 sqlite3_snprintf(sizeof(zLock), zLock, "%d %d", ofst, n);959 nLock = (int)strlen(zLock);960 if( flags & SQLITE_SHM_LOCK ){961 strcpy(&zLock[nLock], " lock");962 }else{963 strcpy(&zLock[nLock], " unlock");964 }965 nLock += (int)strlen(&zLock[nLock]);966 if( flags & SQLITE_SHM_SHARED ){967 strcpy(&zLock[nLock], " shared");968 }else{969 strcpy(&zLock[nLock], " exclusive");970 }971 tvfsExecTcl(p, "xShmLock", 972 Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId,973 Tcl_NewStringObj(zLock, -1), 0974 );975 tvfsResultCode(p, &rc);976 }977 978 if( rc==SQLITE_OK && p->mask&TESTVFS_SHMLOCK_MASK && tvfsInjectIoerr(p) ){979 rc = SQLITE_IOERR;980 }981 982 if( rc==SQLITE_OK ){983 int isLock = (flags & SQLITE_SHM_LOCK);984 int isExcl = (flags & SQLITE_SHM_EXCLUSIVE);985 u32 mask = (((1<<n)-1) << ofst);986 if( isLock ){987 TestvfsFd *p2;988 for(p2=pFd->pShm->pFile; p2; p2=p2->pNext){989 if( p2==pFd ) continue;990 if( (p2->excllock&mask) || (isExcl && p2->sharedlock&mask) ){991 rc = SQLITE_BUSY;992 break;993 }994 }995 if( rc==SQLITE_OK ){996 if( isExcl ) pFd->excllock |= mask;997 if( !isExcl ) pFd->sharedlock |= mask;998 }999 }else{1000 if( isExcl ) pFd->excllock &= (~mask);1001 if( !isExcl ) pFd->sharedlock &= (~mask);1002 }1003 }1004 1005 return rc;1006}1007 1008static void tvfsShmBarrier(sqlite3_file *pFile){1009 TestvfsFd *pFd = tvfsGetFd(pFile);1010 Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);1011 1012 if( p->pScript && p->mask&TESTVFS_SHMBARRIER_MASK ){1013 const char *z = pFd->pShm ? pFd->pShm->zFile : "";1014 tvfsExecTcl(p, "xShmBarrier", Tcl_NewStringObj(z, -1), pFd->pShmId, 0, 0);1015 }1016 1017 if( p->isFullshm ){1018 sqlite3_file *pReal = pFd->pReal;1019 pReal->pMethods->xShmBarrier(pReal);1020 return;1021 }1022}1023 1024static int tvfsShmUnmap(1025 sqlite3_file *pFile,1026 int deleteFlag1027){1028 int rc = SQLITE_OK;1029 TestvfsFd *pFd = tvfsGetFd(pFile);1030 Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);1031 TestvfsBuffer *pBuffer = pFd->pShm;1032 TestvfsFd **ppFd;1033 1034 if( p->isFullshm ){1035 sqlite3_file *pReal = pFd->pReal;1036 return pReal->pMethods->xShmUnmap(pReal, deleteFlag);1037 }1038 1039 if( !pBuffer ) return SQLITE_OK;1040 assert( pFd->pShmId && pFd->pShm );1041 1042 if( p->pScript && p->mask&TESTVFS_SHMCLOSE_MASK ){1043 tvfsExecTcl(p, "xShmUnmap", 1044 Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0, 01045 );1046 tvfsResultCode(p, &rc);1047 }1048 1049 for(ppFd=&pBuffer->pFile; *ppFd!=pFd; ppFd=&((*ppFd)->pNext));1050 assert( (*ppFd)==pFd );1051 *ppFd = pFd->pNext;1052 pFd->pNext = 0;1053 1054 if( pBuffer->pFile==0 ){1055 int i;1056 TestvfsBuffer **pp;1057 for(pp=&p->pBuffer; *pp!=pBuffer; pp=&((*pp)->pNext));1058 *pp = (*pp)->pNext;1059 for(i=0; pBuffer->aPage[i]; i++){1060 ckfree((char *)pBuffer->aPage[i]);1061 }1062 ckfree((char *)pBuffer);1063 }1064 pFd->pShm = 0;1065 1066 return rc;1067}1068 1069static int tvfsFetch(1070 sqlite3_file *pFile, 1071 sqlite3_int64 iOfst, 1072 int iAmt, 1073 void **pp1074){1075 TestvfsFd *pFd = tvfsGetFd(pFile);1076 return sqlite3OsFetch(pFd->pReal, iOfst, iAmt, pp);1077}1078 1079static int tvfsUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *p){1080 TestvfsFd *pFd = tvfsGetFd(pFile);1081 return sqlite3OsUnfetch(pFd->pReal, iOfst, p);1082}1083 1084static int SQLITE_TCLAPI testvfs_obj_cmd(1085 ClientData cd,1086 Tcl_Interp *interp,1087 int objc,1088 Tcl_Obj *CONST objv[]1089){1090 Testvfs *p = (Testvfs *)cd;1091 1092 enum DB_enum { 1093 CMD_SHM, CMD_DELETE, CMD_FILTER, CMD_IOERR, CMD_SCRIPT, 1094 CMD_DEVCHAR, CMD_SECTORSIZE, CMD_FULLERR, CMD_CANTOPENERR1095 };1096 struct TestvfsSubcmd {1097 char *zName;1098 enum DB_enum eCmd;1099 } aSubcmd[] = {1100 { "shm", CMD_SHM },1101 { "delete", CMD_DELETE },1102 { "filter", CMD_FILTER },1103 { "ioerr", CMD_IOERR },1104 { "fullerr", CMD_FULLERR },1105 { "cantopenerr", CMD_CANTOPENERR },1106 { "script", CMD_SCRIPT },1107 { "devchar", CMD_DEVCHAR },1108 { "sectorsize", CMD_SECTORSIZE },1109 { 0, 0 }1110 };1111 int i;1112 1113 if( objc<2 ){1114 Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");1115 return TCL_ERROR;1116 }1117 if( Tcl_GetIndexFromObjStruct(1118 interp, objv[1], aSubcmd, sizeof(aSubcmd[0]), "subcommand", 0, &i) 1119 ){1120 return TCL_ERROR;1121 }1122 Tcl_ResetResult(interp);1123 1124 switch( aSubcmd[i].eCmd ){1125 case CMD_SHM: {1126 Tcl_Obj *pObj;1127 int rc;1128 TestvfsBuffer *pBuffer;1129 char *zName;1130 if( objc!=3 && objc!=4 ){1131 Tcl_WrongNumArgs(interp, 2, objv, "FILE ?VALUE?");1132 return TCL_ERROR;1133 }1134 zName = ckalloc(p->pParent->mxPathname);1135 rc = p->pParent->xFullPathname(1136 p->pParent, Tcl_GetString(objv[2]), 1137 p->pParent->mxPathname, zName1138 );1139 if( rc!=SQLITE_OK ){1140 Tcl_AppendResult(interp, "failed to get full path: ",1141 Tcl_GetString(objv[2]), NULL);1142 ckfree(zName);1143 return TCL_ERROR;1144 }1145 for(pBuffer=p->pBuffer; pBuffer; pBuffer=pBuffer->pNext){1146 if( 0==strcmp(pBuffer->zFile, zName) ) break;1147 }1148 ckfree(zName);1149 if( !pBuffer ){1150 Tcl_AppendResult(interp, "no such file: ", Tcl_GetString(objv[2]), NULL);1151 return TCL_ERROR;1152 }1153 if( objc==4 ){1154 Tcl_Size n;1155 u8 *a = Tcl_GetByteArrayFromObj(objv[3], &n);1156 int pgsz = pBuffer->pgsz;1157 if( pgsz==0 ) pgsz = 65536;1158 for(i=0; i*pgsz<(int)n; i++){1159 int nByte = pgsz;1160 tvfsAllocPage(pBuffer, i, pgsz);1161 if( n-i*pgsz<pgsz ){1162 nByte = (int)n;1163 }1164 memcpy(pBuffer->aPage[i], &a[i*pgsz], nByte);1165 }1166 }1167 1168 pObj = Tcl_NewObj();1169 for(i=0; pBuffer->aPage[i]; i++){1170 int pgsz = pBuffer->pgsz;1171 if( pgsz==0 ) pgsz = 65536;1172 Tcl_AppendObjToObj(pObj, Tcl_NewByteArrayObj(pBuffer->aPage[i], pgsz));1173 }1174 Tcl_SetObjResult(interp, pObj);1175 break;1176 }1177 1178 /* TESTVFS filter METHOD-LIST1179 **1180 ** Activate special processing for those methods contained in the list1181 */1182 case CMD_FILTER: {1183 static struct VfsMethod {1184 char *zName;1185 int mask;1186 } vfsmethod [] = {1187 { "xShmOpen", TESTVFS_SHMOPEN_MASK },1188 { "xShmLock", TESTVFS_SHMLOCK_MASK },1189 { "xShmBarrier", TESTVFS_SHMBARRIER_MASK },1190 { "xShmUnmap", TESTVFS_SHMCLOSE_MASK },1191 { "xShmMap", TESTVFS_SHMMAP_MASK },1192 { "xSync", TESTVFS_SYNC_MASK },1193 { "xDelete", TESTVFS_DELETE_MASK },1194 { "xWrite", TESTVFS_WRITE_MASK },1195 { "xRead", TESTVFS_READ_MASK },1196 { "xTruncate", TESTVFS_TRUNCATE_MASK },1197 { "xOpen", TESTVFS_OPEN_MASK },1198 { "xClose", TESTVFS_CLOSE_MASK },1199 { "xAccess", TESTVFS_ACCESS_MASK },1200 { "xFullPathname", TESTVFS_FULLPATHNAME_MASK },