dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 1 | /* |
| 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 | |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 19 | typedef struct Testvfs Testvfs; |
| 20 | typedef struct TestvfsShm TestvfsShm; |
| 21 | typedef struct TestvfsBuffer TestvfsBuffer; |
dan | 7fd555a | 2010-05-13 06:19:37 +0000 | [diff] [blame] | 22 | typedef struct TestvfsFile TestvfsFile; |
| 23 | |
| 24 | /* |
| 25 | ** An open file handle. |
| 26 | */ |
| 27 | struct 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 */ |
dan | ef4ee8f | 2010-06-05 11:53:34 +0000 | [diff] [blame] | 33 | |
dan | 7fd555a | 2010-05-13 06:19:37 +0000 | [diff] [blame] | 34 | TestvfsBuffer *pShm; /* Shared memory buffer */ |
dan | ef4ee8f | 2010-06-05 11:53:34 +0000 | [diff] [blame] | 35 | u32 excllock; /* Mask of exclusive locks */ |
| 36 | u32 sharedlock; /* Mask of shared locks */ |
| 37 | TestvfsFile *pNext; /* Next handle opened on the same file */ |
drh | d9e5c4f | 2010-05-12 18:01:39 +0000 | [diff] [blame] | 38 | }; |
| 39 | |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 40 | |
| 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 | */ |
| 46 | struct 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 */ |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 51 | Tcl_Obj *pScript; /* Script to execute */ |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 52 | int nScript; /* Number of elements in array apScript */ |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 53 | Tcl_Obj **apScript; /* Array version of pScript */ |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 54 | TestvfsBuffer *pBuffer; /* List of shared buffers */ |
dan | 7fd555a | 2010-05-13 06:19:37 +0000 | [diff] [blame] | 55 | int isNoshm; |
dan | 0235ab9 | 2010-06-01 19:15:18 +0000 | [diff] [blame] | 56 | |
| 57 | int mask; |
| 58 | int iIoerrCnt; |
| 59 | int ioerr; |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 60 | int nIoerrFail; |
dan | 2a321c7 | 2010-06-16 19:04:23 +0000 | [diff] [blame] | 61 | |
dan | 146ed78 | 2010-06-19 17:26:37 +0000 | [diff] [blame] | 62 | int iFullCnt; |
| 63 | int fullerr; |
| 64 | int nFullFail; |
| 65 | |
dan | 2a321c7 | 2010-06-16 19:04:23 +0000 | [diff] [blame] | 66 | int iDevchar; |
| 67 | int iSectorsize; |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 68 | }; |
| 69 | |
| 70 | /* |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 71 | ** 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 |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 79 | #define TESTVFS_SHMLOCK_MASK 0x00000010 |
drh | 6b017cc | 2010-06-14 18:01:46 +0000 | [diff] [blame] | 80 | #define TESTVFS_SHMMAP_MASK 0x00000020 |
| 81 | #define TESTVFS_SHMBARRIER_MASK 0x00000040 |
| 82 | #define TESTVFS_SHMCLOSE_MASK 0x00000080 |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 83 | |
dan | 13a3cb8 | 2010-06-11 19:04:21 +0000 | [diff] [blame] | 84 | #define TESTVFS_OPEN_MASK 0x00000100 |
| 85 | #define TESTVFS_SYNC_MASK 0x00000200 |
dan | b0ac3e3 | 2010-06-16 10:55:42 +0000 | [diff] [blame] | 86 | #define TESTVFS_DELETE_MASK 0x00000400 |
dan | 2a321c7 | 2010-06-16 19:04:23 +0000 | [diff] [blame] | 87 | #define TESTVFS_CLOSE_MASK 0x00000800 |
| 88 | #define TESTVFS_WRITE_MASK 0x00001000 |
| 89 | #define TESTVFS_TRUNCATE_MASK 0x00002000 |
| 90 | #define TESTVFS_ALL_MASK 0x00003FFF |
dan | 13a3cb8 | 2010-06-11 19:04:21 +0000 | [diff] [blame] | 91 | |
| 92 | |
| 93 | #define TESTVFS_MAX_PAGES 256 |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 94 | |
| 95 | /* |
dan | ef4ee8f | 2010-06-05 11:53:34 +0000 | [diff] [blame] | 96 | ** 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. |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 99 | */ |
| 100 | struct TestvfsBuffer { |
| 101 | char *zFile; /* Associated file name */ |
dan | 13a3cb8 | 2010-06-11 19:04:21 +0000 | [diff] [blame] | 102 | int pgsz; /* Page size */ |
| 103 | u8 *aPage[TESTVFS_MAX_PAGES]; /* Array of ckalloc'd pages */ |
dan | ef4ee8f | 2010-06-05 11:53:34 +0000 | [diff] [blame] | 104 | TestvfsFile *pFile; /* List of open handles */ |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 105 | TestvfsBuffer *pNext; /* Next in linked list of all buffers */ |
| 106 | }; |
| 107 | |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 108 | |
| 109 | #define PARENTVFS(x) (((Testvfs *)((x)->pAppData))->pParent) |
| 110 | |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 111 | #define TESTVFS_MAX_ARGS 12 |
| 112 | |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 113 | |
| 114 | /* |
dan | 7fd555a | 2010-05-13 06:19:37 +0000 | [diff] [blame] | 115 | ** Method declarations for TestvfsFile. |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 116 | */ |
| 117 | static int tvfsClose(sqlite3_file*); |
| 118 | static int tvfsRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); |
| 119 | static int tvfsWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst); |
| 120 | static int tvfsTruncate(sqlite3_file*, sqlite3_int64 size); |
| 121 | static int tvfsSync(sqlite3_file*, int flags); |
| 122 | static int tvfsFileSize(sqlite3_file*, sqlite3_int64 *pSize); |
| 123 | static int tvfsLock(sqlite3_file*, int); |
| 124 | static int tvfsUnlock(sqlite3_file*, int); |
| 125 | static int tvfsCheckReservedLock(sqlite3_file*, int *); |
| 126 | static int tvfsFileControl(sqlite3_file*, int op, void *pArg); |
| 127 | static int tvfsSectorSize(sqlite3_file*); |
| 128 | static int tvfsDeviceCharacteristics(sqlite3_file*); |
| 129 | |
| 130 | /* |
| 131 | ** Method declarations for tvfs_vfs. |
| 132 | */ |
| 133 | static int tvfsOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *); |
| 134 | static int tvfsDelete(sqlite3_vfs*, const char *zName, int syncDir); |
| 135 | static int tvfsAccess(sqlite3_vfs*, const char *zName, int flags, int *); |
| 136 | static int tvfsFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut); |
| 137 | #ifndef SQLITE_OMIT_LOAD_EXTENSION |
| 138 | static void *tvfsDlOpen(sqlite3_vfs*, const char *zFilename); |
| 139 | static void tvfsDlError(sqlite3_vfs*, int nByte, char *zErrMsg); |
| 140 | static void (*tvfsDlSym(sqlite3_vfs*,void*, const char *zSymbol))(void); |
| 141 | static void tvfsDlClose(sqlite3_vfs*, void*); |
| 142 | #endif /* SQLITE_OMIT_LOAD_EXTENSION */ |
| 143 | static int tvfsRandomness(sqlite3_vfs*, int nByte, char *zOut); |
| 144 | static int tvfsSleep(sqlite3_vfs*, int microseconds); |
| 145 | static int tvfsCurrentTime(sqlite3_vfs*, double*); |
| 146 | |
drh | d9e5c4f | 2010-05-12 18:01:39 +0000 | [diff] [blame] | 147 | static int tvfsShmOpen(sqlite3_file*); |
drh | 73b64e4 | 2010-05-30 19:55:15 +0000 | [diff] [blame] | 148 | static int tvfsShmLock(sqlite3_file*, int , int, int); |
drh | 6b017cc | 2010-06-14 18:01:46 +0000 | [diff] [blame] | 149 | static int tvfsShmMap(sqlite3_file*,int,int,int, void volatile **); |
drh | 286a288 | 2010-05-20 23:51:06 +0000 | [diff] [blame] | 150 | static void tvfsShmBarrier(sqlite3_file*); |
drh | d9e5c4f | 2010-05-12 18:01:39 +0000 | [diff] [blame] | 151 | static int tvfsShmClose(sqlite3_file*, int); |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 152 | |
| 153 | static sqlite3_io_methods tvfs_io_methods = { |
drh | d9e5c4f | 2010-05-12 18:01:39 +0000 | [diff] [blame] | 154 | 2, /* iVersion */ |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 155 | 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 */ |
drh | d9e5c4f | 2010-05-12 18:01:39 +0000 | [diff] [blame] | 166 | tvfsDeviceCharacteristics, /* xDeviceCharacteristics */ |
| 167 | tvfsShmOpen, /* xShmOpen */ |
drh | d9e5c4f | 2010-05-12 18:01:39 +0000 | [diff] [blame] | 168 | tvfsShmLock, /* xShmLock */ |
drh | 6b017cc | 2010-06-14 18:01:46 +0000 | [diff] [blame] | 169 | tvfsShmMap, /* xShmMap */ |
drh | 286a288 | 2010-05-20 23:51:06 +0000 | [diff] [blame] | 170 | tvfsShmBarrier, /* xShmBarrier */ |
drh | 6b017cc | 2010-06-14 18:01:46 +0000 | [diff] [blame] | 171 | tvfsShmClose /* xShmClose */ |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 172 | }; |
| 173 | |
dan | d764c7d | 2010-06-04 11:56:22 +0000 | [diff] [blame] | 174 | static 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 | |
dan | 146ed78 | 2010-06-19 17:26:37 +0000 | [diff] [blame] | 200 | static 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 | |
| 212 | static 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 | |
dan | d764c7d | 2010-06-04 11:56:22 +0000 | [diff] [blame] | 224 | |
| 225 | static 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 | |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 278 | /* |
| 279 | ** Close an tvfs-file. |
| 280 | */ |
| 281 | static int tvfsClose(sqlite3_file *pFile){ |
dan | 2a321c7 | 2010-06-16 19:04:23 +0000 | [diff] [blame] | 282 | 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; |
dan | 7fd555a | 2010-05-13 06:19:37 +0000 | [diff] [blame] | 294 | } |
| 295 | if( pFile->pMethods ){ |
| 296 | ckfree((char *)pFile->pMethods); |
| 297 | } |
dan | 2a321c7 | 2010-06-16 19:04:23 +0000 | [diff] [blame] | 298 | return sqlite3OsClose(pFd->pReal); |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 299 | } |
| 300 | |
| 301 | /* |
| 302 | ** Read data from an tvfs-file. |
| 303 | */ |
| 304 | static int tvfsRead( |
| 305 | sqlite3_file *pFile, |
| 306 | void *zBuf, |
| 307 | int iAmt, |
| 308 | sqlite_int64 iOfst |
| 309 | ){ |
dan | 7fd555a | 2010-05-13 06:19:37 +0000 | [diff] [blame] | 310 | TestvfsFile *p = (TestvfsFile *)pFile; |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 311 | return sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst); |
| 312 | } |
| 313 | |
| 314 | /* |
| 315 | ** Write data to an tvfs-file. |
| 316 | */ |
| 317 | static int tvfsWrite( |
| 318 | sqlite3_file *pFile, |
| 319 | const void *zBuf, |
| 320 | int iAmt, |
| 321 | sqlite_int64 iOfst |
| 322 | ){ |
dan | 2a321c7 | 2010-06-16 19:04:23 +0000 | [diff] [blame] | 323 | 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 | } |
dan | 146ed78 | 2010-06-19 17:26:37 +0000 | [diff] [blame] | 333 | |
| 334 | if( rc==SQLITE_OK && tvfsInjectFullerr(p) ) rc = SQLITE_FULL; |
dan | 2a321c7 | 2010-06-16 19:04:23 +0000 | [diff] [blame] | 335 | |
| 336 | if( rc==SQLITE_OK ){ |
| 337 | rc = sqlite3OsWrite(pFd->pReal, zBuf, iAmt, iOfst); |
| 338 | } |
| 339 | return rc; |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 340 | } |
| 341 | |
| 342 | /* |
| 343 | ** Truncate an tvfs-file. |
| 344 | */ |
| 345 | static int tvfsTruncate(sqlite3_file *pFile, sqlite_int64 size){ |
dan | 2a321c7 | 2010-06-16 19:04:23 +0000 | [diff] [blame] | 346 | 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; |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 361 | } |
| 362 | |
| 363 | /* |
| 364 | ** Sync an tvfs-file. |
| 365 | */ |
| 366 | static int tvfsSync(sqlite3_file *pFile, int flags){ |
dan | d764c7d | 2010-06-04 11:56:22 +0000 | [diff] [blame] | 367 | 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 | |
dan | 146ed78 | 2010-06-19 17:26:37 +0000 | [diff] [blame] | 398 | if( rc==SQLITE_OK && tvfsInjectFullerr(p) ) rc = SQLITE_FULL; |
| 399 | |
dan | d764c7d | 2010-06-04 11:56:22 +0000 | [diff] [blame] | 400 | if( rc==SQLITE_OK ){ |
| 401 | rc = sqlite3OsSync(pFd->pReal, flags); |
| 402 | } |
| 403 | |
| 404 | return rc; |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 405 | } |
| 406 | |
| 407 | /* |
| 408 | ** Return the current file-size of an tvfs-file. |
| 409 | */ |
| 410 | static int tvfsFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ |
dan | 7fd555a | 2010-05-13 06:19:37 +0000 | [diff] [blame] | 411 | TestvfsFile *p = (TestvfsFile *)pFile; |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 412 | return sqlite3OsFileSize(p->pReal, pSize); |
| 413 | } |
| 414 | |
| 415 | /* |
| 416 | ** Lock an tvfs-file. |
| 417 | */ |
| 418 | static int tvfsLock(sqlite3_file *pFile, int eLock){ |
dan | 7fd555a | 2010-05-13 06:19:37 +0000 | [diff] [blame] | 419 | TestvfsFile *p = (TestvfsFile *)pFile; |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 420 | return sqlite3OsLock(p->pReal, eLock); |
| 421 | } |
| 422 | |
| 423 | /* |
| 424 | ** Unlock an tvfs-file. |
| 425 | */ |
| 426 | static int tvfsUnlock(sqlite3_file *pFile, int eLock){ |
dan | 7fd555a | 2010-05-13 06:19:37 +0000 | [diff] [blame] | 427 | TestvfsFile *p = (TestvfsFile *)pFile; |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 428 | return sqlite3OsUnlock(p->pReal, eLock); |
| 429 | } |
| 430 | |
| 431 | /* |
| 432 | ** Check if another file-handle holds a RESERVED lock on an tvfs-file. |
| 433 | */ |
| 434 | static int tvfsCheckReservedLock(sqlite3_file *pFile, int *pResOut){ |
dan | 7fd555a | 2010-05-13 06:19:37 +0000 | [diff] [blame] | 435 | TestvfsFile *p = (TestvfsFile *)pFile; |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 436 | return sqlite3OsCheckReservedLock(p->pReal, pResOut); |
| 437 | } |
| 438 | |
| 439 | /* |
| 440 | ** File control method. For custom operations on an tvfs-file. |
| 441 | */ |
| 442 | static int tvfsFileControl(sqlite3_file *pFile, int op, void *pArg){ |
dan | 7fd555a | 2010-05-13 06:19:37 +0000 | [diff] [blame] | 443 | TestvfsFile *p = (TestvfsFile *)pFile; |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 444 | return sqlite3OsFileControl(p->pReal, op, pArg); |
| 445 | } |
| 446 | |
| 447 | /* |
| 448 | ** Return the sector-size in bytes for an tvfs-file. |
| 449 | */ |
| 450 | static int tvfsSectorSize(sqlite3_file *pFile){ |
dan | 2a321c7 | 2010-06-16 19:04:23 +0000 | [diff] [blame] | 451 | 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); |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 457 | } |
| 458 | |
| 459 | /* |
| 460 | ** Return the device characteristic flags supported by an tvfs-file. |
| 461 | */ |
| 462 | static int tvfsDeviceCharacteristics(sqlite3_file *pFile){ |
dan | 2a321c7 | 2010-06-16 19:04:23 +0000 | [diff] [blame] | 463 | 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); |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 469 | } |
| 470 | |
| 471 | /* |
| 472 | ** Open an tvfs file handle. |
| 473 | */ |
| 474 | static int tvfsOpen( |
| 475 | sqlite3_vfs *pVfs, |
| 476 | const char *zName, |
| 477 | sqlite3_file *pFile, |
| 478 | int flags, |
| 479 | int *pOutFlags |
| 480 | ){ |
| 481 | int rc; |
dan | d764c7d | 2010-06-04 11:56:22 +0000 | [diff] [blame] | 482 | 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 ){ |
dan | 7fd555a | 2010-05-13 06:19:37 +0000 | [diff] [blame] | 519 | 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; |
dan | 7fd555a | 2010-05-13 06:19:37 +0000 | [diff] [blame] | 524 | pMethods->xShmClose = 0; |
| 525 | pMethods->xShmLock = 0; |
drh | 286a288 | 2010-05-20 23:51:06 +0000 | [diff] [blame] | 526 | pMethods->xShmBarrier = 0; |
dan | 1880191 | 2010-06-14 14:07:50 +0000 | [diff] [blame] | 527 | pMethods->xShmMap = 0; |
dan | 7fd555a | 2010-05-13 06:19:37 +0000 | [diff] [blame] | 528 | } |
| 529 | pFile->pMethods = pMethods; |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 530 | } |
dan | 7fd555a | 2010-05-13 06:19:37 +0000 | [diff] [blame] | 531 | |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 532 | 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 | */ |
| 540 | static int tvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ |
dan | b0ac3e3 | 2010-06-16 10:55:42 +0000 | [diff] [blame] | 541 | 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; |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 554 | } |
| 555 | |
| 556 | /* |
| 557 | ** Test for access permissions. Return true if the requested permission |
| 558 | ** is available, or false otherwise. |
| 559 | */ |
| 560 | static 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 | */ |
| 574 | static 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 | */ |
| 587 | static 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 | */ |
| 596 | static 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 | */ |
| 603 | static 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 | */ |
| 610 | static 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 | */ |
| 619 | static 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 | */ |
| 627 | static 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 | */ |
| 634 | static int tvfsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ |
| 635 | return PARENTVFS(pVfs)->xCurrentTime(PARENTVFS(pVfs), pTimeOut); |
| 636 | } |
| 637 | |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 638 | static int tvfsShmOpen( |
drh | d9e5c4f | 2010-05-12 18:01:39 +0000 | [diff] [blame] | 639 | sqlite3_file *pFileDes |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 640 | ){ |
dan | 7fd555a | 2010-05-13 06:19:37 +0000 | [diff] [blame] | 641 | Testvfs *p; |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 642 | int rc = SQLITE_OK; /* Return code */ |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 643 | TestvfsBuffer *pBuffer; /* Buffer to open connection to */ |
dan | 7fd555a | 2010-05-13 06:19:37 +0000 | [diff] [blame] | 644 | TestvfsFile *pFd; /* The testvfs file structure */ |
drh | d9e5c4f | 2010-05-12 18:01:39 +0000 | [diff] [blame] | 645 | |
dan | 7fd555a | 2010-05-13 06:19:37 +0000 | [diff] [blame] | 646 | pFd = (TestvfsFile*)pFileDes; |
| 647 | p = (Testvfs *)pFd->pVfs->pAppData; |
dan | ef4ee8f | 2010-06-05 11:53:34 +0000 | [diff] [blame] | 648 | assert( pFd->pShmId && pFd->pShm==0 && pFd->pNext==0 ); |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 649 | |
| 650 | /* Evaluate the Tcl script: |
| 651 | ** |
| 652 | ** SCRIPT xShmOpen FILENAME |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 653 | */ |
dan | 0235ab9 | 2010-06-01 19:15:18 +0000 | [diff] [blame] | 654 | Tcl_ResetResult(p->interp); |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 655 | if( p->pScript && p->mask&TESTVFS_SHMOPEN_MASK ){ |
dan | 0235ab9 | 2010-06-01 19:15:18 +0000 | [diff] [blame] | 656 | tvfsExecTcl(p, "xShmOpen", Tcl_NewStringObj(pFd->zFilename, -1), 0, 0); |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 657 | if( tvfsResultCode(p, &rc) ){ |
| 658 | if( rc!=SQLITE_OK ) return rc; |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 659 | } |
dan | 0235ab9 | 2010-06-01 19:15:18 +0000 | [diff] [blame] | 660 | } |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 661 | |
| 662 | assert( rc==SQLITE_OK ); |
| 663 | if( p->mask&TESTVFS_SHMOPEN_MASK && tvfsInjectIoerr(p) ){ |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 664 | return SQLITE_IOERR; |
| 665 | } |
| 666 | |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 667 | /* Search for a TestvfsBuffer. Create a new one if required. */ |
| 668 | for(pBuffer=p->pBuffer; pBuffer; pBuffer=pBuffer->pNext){ |
dan | 7fd555a | 2010-05-13 06:19:37 +0000 | [diff] [blame] | 669 | if( 0==strcmp(pFd->zFilename, pBuffer->zFile) ) break; |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 670 | } |
| 671 | if( !pBuffer ){ |
dan | 7fd555a | 2010-05-13 06:19:37 +0000 | [diff] [blame] | 672 | int nByte = sizeof(TestvfsBuffer) + strlen(pFd->zFilename) + 1; |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 673 | pBuffer = (TestvfsBuffer *)ckalloc(nByte); |
| 674 | memset(pBuffer, 0, nByte); |
| 675 | pBuffer->zFile = (char *)&pBuffer[1]; |
dan | 7fd555a | 2010-05-13 06:19:37 +0000 | [diff] [blame] | 676 | strcpy(pBuffer->zFile, pFd->zFilename); |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 677 | pBuffer->pNext = p->pBuffer; |
| 678 | p->pBuffer = pBuffer; |
| 679 | } |
| 680 | |
| 681 | /* Connect the TestvfsBuffer to the new TestvfsShm handle and return. */ |
dan | ef4ee8f | 2010-06-05 11:53:34 +0000 | [diff] [blame] | 682 | pFd->pNext = pBuffer->pFile; |
| 683 | pBuffer->pFile = pFd; |
dan | 7fd555a | 2010-05-13 06:19:37 +0000 | [diff] [blame] | 684 | pFd->pShm = pBuffer; |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 685 | return SQLITE_OK; |
| 686 | } |
| 687 | |
dan | 13a3cb8 | 2010-06-11 19:04:21 +0000 | [diff] [blame] | 688 | static void tvfsAllocPage(TestvfsBuffer *p, int iPage, int pgsz){ |
| 689 | assert( iPage<TESTVFS_MAX_PAGES ); |
| 690 | if( p->aPage[iPage]==0 ){ |
dan | 4280eb3 | 2010-06-12 12:02:35 +0000 | [diff] [blame] | 691 | p->aPage[iPage] = (u8 *)ckalloc(pgsz); |
dan | 13a3cb8 | 2010-06-11 19:04:21 +0000 | [diff] [blame] | 692 | memset(p->aPage[iPage], 0, pgsz); |
| 693 | p->pgsz = pgsz; |
| 694 | } |
| 695 | } |
| 696 | |
drh | 6b017cc | 2010-06-14 18:01:46 +0000 | [diff] [blame] | 697 | static int tvfsShmMap( |
dan | 13a3cb8 | 2010-06-11 19:04:21 +0000 | [diff] [blame] | 698 | 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 | ){ |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 704 | int rc = SQLITE_OK; |
dan | 7fd555a | 2010-05-13 06:19:37 +0000 | [diff] [blame] | 705 | TestvfsFile *pFd = (TestvfsFile *)pFile; |
| 706 | Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData); |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 707 | |
drh | 6b017cc | 2010-06-14 18:01:46 +0000 | [diff] [blame] | 708 | if( p->pScript && p->mask&TESTVFS_SHMMAP_MASK ){ |
dan | 13a3cb8 | 2010-06-11 19:04:21 +0000 | [diff] [blame] | 709 | Tcl_Obj *pArg = Tcl_NewObj(); |
dan | 5d65685 | 2010-06-14 07:53:26 +0000 | [diff] [blame] | 710 | Tcl_IncrRefCount(pArg); |
dan | 13a3cb8 | 2010-06-11 19:04:21 +0000 | [diff] [blame] | 711 | 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)); |
drh | 6b017cc | 2010-06-14 18:01:46 +0000 | [diff] [blame] | 714 | tvfsExecTcl(p, "xShmMap", |
dan | 13a3cb8 | 2010-06-11 19:04:21 +0000 | [diff] [blame] | 715 | Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, pArg |
dan | 0235ab9 | 2010-06-01 19:15:18 +0000 | [diff] [blame] | 716 | ); |
| 717 | tvfsResultCode(p, &rc); |
dan | 5d65685 | 2010-06-14 07:53:26 +0000 | [diff] [blame] | 718 | Tcl_DecrRefCount(pArg); |
dan | 0235ab9 | 2010-06-01 19:15:18 +0000 | [diff] [blame] | 719 | } |
drh | 6b017cc | 2010-06-14 18:01:46 +0000 | [diff] [blame] | 720 | if( rc==SQLITE_OK && p->mask&TESTVFS_SHMMAP_MASK && tvfsInjectIoerr(p) ){ |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 721 | rc = SQLITE_IOERR; |
| 722 | } |
dan | d03f523 | 2010-06-03 19:10:08 +0000 | [diff] [blame] | 723 | |
dan | 13a3cb8 | 2010-06-11 19:04:21 +0000 | [diff] [blame] | 724 | if( rc==SQLITE_OK && isWrite && !pFd->pShm->aPage[iPage] ){ |
| 725 | tvfsAllocPage(pFd->pShm, iPage, pgsz); |
dan | 0235ab9 | 2010-06-01 19:15:18 +0000 | [diff] [blame] | 726 | } |
dan | 13a3cb8 | 2010-06-11 19:04:21 +0000 | [diff] [blame] | 727 | *pp = (void volatile *)pFd->pShm->aPage[iPage]; |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 728 | |
| 729 | return rc; |
| 730 | } |
| 731 | |
dan | 13a3cb8 | 2010-06-11 19:04:21 +0000 | [diff] [blame] | 732 | |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 733 | static int tvfsShmLock( |
dan | 7fd555a | 2010-05-13 06:19:37 +0000 | [diff] [blame] | 734 | sqlite3_file *pFile, |
drh | 73b64e4 | 2010-05-30 19:55:15 +0000 | [diff] [blame] | 735 | int ofst, |
| 736 | int n, |
| 737 | int flags |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 738 | ){ |
| 739 | int rc = SQLITE_OK; |
dan | 7fd555a | 2010-05-13 06:19:37 +0000 | [diff] [blame] | 740 | TestvfsFile *pFd = (TestvfsFile *)pFile; |
| 741 | Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData); |
drh | 73b64e4 | 2010-05-30 19:55:15 +0000 | [diff] [blame] | 742 | int nLock; |
| 743 | char zLock[80]; |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 744 | |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 745 | if( p->pScript && p->mask&TESTVFS_SHMLOCK_MASK ){ |
dan | 0235ab9 | 2010-06-01 19:15:18 +0000 | [diff] [blame] | 746 | 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); |
drh | 73b64e4 | 2010-05-30 19:55:15 +0000 | [diff] [blame] | 764 | } |
dan | 961ff45 | 2010-06-03 18:20:19 +0000 | [diff] [blame] | 765 | |
| 766 | if( rc==SQLITE_OK && p->mask&TESTVFS_SHMLOCK_MASK && tvfsInjectIoerr(p) ){ |
| 767 | rc = SQLITE_IOERR; |
| 768 | } |
dan | ef4ee8f | 2010-06-05 11:53:34 +0000 | [diff] [blame] | 769 | |
| 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 | |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 793 | return rc; |
| 794 | } |
| 795 | |
drh | 286a288 | 2010-05-20 23:51:06 +0000 | [diff] [blame] | 796 | static void tvfsShmBarrier(sqlite3_file *pFile){ |
drh | 286a288 | 2010-05-20 23:51:06 +0000 | [diff] [blame] | 797 | TestvfsFile *pFd = (TestvfsFile *)pFile; |
| 798 | Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData); |
| 799 | |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 800 | if( p->pScript && p->mask&TESTVFS_SHMBARRIER_MASK ){ |
dan | 0235ab9 | 2010-06-01 19:15:18 +0000 | [diff] [blame] | 801 | tvfsExecTcl(p, "xShmBarrier", |
| 802 | Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0 |
| 803 | ); |
| 804 | } |
drh | 286a288 | 2010-05-20 23:51:06 +0000 | [diff] [blame] | 805 | } |
| 806 | |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 807 | static int tvfsShmClose( |
dan | 7fd555a | 2010-05-13 06:19:37 +0000 | [diff] [blame] | 808 | sqlite3_file *pFile, |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 809 | int deleteFlag |
| 810 | ){ |
| 811 | int rc = SQLITE_OK; |
dan | 7fd555a | 2010-05-13 06:19:37 +0000 | [diff] [blame] | 812 | TestvfsFile *pFd = (TestvfsFile *)pFile; |
| 813 | Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData); |
| 814 | TestvfsBuffer *pBuffer = pFd->pShm; |
dan | ef4ee8f | 2010-06-05 11:53:34 +0000 | [diff] [blame] | 815 | TestvfsFile **ppFd; |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 816 | |
dan | 7fd555a | 2010-05-13 06:19:37 +0000 | [diff] [blame] | 817 | assert( pFd->pShmId && pFd->pShm ); |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 818 | |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 819 | if( p->pScript && p->mask&TESTVFS_SHMCLOSE_MASK ){ |
dan | 0235ab9 | 2010-06-01 19:15:18 +0000 | [diff] [blame] | 820 | tvfsExecTcl(p, "xShmClose", |
| 821 | Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0 |
| 822 | ); |
| 823 | tvfsResultCode(p, &rc); |
| 824 | } |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 825 | |
dan | ef4ee8f | 2010-06-05 11:53:34 +0000 | [diff] [blame] | 826 | for(ppFd=&pBuffer->pFile; *ppFd!=pFd; ppFd=&((*ppFd)->pNext)); |
| 827 | assert( (*ppFd)==pFd ); |
| 828 | *ppFd = pFd->pNext; |
| 829 | |
| 830 | if( pBuffer->pFile==0 ){ |
dan | 13a3cb8 | 2010-06-11 19:04:21 +0000 | [diff] [blame] | 831 | int i; |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 832 | TestvfsBuffer **pp; |
| 833 | for(pp=&p->pBuffer; *pp!=pBuffer; pp=&((*pp)->pNext)); |
| 834 | *pp = (*pp)->pNext; |
dan | 13a3cb8 | 2010-06-11 19:04:21 +0000 | [diff] [blame] | 835 | for(i=0; pBuffer->aPage[i]; i++){ |
| 836 | ckfree((char *)pBuffer->aPage[i]); |
| 837 | } |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 838 | ckfree((char *)pBuffer); |
| 839 | } |
dan | 7fd555a | 2010-05-13 06:19:37 +0000 | [diff] [blame] | 840 | pFd->pShm = 0; |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 841 | |
| 842 | return rc; |
| 843 | } |
| 844 | |
| 845 | static 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 | |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 853 | enum DB_enum { |
dan | 2a321c7 | 2010-06-16 19:04:23 +0000 | [diff] [blame] | 854 | CMD_SHM, CMD_DELETE, CMD_FILTER, CMD_IOERR, CMD_SCRIPT, |
dan | 146ed78 | 2010-06-19 17:26:37 +0000 | [diff] [blame] | 855 | CMD_DEVCHAR, CMD_SECTORSIZE, CMD_FULLERR |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 856 | }; |
dan | 2a321c7 | 2010-06-16 19:04:23 +0000 | [diff] [blame] | 857 | 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 }, |
dan | 146ed78 | 2010-06-19 17:26:37 +0000 | [diff] [blame] | 865 | { "fullerr", CMD_FULLERR }, |
dan | 2a321c7 | 2010-06-16 19:04:23 +0000 | [diff] [blame] | 866 | { "script", CMD_SCRIPT }, |
| 867 | { "devchar", CMD_DEVCHAR }, |
| 868 | { "sectorsize", CMD_SECTORSIZE }, |
| 869 | { 0, 0 } |
| 870 | }; |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 871 | int i; |
| 872 | |
| 873 | if( objc<2 ){ |
| 874 | Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ..."); |
| 875 | return TCL_ERROR; |
| 876 | } |
dan | 2a321c7 | 2010-06-16 19:04:23 +0000 | [diff] [blame] | 877 | if( Tcl_GetIndexFromObjStruct( |
| 878 | interp, objv[1], aSubcmd, sizeof(aSubcmd[0]), "subcommand", 0, &i) |
| 879 | ){ |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 880 | return TCL_ERROR; |
| 881 | } |
| 882 | Tcl_ResetResult(interp); |
| 883 | |
dan | 2a321c7 | 2010-06-16 19:04:23 +0000 | [diff] [blame] | 884 | switch( aSubcmd[i].eCmd ){ |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 885 | case CMD_SHM: { |
dan | 13a3cb8 | 2010-06-11 19:04:21 +0000 | [diff] [blame] | 886 | Tcl_Obj *pObj; |
| 887 | int i; |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 888 | TestvfsBuffer *pBuffer; |
| 889 | char *zName; |
| 890 | if( objc!=3 && objc!=4 ){ |
| 891 | Tcl_WrongNumArgs(interp, 2, objv, "FILE ?VALUE?"); |
| 892 | return TCL_ERROR; |
| 893 | } |
dan | 5d65685 | 2010-06-14 07:53:26 +0000 | [diff] [blame] | 894 | zName = ckalloc(p->pParent->mxPathname); |
| 895 | p->pParent->xFullPathname( |
| 896 | p->pParent, Tcl_GetString(objv[2]), |
| 897 | p->pParent->mxPathname, zName |
| 898 | ); |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 899 | for(pBuffer=p->pBuffer; pBuffer; pBuffer=pBuffer->pNext){ |
| 900 | if( 0==strcmp(pBuffer->zFile, zName) ) break; |
| 901 | } |
dan | 5d65685 | 2010-06-14 07:53:26 +0000 | [diff] [blame] | 902 | ckfree(zName); |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 903 | if( !pBuffer ){ |
dan | 5d65685 | 2010-06-14 07:53:26 +0000 | [diff] [blame] | 904 | Tcl_AppendResult(interp, "no such file: ", Tcl_GetString(objv[2]), 0); |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 905 | return TCL_ERROR; |
| 906 | } |
| 907 | if( objc==4 ){ |
| 908 | int n; |
| 909 | u8 *a = Tcl_GetByteArrayFromObj(objv[3], &n); |
dan | 13a3cb8 | 2010-06-11 19:04:21 +0000 | [diff] [blame] | 910 | 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 | } |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 919 | } |
dan | 13a3cb8 | 2010-06-11 19:04:21 +0000 | [diff] [blame] | 920 | |
| 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); |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 926 | break; |
| 927 | } |
dan | 0235ab9 | 2010-06-01 19:15:18 +0000 | [diff] [blame] | 928 | |
| 929 | case CMD_FILTER: { |
| 930 | static struct VfsMethod { |
| 931 | char *zName; |
| 932 | int mask; |
| 933 | } vfsmethod [] = { |
| 934 | { "xShmOpen", TESTVFS_SHMOPEN_MASK }, |
dan | 0235ab9 | 2010-06-01 19:15:18 +0000 | [diff] [blame] | 935 | { "xShmLock", TESTVFS_SHMLOCK_MASK }, |
| 936 | { "xShmBarrier", TESTVFS_SHMBARRIER_MASK }, |
| 937 | { "xShmClose", TESTVFS_SHMCLOSE_MASK }, |
drh | 6b017cc | 2010-06-14 18:01:46 +0000 | [diff] [blame] | 938 | { "xShmMap", TESTVFS_SHMMAP_MASK }, |
dan | d764c7d | 2010-06-04 11:56:22 +0000 | [diff] [blame] | 939 | { "xSync", TESTVFS_SYNC_MASK }, |
dan | b0ac3e3 | 2010-06-16 10:55:42 +0000 | [diff] [blame] | 940 | { "xDelete", TESTVFS_DELETE_MASK }, |
dan | 2a321c7 | 2010-06-16 19:04:23 +0000 | [diff] [blame] | 941 | { "xWrite", TESTVFS_WRITE_MASK }, |
| 942 | { "xTruncate", TESTVFS_TRUNCATE_MASK }, |
dan | d764c7d | 2010-06-04 11:56:22 +0000 | [diff] [blame] | 943 | { "xOpen", TESTVFS_OPEN_MASK }, |
dan | 2a321c7 | 2010-06-16 19:04:23 +0000 | [diff] [blame] | 944 | { "xClose", TESTVFS_CLOSE_MASK }, |
dan | 0235ab9 | 2010-06-01 19:15:18 +0000 | [diff] [blame] | 945 | }; |
| 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 | |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 976 | 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; |
dan | 5d65685 | 2010-06-14 07:53:26 +0000 | [diff] [blame] | 984 | p->pScript = 0; |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 985 | } |
| 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 | /* |
dan | 146ed78 | 2010-06-19 17:26:37 +0000 | [diff] [blame] | 1003 | ** 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 | /* |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 1031 | ** TESTVFS ioerr ?IFAIL PERSIST? |
| 1032 | ** |
| 1033 | ** Where IFAIL is an integer and PERSIST is boolean. |
| 1034 | */ |
dan | 0235ab9 | 2010-06-01 19:15:18 +0000 | [diff] [blame] | 1035 | case CMD_IOERR: { |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 1036 | int iRet = p->nIoerrFail; |
| 1037 | |
| 1038 | p->nIoerrFail = 0; |
| 1039 | p->ioerr = 0; |
| 1040 | p->iIoerrCnt = 0; |
| 1041 | |
| 1042 | if( objc==4 ){ |
dan | 0235ab9 | 2010-06-01 19:15:18 +0000 | [diff] [blame] | 1043 | 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 | } |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 1049 | p->ioerr = (iCnt>0) + iPersist; |
dan | 0235ab9 | 2010-06-01 19:15:18 +0000 | [diff] [blame] | 1050 | p->iIoerrCnt = iCnt; |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 1051 | }else if( objc!=2 ){ |
dan | 0235ab9 | 2010-06-01 19:15:18 +0000 | [diff] [blame] | 1052 | Tcl_AppendResult(interp, "Bad args", 0); |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 1053 | return TCL_ERROR; |
dan | 0235ab9 | 2010-06-01 19:15:18 +0000 | [diff] [blame] | 1054 | } |
| 1055 | Tcl_SetObjResult(interp, Tcl_NewIntObj(iRet)); |
| 1056 | break; |
| 1057 | } |
| 1058 | |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 1059 | case CMD_DELETE: { |
| 1060 | Tcl_DeleteCommand(interp, Tcl_GetString(objv[0])); |
| 1061 | break; |
| 1062 | } |
dan | 2a321c7 | 2010-06-16 19:04:23 +0000 | [diff] [blame] | 1063 | |
| 1064 | case CMD_DEVCHAR: { |
| 1065 | struct DeviceFlag { |
| 1066 | char *zName; |
| 1067 | int iValue; |
| 1068 | } aFlag[] = { |
dan | 8ce49d6 | 2010-06-19 18:12:02 +0000 | [diff] [blame^] | 1069 | { "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 }, |
dan | 2a321c7 | 2010-06-16 19:04:23 +0000 | [diff] [blame] | 1082 | { 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 | } |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 1146 | } |
| 1147 | |
| 1148 | return TCL_OK; |
| 1149 | } |
| 1150 | |
| 1151 | static void testvfs_obj_del(ClientData cd){ |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 1152 | Testvfs *p = (Testvfs *)cd; |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 1153 | if( p->pScript ) Tcl_DecrRefCount(p->pScript); |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 1154 | sqlite3_vfs_unregister(p->pVfs); |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 1155 | ckfree((char *)p->apScript); |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 1156 | ckfree((char *)p->pVfs); |
| 1157 | ckfree((char *)p); |
| 1158 | } |
| 1159 | |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 1160 | /* |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 1161 | ** 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) |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 1167 | ** |
| 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 | ** |
drh | 6b017cc | 2010-06-14 18:01:46 +0000 | [diff] [blame] | 1174 | ** Whenever the xShmMap method of the VFS |
| 1175 | ** is invoked, the SCRIPT is executed as follows: |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 1176 | ** |
drh | 6b017cc | 2010-06-14 18:01:46 +0000 | [diff] [blame] | 1177 | ** SCRIPT xShmMap FILENAME ID |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 1178 | ** |
| 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 | ** |
drh | 73b64e4 | 2010-05-30 19:55:15 +0000 | [diff] [blame] | 1193 | ** where LOCK is of the form "OFFSET NBYTE lock/unlock shared/exclusive" |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 1194 | */ |
| 1195 | static int testvfs_cmd( |
| 1196 | ClientData cd, |
| 1197 | Tcl_Interp *interp, |
| 1198 | int objc, |
| 1199 | Tcl_Obj *CONST objv[] |
| 1200 | ){ |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 1201 | static sqlite3_vfs tvfs_vfs = { |
| 1202 | 2, /* iVersion */ |
dan | 7fd555a | 2010-05-13 06:19:37 +0000 | [diff] [blame] | 1203 | sizeof(TestvfsFile), /* szOsFile */ |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 1204 | 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 */ |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 1227 | 0, |
| 1228 | 0, |
| 1229 | }; |
| 1230 | |
| 1231 | Testvfs *p; /* New object */ |
| 1232 | sqlite3_vfs *pVfs; /* New VFS */ |
| 1233 | char *zVfs; |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 1234 | int nByte; /* Bytes of space to allocate at p */ |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 1235 | |
| 1236 | int i; |
dan | 576bc32 | 2010-05-06 18:04:50 +0000 | [diff] [blame] | 1237 | int isNoshm = 0; /* True if -noshm is passed */ |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 1238 | int isDefault = 0; /* True if -default is passed */ |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 1239 | |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 1240 | if( objc<2 || 0!=(objc%2) ) goto bad_args; |
| 1241 | for(i=2; i<objc; i += 2){ |
| 1242 | int nSwitch; |
| 1243 | char *zSwitch; |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 1244 | |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 1245 | 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 | } |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 1259 | } |
| 1260 | |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 1261 | zVfs = Tcl_GetString(objv[1]); |
| 1262 | nByte = sizeof(Testvfs) + strlen(zVfs)+1; |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 1263 | p = (Testvfs *)ckalloc(nByte); |
| 1264 | memset(p, 0, nByte); |
dan | 2a321c7 | 2010-06-16 19:04:23 +0000 | [diff] [blame] | 1265 | p->iDevchar = -1; |
| 1266 | p->iSectorsize = -1; |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 1267 | |
dan | 5d65685 | 2010-06-14 07:53:26 +0000 | [diff] [blame] | 1268 | /* 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); |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 1275 | p->pParent = sqlite3_vfs_find(0); |
| 1276 | p->interp = interp; |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 1277 | |
| 1278 | p->zName = (char *)&p[1]; |
| 1279 | memcpy(p->zName, zVfs, strlen(zVfs)+1); |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 1280 | |
| 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; |
dan | 8f6097c | 2010-05-06 07:43:58 +0000 | [diff] [blame] | 1287 | p->pVfs = pVfs; |
dan | 7fd555a | 2010-05-13 06:19:37 +0000 | [diff] [blame] | 1288 | p->isNoshm = isNoshm; |
dan | 0235ab9 | 2010-06-01 19:15:18 +0000 | [diff] [blame] | 1289 | p->mask = TESTVFS_ALL_MASK; |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 1290 | |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 1291 | sqlite3_vfs_register(pVfs, isDefault); |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 1292 | |
| 1293 | return TCL_OK; |
dan | 576bc32 | 2010-05-06 18:04:50 +0000 | [diff] [blame] | 1294 | |
| 1295 | bad_args: |
dan | 1f55e28 | 2010-06-03 09:25:10 +0000 | [diff] [blame] | 1296 | Tcl_WrongNumArgs(interp, 1, objv, "VFSNAME ?-noshm BOOL? ?-default BOOL?"); |
dan | 576bc32 | 2010-05-06 18:04:50 +0000 | [diff] [blame] | 1297 | return TCL_ERROR; |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 1298 | } |
| 1299 | |
| 1300 | int Sqlitetestvfs_Init(Tcl_Interp *interp){ |
dan | 7fd555a | 2010-05-13 06:19:37 +0000 | [diff] [blame] | 1301 | Tcl_CreateObjCommand(interp, "testvfs", testvfs_cmd, 0, 0); |
dan | c7991bd | 2010-05-05 19:04:59 +0000 | [diff] [blame] | 1302 | return TCL_OK; |
| 1303 | } |
| 1304 | |
| 1305 | #endif |