/* ** Test for access permissions. Return true if the requested permission ** is available, or false otherwise. */ static int tvfsAccess( sqlite3_vfs *pVfs, const char *zPath, int flags, int *pResOut ){ Testvfs *p = (Testvfs *)pVfs->pAppData; if( p->pScript && p->mask&TESTVFS_ACCESS_MASK ){ int rc; char *zArg = 0; if( flags==SQLITE_ACCESS_EXISTS ) zArg = "SQLITE_ACCESS_EXISTS"; if( flags==SQLITE_ACCESS_READWRITE ) zArg = "SQLITE_ACCESS_READWRITE"; if( flags==SQLITE_ACCESS_READ ) zArg = "SQLITE_ACCESS_READ"; tvfsExecTcl(p, "xAccess", Tcl_NewStringObj(zPath, -1), Tcl_NewStringObj(zArg, -1), 0, 0 ); if( tvfsResultCode(p, &rc) ){ if( rc!=SQLITE_OK ) return rc; }else{ Tcl_Interp *interp = p->interp; if( TCL_OK==Tcl_GetBooleanFromObj(0, Tcl_GetObjResult(interp), pResOut) ){ return SQLITE_OK; } } } return sqlite3OsAccess(PARENTVFS(pVfs), zPath, flags, pResOut); }
/* ** Write data to an tvfs-file. */ static int tvfsWrite( sqlite3_file *pFile, const void *zBuf, int iAmt, sqlite_int64 iOfst ){ int rc = SQLITE_OK; TestvfsFd *pFd = tvfsGetFd(pFile); Testvfs *p = (Testvfs *)pFd->pVfs->pAppData; if( p->pScript && p->mask&TESTVFS_WRITE_MASK ){ tvfsExecTcl(p, "xWrite", Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, Tcl_NewWideIntObj(iOfst), Tcl_NewIntObj(iAmt) ); tvfsResultCode(p, &rc); } if( rc==SQLITE_OK && tvfsInjectFullerr(p) ){ rc = SQLITE_FULL; } if( rc==SQLITE_OK && p->mask&TESTVFS_WRITE_MASK && tvfsInjectIoerr(p) ){ rc = SQLITE_IOERR; } if( rc==SQLITE_OK ){ rc = sqlite3OsWrite(pFd->pReal, zBuf, iAmt, iOfst); } return rc; }
/* ** Check if another file-handle holds a RESERVED lock on an tvfs-file. */ static int tvfsCheckReservedLock(sqlite3_file *pFile, int *pResOut){ TestvfsFd *pFd = tvfsGetFd(pFile); Testvfs *p = (Testvfs *)pFd->pVfs->pAppData; if( p->pScript && p->mask&TESTVFS_CKLOCK_MASK ){ tvfsExecTcl(p, "xCheckReservedLock", Tcl_NewStringObj(pFd->zFilename, -1), 0, 0, 0); } return sqlite3OsCheckReservedLock(pFd->pReal, pResOut); }
static void tvfsShmBarrier(sqlite3_file *pFile){ TestvfsFd *pFd = tvfsGetFd(pFile); Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData); if( p->pScript && p->mask&TESTVFS_SHMBARRIER_MASK ){ tvfsExecTcl(p, "xShmBarrier", Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0 ); } }
/* ** Lock an tvfs-file. */ static int tvfsLock(sqlite3_file *pFile, int eLock){ TestvfsFd *pFd = tvfsGetFd(pFile); Testvfs *p = (Testvfs *)pFd->pVfs->pAppData; if( p->pScript && p->mask&TESTVFS_LOCK_MASK ){ char zLock[30]; sqlite3_snprintf(sizeof(zLock),zLock,"%d",eLock); tvfsExecTcl(p, "xLock", Tcl_NewStringObj(pFd->zFilename, -1), Tcl_NewStringObj(zLock, -1), 0, 0); } return sqlite3OsLock(pFd->pReal, eLock); }
/* ** Unlock an tvfs-file. */ static int tvfsUnlock(sqlite3_file *pFile, int eLock){ TestvfsFd *pFd = tvfsGetFd(pFile); Testvfs *p = (Testvfs *)pFd->pVfs->pAppData; if( p->pScript && p->mask&TESTVFS_UNLOCK_MASK ){ char zLock[30]; sqlite3_snprintf(sizeof(zLock),zLock,"%d",eLock); tvfsExecTcl(p, "xUnlock", Tcl_NewStringObj(pFd->zFilename, -1), Tcl_NewStringObj(zLock, -1), 0, 0); } if( p->mask&TESTVFS_WRITE_MASK && tvfsInjectIoerr(p) ){ return SQLITE_IOERR_UNLOCK; } return sqlite3OsUnlock(pFd->pReal, eLock); }
/* ** Delete the file located at zPath. If the dirSync argument is true, ** ensure the file-system modifications are synced to disk before ** returning. */ static int tvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ int rc = SQLITE_OK; Testvfs *p = (Testvfs *)pVfs->pAppData; if( p->pScript && p->mask&TESTVFS_DELETE_MASK ){ tvfsExecTcl(p, "xDelete", Tcl_NewStringObj(zPath, -1), Tcl_NewIntObj(dirSync), 0, 0 ); tvfsResultCode(p, &rc); } if( rc==SQLITE_OK ){ rc = sqlite3OsDelete(PARENTVFS(pVfs), zPath, dirSync); } return rc; }
static int tvfsShmOpen(sqlite3_file *pFile){ Testvfs *p; int rc = SQLITE_OK; /* Return code */ TestvfsBuffer *pBuffer; /* Buffer to open connection to */ TestvfsFd *pFd; /* The testvfs file structure */ pFd = tvfsGetFd(pFile); p = (Testvfs *)pFd->pVfs->pAppData; assert( 0==p->isFullshm ); assert( pFd->pShmId && pFd->pShm==0 && pFd->pNext==0 ); /* Evaluate the Tcl script: ** ** SCRIPT xShmOpen FILENAME */ Tcl_ResetResult(p->interp); if( p->pScript && p->mask&TESTVFS_SHMOPEN_MASK ){ tvfsExecTcl(p, "xShmOpen", Tcl_NewStringObj(pFd->zFilename, -1), 0, 0, 0); if( tvfsResultCode(p, &rc) ){ if( rc!=SQLITE_OK ) return rc; } } assert( rc==SQLITE_OK ); if( p->mask&TESTVFS_SHMOPEN_MASK && tvfsInjectIoerr(p) ){ return SQLITE_IOERR; } /* Search for a TestvfsBuffer. Create a new one if required. */ for(pBuffer=p->pBuffer; pBuffer; pBuffer=pBuffer->pNext){ if( 0==strcmp(pFd->zFilename, pBuffer->zFile) ) break; } if( !pBuffer ){ int nByte = sizeof(TestvfsBuffer) + (int)strlen(pFd->zFilename) + 1; pBuffer = (TestvfsBuffer *)ckalloc(nByte); memset(pBuffer, 0, nByte); pBuffer->zFile = (char *)&pBuffer[1]; strcpy(pBuffer->zFile, pFd->zFilename); pBuffer->pNext = p->pBuffer; p->pBuffer = pBuffer; } /* Connect the TestvfsBuffer to the new TestvfsShm handle and return. */ pFd->pNext = pBuffer->pFile; pBuffer->pFile = pFd; pFd->pShm = pBuffer; return SQLITE_OK; }
/* ** Populate buffer zOut with the full canonical pathname corresponding ** to the pathname in zPath. zOut is guaranteed to point to a buffer ** of at least (DEVSYM_MAX_PATHNAME+1) bytes. */ static int tvfsFullPathname( sqlite3_vfs *pVfs, const char *zPath, int nOut, char *zOut ){ Testvfs *p = (Testvfs *)pVfs->pAppData; if( p->pScript && p->mask&TESTVFS_FULLPATHNAME_MASK ){ int rc; tvfsExecTcl(p, "xFullPathname", Tcl_NewStringObj(zPath, -1), 0, 0, 0); if( tvfsResultCode(p, &rc) ){ if( rc!=SQLITE_OK ) return rc; } } return sqlite3OsFullPathname(PARENTVFS(pVfs), zPath, nOut, zOut); }
/* ** Truncate an tvfs-file. */ static int tvfsTruncate(sqlite3_file *pFile, sqlite_int64 size){ int rc = SQLITE_OK; TestvfsFd *pFd = tvfsGetFd(pFile); Testvfs *p = (Testvfs *)pFd->pVfs->pAppData; if( p->pScript && p->mask&TESTVFS_TRUNCATE_MASK ){ tvfsExecTcl(p, "xTruncate", Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0, 0 ); tvfsResultCode(p, &rc); } if( rc==SQLITE_OK ){ rc = sqlite3OsTruncate(pFd->pReal, size); } return rc; }
static int tvfsShmMap( sqlite3_file *pFile, /* Handle open on database file */ int iPage, /* Page to retrieve */ int pgsz, /* Size of pages */ int isWrite, /* True to extend file if necessary */ void volatile **pp /* OUT: Mapped memory */ ){ int rc = SQLITE_OK; TestvfsFd *pFd = tvfsGetFd(pFile); Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData); if( p->isFullshm ){ return sqlite3OsShmMap(pFd->pReal, iPage, pgsz, isWrite, pp); } if( 0==pFd->pShm ){ rc = tvfsShmOpen(pFile); if( rc!=SQLITE_OK ){ return rc; } } if( p->pScript && p->mask&TESTVFS_SHMMAP_MASK ){ Tcl_Obj *pArg = Tcl_NewObj(); Tcl_IncrRefCount(pArg); Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(iPage)); Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(pgsz)); Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(isWrite)); tvfsExecTcl(p, "xShmMap", Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, pArg, 0 ); tvfsResultCode(p, &rc); Tcl_DecrRefCount(pArg); } if( rc==SQLITE_OK && p->mask&TESTVFS_SHMMAP_MASK && tvfsInjectIoerr(p) ){ rc = SQLITE_IOERR; } if( rc==SQLITE_OK && isWrite && !pFd->pShm->aPage[iPage] ){ tvfsAllocPage(pFd->pShm, iPage, pgsz); } *pp = (void volatile *)pFd->pShm->aPage[iPage]; return rc; }
static int tvfsShmUnmap( sqlite3_file *pFile, int deleteFlag ){ int rc = SQLITE_OK; TestvfsFd *pFd = tvfsGetFd(pFile); Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData); TestvfsBuffer *pBuffer = pFd->pShm; TestvfsFd **ppFd; if( p->isFullshm ){ return sqlite3OsShmUnmap(pFd->pReal, deleteFlag); } if( !pBuffer ) return SQLITE_OK; assert( pFd->pShmId && pFd->pShm ); if( p->pScript && p->mask&TESTVFS_SHMCLOSE_MASK ){ tvfsExecTcl(p, "xShmUnmap", Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0, 0 ); tvfsResultCode(p, &rc); } for(ppFd=&pBuffer->pFile; *ppFd!=pFd; ppFd=&((*ppFd)->pNext)); assert( (*ppFd)==pFd ); *ppFd = pFd->pNext; pFd->pNext = 0; if( pBuffer->pFile==0 ){ int i; TestvfsBuffer **pp; for(pp=&p->pBuffer; *pp!=pBuffer; pp=&((*pp)->pNext)); *pp = (*pp)->pNext; for(i=0; pBuffer->aPage[i]; i++){ ckfree((char *)pBuffer->aPage[i]); } ckfree((char *)pBuffer); } pFd->pShm = 0; return rc; }
/* ** Sync an tvfs-file. */ static int tvfsSync(sqlite3_file *pFile, int flags){ int rc = SQLITE_OK; TestvfsFd *pFd = tvfsGetFd(pFile); Testvfs *p = (Testvfs *)pFd->pVfs->pAppData; if( p->pScript && p->mask&TESTVFS_SYNC_MASK ){ char *zFlags; switch( flags ){ case SQLITE_SYNC_NORMAL: zFlags = "normal"; break; case SQLITE_SYNC_FULL: zFlags = "full"; break; case SQLITE_SYNC_NORMAL|SQLITE_SYNC_DATAONLY: zFlags = "normal|dataonly"; break; case SQLITE_SYNC_FULL|SQLITE_SYNC_DATAONLY: zFlags = "full|dataonly"; break; default: assert(0); } tvfsExecTcl(p, "xSync", Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, Tcl_NewStringObj(zFlags, -1), 0 ); tvfsResultCode(p, &rc); } if( rc==SQLITE_OK && tvfsInjectFullerr(p) ) rc = SQLITE_FULL; if( rc==SQLITE_OK ){ rc = sqlite3OsSync(pFd->pReal, flags); } return rc; }
/* ** Read data from an tvfs-file. */ static int tvfsRead( sqlite3_file *pFile, void *zBuf, int iAmt, sqlite_int64 iOfst ){ int rc = SQLITE_OK; TestvfsFd *pFd = tvfsGetFd(pFile); Testvfs *p = (Testvfs *)pFd->pVfs->pAppData; if( p->pScript && p->mask&TESTVFS_READ_MASK ){ tvfsExecTcl(p, "xRead", Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0, 0 ); tvfsResultCode(p, &rc); } if( rc==SQLITE_OK && p->mask&TESTVFS_READ_MASK && tvfsInjectIoerr(p) ){ rc = SQLITE_IOERR; } if( rc==SQLITE_OK ){ rc = sqlite3OsRead(pFd->pReal, zBuf, iAmt, iOfst); } return rc; }
/* ** Close an tvfs-file. */ static int tvfsClose(sqlite3_file *pFile){ int rc; TestvfsFile *pTestfile = (TestvfsFile *)pFile; TestvfsFd *pFd = pTestfile->pFd; Testvfs *p = (Testvfs *)pFd->pVfs->pAppData; if( p->pScript && p->mask&TESTVFS_CLOSE_MASK ){ tvfsExecTcl(p, "xClose", Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0, 0 ); } if( pFd->pShmId ){ Tcl_DecrRefCount(pFd->pShmId); pFd->pShmId = 0; } if( pFile->pMethods ){ ckfree((char *)pFile->pMethods); } rc = sqlite3OsClose(pFd->pReal); ckfree((char *)pFd); pTestfile->pFd = 0; return rc; }
static int tvfsShmLock( sqlite3_file *pFile, int ofst, int n, int flags ){ int rc = SQLITE_OK; TestvfsFd *pFd = tvfsGetFd(pFile); Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData); int nLock; char zLock[80]; if( p->isFullshm ){ return sqlite3OsShmLock(pFd->pReal, ofst, n, flags); } if( p->pScript && p->mask&TESTVFS_SHMLOCK_MASK ){ sqlite3_snprintf(sizeof(zLock), zLock, "%d %d", ofst, n); nLock = (int)strlen(zLock); if( flags & SQLITE_SHM_LOCK ){ strcpy(&zLock[nLock], " lock"); }else{ strcpy(&zLock[nLock], " unlock"); } nLock += (int)strlen(&zLock[nLock]); if( flags & SQLITE_SHM_SHARED ){ strcpy(&zLock[nLock], " shared"); }else{ strcpy(&zLock[nLock], " exclusive"); } tvfsExecTcl(p, "xShmLock", Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, Tcl_NewStringObj(zLock, -1), 0 ); tvfsResultCode(p, &rc); } if( rc==SQLITE_OK && p->mask&TESTVFS_SHMLOCK_MASK && tvfsInjectIoerr(p) ){ rc = SQLITE_IOERR; } if( rc==SQLITE_OK ){ int isLock = (flags & SQLITE_SHM_LOCK); int isExcl = (flags & SQLITE_SHM_EXCLUSIVE); u32 mask = (((1<<n)-1) << ofst); if( isLock ){ TestvfsFd *p2; for(p2=pFd->pShm->pFile; p2; p2=p2->pNext){ if( p2==pFd ) continue; if( (p2->excllock&mask) || (isExcl && p2->sharedlock&mask) ){ rc = SQLITE_BUSY; break; } } if( rc==SQLITE_OK ){ if( isExcl ) pFd->excllock |= mask; if( !isExcl ) pFd->sharedlock |= mask; } }else{ if( isExcl ) pFd->excllock &= (~mask); if( !isExcl ) pFd->sharedlock &= (~mask); } } return rc; }
/* ** Open an tvfs file handle. */ static int tvfsOpen( sqlite3_vfs *pVfs, const char *zName, sqlite3_file *pFile, int flags, int *pOutFlags ){ int rc; TestvfsFile *pTestfile = (TestvfsFile *)pFile; TestvfsFd *pFd; Tcl_Obj *pId = 0; Testvfs *p = (Testvfs *)pVfs->pAppData; pFd = (TestvfsFd *)ckalloc(sizeof(TestvfsFd) + PARENTVFS(pVfs)->szOsFile); memset(pFd, 0, sizeof(TestvfsFd) + PARENTVFS(pVfs)->szOsFile); pFd->pShm = 0; pFd->pShmId = 0; pFd->zFilename = zName; pFd->pVfs = pVfs; pFd->pReal = (sqlite3_file *)&pFd[1]; memset(pTestfile, 0, sizeof(TestvfsFile)); pTestfile->pFd = pFd; /* Evaluate the Tcl script: ** ** SCRIPT xOpen FILENAME KEY-VALUE-ARGS ** ** If the script returns an SQLite error code other than SQLITE_OK, an ** error is returned to the caller. If it returns SQLITE_OK, the new ** connection is named "anon". Otherwise, the value returned by the ** script is used as the connection name. */ Tcl_ResetResult(p->interp); if( p->pScript && p->mask&TESTVFS_OPEN_MASK ){ Tcl_Obj *pArg = Tcl_NewObj(); Tcl_IncrRefCount(pArg); if( flags&SQLITE_OPEN_MAIN_DB ){ const char *z = &zName[strlen(zName)+1]; while( *z ){ Tcl_ListObjAppendElement(0, pArg, Tcl_NewStringObj(z, -1)); z += strlen(z) + 1; Tcl_ListObjAppendElement(0, pArg, Tcl_NewStringObj(z, -1)); z += strlen(z) + 1; } } tvfsExecTcl(p, "xOpen", Tcl_NewStringObj(pFd->zFilename, -1), pArg, 0, 0); Tcl_DecrRefCount(pArg); if( tvfsResultCode(p, &rc) ){ if( rc!=SQLITE_OK ) return rc; }else{ pId = Tcl_GetObjResult(p->interp); } } if( (p->mask&TESTVFS_OPEN_MASK) && tvfsInjectIoerr(p) ) return SQLITE_IOERR; if( tvfsInjectCantopenerr(p) ) return SQLITE_CANTOPEN; if( tvfsInjectFullerr(p) ) return SQLITE_FULL; if( !pId ){ pId = Tcl_NewStringObj("anon", -1); } Tcl_IncrRefCount(pId); pFd->pShmId = pId; Tcl_ResetResult(p->interp); rc = sqlite3OsOpen(PARENTVFS(pVfs), zName, pFd->pReal, flags, pOutFlags); if( pFd->pReal->pMethods ){ sqlite3_io_methods *pMethods; int nByte; if( pVfs->iVersion>1 ){ nByte = sizeof(sqlite3_io_methods); }else{ nByte = offsetof(sqlite3_io_methods, xShmMap); } pMethods = (sqlite3_io_methods *)ckalloc(nByte); memcpy(pMethods, &tvfs_io_methods, nByte); pMethods->iVersion = pVfs->iVersion; if( pVfs->iVersion>1 && ((Testvfs *)pVfs->pAppData)->isNoshm ){ pMethods->xShmUnmap = 0; pMethods->xShmLock = 0; pMethods->xShmBarrier = 0; pMethods->xShmMap = 0; } pFile->pMethods = pMethods; } return rc; }