Annotation of embedaddon/sqlite3/src/test_journal.c, revision 1.1
1.1 ! misho 1: /*
! 2: ** 2008 Jan 22
! 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: ** This file contains code for a VFS layer that acts as a wrapper around
! 14: ** an existing VFS. The code in this file attempts to verify that SQLite
! 15: ** correctly populates and syncs a journal file before writing to a
! 16: ** corresponding database file.
! 17: **
! 18: ** INTERFACE
! 19: **
! 20: ** The public interface to this wrapper VFS is two functions:
! 21: **
! 22: ** jt_register()
! 23: ** jt_unregister()
! 24: **
! 25: ** See header comments associated with those two functions below for
! 26: ** details.
! 27: **
! 28: ** LIMITATIONS
! 29: **
! 30: ** This wrapper will not work if "PRAGMA synchronous = off" is used.
! 31: **
! 32: ** OPERATION
! 33: **
! 34: ** Starting a Transaction:
! 35: **
! 36: ** When a write-transaction is started, the contents of the database is
! 37: ** inspected and the following data stored as part of the database file
! 38: ** handle (type struct jt_file):
! 39: **
! 40: ** a) The page-size of the database file.
! 41: ** b) The number of pages that are in the database file.
! 42: ** c) The set of page numbers corresponding to free-list leaf pages.
! 43: ** d) A check-sum for every page in the database file.
! 44: **
! 45: ** The start of a write-transaction is deemed to have occurred when a
! 46: ** 28-byte journal header is written to byte offset 0 of the journal
! 47: ** file.
! 48: **
! 49: ** Syncing the Journal File:
! 50: **
! 51: ** Whenever the xSync method is invoked to sync a journal-file, the
! 52: ** contents of the journal file are read. For each page written to
! 53: ** the journal file, a check-sum is calculated and compared to the
! 54: ** check-sum calculated for the corresponding database page when the
! 55: ** write-transaction was initialized. The success of the comparison
! 56: ** is assert()ed. So if SQLite has written something other than the
! 57: ** original content to the database file, an assert() will fail.
! 58: **
! 59: ** Additionally, the set of page numbers for which records exist in
! 60: ** the journal file is added to (unioned with) the set of page numbers
! 61: ** corresponding to free-list leaf pages collected when the
! 62: ** write-transaction was initialized. This set comprises the page-numbers
! 63: ** corresponding to those pages that SQLite may now safely modify.
! 64: **
! 65: ** Writing to the Database File:
! 66: **
! 67: ** When a block of data is written to a database file, the following
! 68: ** invariants are asserted:
! 69: **
! 70: ** a) That the block of data is an aligned block of page-size bytes.
! 71: **
! 72: ** b) That if the page being written did not exist when the
! 73: ** transaction was started (i.e. the database file is growing), then
! 74: ** the journal-file must have been synced at least once since
! 75: ** the start of the transaction.
! 76: **
! 77: ** c) That if the page being written did exist when the transaction
! 78: ** was started, then the page must have either been a free-list
! 79: ** leaf page at the start of the transaction, or else must have
! 80: ** been stored in the journal file prior to the most recent sync.
! 81: **
! 82: ** Closing a Transaction:
! 83: **
! 84: ** When a transaction is closed, all data collected at the start of
! 85: ** the transaction, or following an xSync of a journal-file, is
! 86: ** discarded. The end of a transaction is recognized when any one
! 87: ** of the following occur:
! 88: **
! 89: ** a) A block of zeroes (or anything else that is not a valid
! 90: ** journal-header) is written to the start of the journal file.
! 91: **
! 92: ** b) A journal file is truncated to zero bytes in size using xTruncate.
! 93: **
! 94: ** c) The journal file is deleted using xDelete.
! 95: */
! 96: #if SQLITE_TEST /* This file is used for testing only */
! 97:
! 98: #include "sqlite3.h"
! 99: #include "sqliteInt.h"
! 100:
! 101: /*
! 102: ** Maximum pathname length supported by the jt backend.
! 103: */
! 104: #define JT_MAX_PATHNAME 512
! 105:
! 106: /*
! 107: ** Name used to identify this VFS.
! 108: */
! 109: #define JT_VFS_NAME "jt"
! 110:
! 111: typedef struct jt_file jt_file;
! 112: struct jt_file {
! 113: sqlite3_file base;
! 114: const char *zName; /* Name of open file */
! 115: int flags; /* Flags the file was opened with */
! 116:
! 117: /* The following are only used by database file file handles */
! 118: int eLock; /* Current lock held on the file */
! 119: u32 nPage; /* Size of file in pages when transaction started */
! 120: u32 nPagesize; /* Page size when transaction started */
! 121: Bitvec *pWritable; /* Bitvec of pages that may be written to the file */
! 122: u32 *aCksum; /* Checksum for first nPage pages */
! 123: int nSync; /* Number of times journal file has been synced */
! 124:
! 125: /* Only used by journal file-handles */
! 126: sqlite3_int64 iMaxOff; /* Maximum offset written to this transaction */
! 127:
! 128: jt_file *pNext; /* All files are stored in a linked list */
! 129: sqlite3_file *pReal; /* The file handle for the underlying vfs */
! 130: };
! 131:
! 132: /*
! 133: ** Method declarations for jt_file.
! 134: */
! 135: static int jtClose(sqlite3_file*);
! 136: static int jtRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
! 137: static int jtWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
! 138: static int jtTruncate(sqlite3_file*, sqlite3_int64 size);
! 139: static int jtSync(sqlite3_file*, int flags);
! 140: static int jtFileSize(sqlite3_file*, sqlite3_int64 *pSize);
! 141: static int jtLock(sqlite3_file*, int);
! 142: static int jtUnlock(sqlite3_file*, int);
! 143: static int jtCheckReservedLock(sqlite3_file*, int *);
! 144: static int jtFileControl(sqlite3_file*, int op, void *pArg);
! 145: static int jtSectorSize(sqlite3_file*);
! 146: static int jtDeviceCharacteristics(sqlite3_file*);
! 147:
! 148: /*
! 149: ** Method declarations for jt_vfs.
! 150: */
! 151: static int jtOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
! 152: static int jtDelete(sqlite3_vfs*, const char *zName, int syncDir);
! 153: static int jtAccess(sqlite3_vfs*, const char *zName, int flags, int *);
! 154: static int jtFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
! 155: static void *jtDlOpen(sqlite3_vfs*, const char *zFilename);
! 156: static void jtDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
! 157: static void (*jtDlSym(sqlite3_vfs*,void*, const char *zSymbol))(void);
! 158: static void jtDlClose(sqlite3_vfs*, void*);
! 159: static int jtRandomness(sqlite3_vfs*, int nByte, char *zOut);
! 160: static int jtSleep(sqlite3_vfs*, int microseconds);
! 161: static int jtCurrentTime(sqlite3_vfs*, double*);
! 162: static int jtCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
! 163:
! 164: static sqlite3_vfs jt_vfs = {
! 165: 2, /* iVersion */
! 166: sizeof(jt_file), /* szOsFile */
! 167: JT_MAX_PATHNAME, /* mxPathname */
! 168: 0, /* pNext */
! 169: JT_VFS_NAME, /* zName */
! 170: 0, /* pAppData */
! 171: jtOpen, /* xOpen */
! 172: jtDelete, /* xDelete */
! 173: jtAccess, /* xAccess */
! 174: jtFullPathname, /* xFullPathname */
! 175: jtDlOpen, /* xDlOpen */
! 176: jtDlError, /* xDlError */
! 177: jtDlSym, /* xDlSym */
! 178: jtDlClose, /* xDlClose */
! 179: jtRandomness, /* xRandomness */
! 180: jtSleep, /* xSleep */
! 181: jtCurrentTime, /* xCurrentTime */
! 182: 0, /* xGetLastError */
! 183: jtCurrentTimeInt64 /* xCurrentTimeInt64 */
! 184: };
! 185:
! 186: static sqlite3_io_methods jt_io_methods = {
! 187: 1, /* iVersion */
! 188: jtClose, /* xClose */
! 189: jtRead, /* xRead */
! 190: jtWrite, /* xWrite */
! 191: jtTruncate, /* xTruncate */
! 192: jtSync, /* xSync */
! 193: jtFileSize, /* xFileSize */
! 194: jtLock, /* xLock */
! 195: jtUnlock, /* xUnlock */
! 196: jtCheckReservedLock, /* xCheckReservedLock */
! 197: jtFileControl, /* xFileControl */
! 198: jtSectorSize, /* xSectorSize */
! 199: jtDeviceCharacteristics /* xDeviceCharacteristics */
! 200: };
! 201:
! 202: struct JtGlobal {
! 203: sqlite3_vfs *pVfs; /* Parent VFS */
! 204: jt_file *pList; /* List of all open files */
! 205: };
! 206: static struct JtGlobal g = {0, 0};
! 207:
! 208: /*
! 209: ** Functions to obtain and relinquish a mutex to protect g.pList. The
! 210: ** STATIC_PRNG mutex is reused, purely for the sake of convenience.
! 211: */
! 212: static void enterJtMutex(void){
! 213: sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_PRNG));
! 214: }
! 215: static void leaveJtMutex(void){
! 216: sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_PRNG));
! 217: }
! 218:
! 219: extern int sqlite3_io_error_pending;
! 220: extern int sqlite3_io_error_hit;
! 221: static void stop_ioerr_simulation(int *piSave, int *piSave2){
! 222: *piSave = sqlite3_io_error_pending;
! 223: *piSave2 = sqlite3_io_error_hit;
! 224: sqlite3_io_error_pending = -1;
! 225: sqlite3_io_error_hit = 0;
! 226: }
! 227: static void start_ioerr_simulation(int iSave, int iSave2){
! 228: sqlite3_io_error_pending = iSave;
! 229: sqlite3_io_error_hit = iSave2;
! 230: }
! 231:
! 232: /*
! 233: ** The jt_file pointed to by the argument may or may not be a file-handle
! 234: ** open on a main database file. If it is, and a transaction is currently
! 235: ** opened on the file, then discard all transaction related data.
! 236: */
! 237: static void closeTransaction(jt_file *p){
! 238: sqlite3BitvecDestroy(p->pWritable);
! 239: sqlite3_free(p->aCksum);
! 240: p->pWritable = 0;
! 241: p->aCksum = 0;
! 242: p->nSync = 0;
! 243: }
! 244:
! 245: /*
! 246: ** Close an jt-file.
! 247: */
! 248: static int jtClose(sqlite3_file *pFile){
! 249: jt_file **pp;
! 250: jt_file *p = (jt_file *)pFile;
! 251:
! 252: closeTransaction(p);
! 253: enterJtMutex();
! 254: if( p->zName ){
! 255: for(pp=&g.pList; *pp!=p; pp=&(*pp)->pNext);
! 256: *pp = p->pNext;
! 257: }
! 258: leaveJtMutex();
! 259: return sqlite3OsClose(p->pReal);
! 260: }
! 261:
! 262: /*
! 263: ** Read data from an jt-file.
! 264: */
! 265: static int jtRead(
! 266: sqlite3_file *pFile,
! 267: void *zBuf,
! 268: int iAmt,
! 269: sqlite_int64 iOfst
! 270: ){
! 271: jt_file *p = (jt_file *)pFile;
! 272: return sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst);
! 273: }
! 274:
! 275: /*
! 276: ** Parameter zJournal is the name of a journal file that is currently
! 277: ** open. This function locates and returns the handle opened on the
! 278: ** corresponding database file by the pager that currently has the
! 279: ** journal file opened. This file-handle is identified by the
! 280: ** following properties:
! 281: **
! 282: ** a) SQLITE_OPEN_MAIN_DB was specified when the file was opened.
! 283: **
! 284: ** b) The file-name specified when the file was opened matches
! 285: ** all but the final 8 characters of the journal file name.
! 286: **
! 287: ** c) There is currently a reserved lock on the file.
! 288: **/
! 289: static jt_file *locateDatabaseHandle(const char *zJournal){
! 290: jt_file *pMain = 0;
! 291: enterJtMutex();
! 292: for(pMain=g.pList; pMain; pMain=pMain->pNext){
! 293: int nName = strlen(zJournal) - strlen("-journal");
! 294: if( (pMain->flags&SQLITE_OPEN_MAIN_DB)
! 295: && (strlen(pMain->zName)==nName)
! 296: && 0==memcmp(pMain->zName, zJournal, nName)
! 297: && (pMain->eLock>=SQLITE_LOCK_RESERVED)
! 298: ){
! 299: break;
! 300: }
! 301: }
! 302: leaveJtMutex();
! 303: return pMain;
! 304: }
! 305:
! 306: /*
! 307: ** Parameter z points to a buffer of 4 bytes in size containing a
! 308: ** unsigned 32-bit integer stored in big-endian format. Decode the
! 309: ** integer and return its value.
! 310: */
! 311: static u32 decodeUint32(const unsigned char *z){
! 312: return (z[0]<<24) + (z[1]<<16) + (z[2]<<8) + z[3];
! 313: }
! 314:
! 315: /*
! 316: ** Calculate a checksum from the buffer of length n bytes pointed to
! 317: ** by parameter z.
! 318: */
! 319: static u32 genCksum(const unsigned char *z, int n){
! 320: int i;
! 321: u32 cksum = 0;
! 322: for(i=0; i<n; i++){
! 323: cksum = cksum + z[i] + (cksum<<3);
! 324: }
! 325: return cksum;
! 326: }
! 327:
! 328: /*
! 329: ** The first argument, zBuf, points to a buffer containing a 28 byte
! 330: ** serialized journal header. This function deserializes four of the
! 331: ** integer fields contained in the journal header and writes their
! 332: ** values to the output variables.
! 333: **
! 334: ** SQLITE_OK is returned if the journal-header is successfully
! 335: ** decoded. Otherwise, SQLITE_ERROR.
! 336: */
! 337: static int decodeJournalHdr(
! 338: const unsigned char *zBuf, /* Input: 28 byte journal header */
! 339: u32 *pnRec, /* Out: Number of journalled records */
! 340: u32 *pnPage, /* Out: Original database page count */
! 341: u32 *pnSector, /* Out: Sector size in bytes */
! 342: u32 *pnPagesize /* Out: Page size in bytes */
! 343: ){
! 344: unsigned char aMagic[] = { 0xd9, 0xd5, 0x05, 0xf9, 0x20, 0xa1, 0x63, 0xd7 };
! 345: if( memcmp(aMagic, zBuf, 8) ) return SQLITE_ERROR;
! 346: if( pnRec ) *pnRec = decodeUint32(&zBuf[8]);
! 347: if( pnPage ) *pnPage = decodeUint32(&zBuf[16]);
! 348: if( pnSector ) *pnSector = decodeUint32(&zBuf[20]);
! 349: if( pnPagesize ) *pnPagesize = decodeUint32(&zBuf[24]);
! 350: return SQLITE_OK;
! 351: }
! 352:
! 353: /*
! 354: ** This function is called when a new transaction is opened, just after
! 355: ** the first journal-header is written to the journal file.
! 356: */
! 357: static int openTransaction(jt_file *pMain, jt_file *pJournal){
! 358: unsigned char *aData;
! 359: sqlite3_file *p = pMain->pReal;
! 360: int rc = SQLITE_OK;
! 361:
! 362: closeTransaction(pMain);
! 363: aData = sqlite3_malloc(pMain->nPagesize);
! 364: pMain->pWritable = sqlite3BitvecCreate(pMain->nPage);
! 365: pMain->aCksum = sqlite3_malloc(sizeof(u32) * (pMain->nPage + 1));
! 366: pJournal->iMaxOff = 0;
! 367:
! 368: if( !pMain->pWritable || !pMain->aCksum || !aData ){
! 369: rc = SQLITE_IOERR_NOMEM;
! 370: }else if( pMain->nPage>0 ){
! 371: u32 iTrunk;
! 372: int iSave;
! 373: int iSave2;
! 374:
! 375: stop_ioerr_simulation(&iSave, &iSave2);
! 376:
! 377: /* Read the database free-list. Add the page-number for each free-list
! 378: ** leaf to the jt_file.pWritable bitvec.
! 379: */
! 380: rc = sqlite3OsRead(p, aData, pMain->nPagesize, 0);
! 381: if( rc==SQLITE_OK ){
! 382: u32 nDbsize = decodeUint32(&aData[28]);
! 383: if( nDbsize>0 && memcmp(&aData[24], &aData[92], 4)==0 ){
! 384: u32 iPg;
! 385: for(iPg=nDbsize+1; iPg<=pMain->nPage; iPg++){
! 386: sqlite3BitvecSet(pMain->pWritable, iPg);
! 387: }
! 388: }
! 389: }
! 390: iTrunk = decodeUint32(&aData[32]);
! 391: while( rc==SQLITE_OK && iTrunk>0 ){
! 392: u32 nLeaf;
! 393: u32 iLeaf;
! 394: sqlite3_int64 iOff = (i64)(iTrunk-1)*pMain->nPagesize;
! 395: rc = sqlite3OsRead(p, aData, pMain->nPagesize, iOff);
! 396: nLeaf = decodeUint32(&aData[4]);
! 397: for(iLeaf=0; rc==SQLITE_OK && iLeaf<nLeaf; iLeaf++){
! 398: u32 pgno = decodeUint32(&aData[8+4*iLeaf]);
! 399: sqlite3BitvecSet(pMain->pWritable, pgno);
! 400: }
! 401: iTrunk = decodeUint32(aData);
! 402: }
! 403:
! 404: /* Calculate and store a checksum for each page in the database file. */
! 405: if( rc==SQLITE_OK ){
! 406: int ii;
! 407: for(ii=0; rc==SQLITE_OK && ii<pMain->nPage; ii++){
! 408: i64 iOff = (i64)(pMain->nPagesize) * (i64)ii;
! 409: if( iOff==PENDING_BYTE ) continue;
! 410: rc = sqlite3OsRead(pMain->pReal, aData, pMain->nPagesize, iOff);
! 411: pMain->aCksum[ii] = genCksum(aData, pMain->nPagesize);
! 412: if( ii+1==pMain->nPage && rc==SQLITE_IOERR_SHORT_READ ) rc = SQLITE_OK;
! 413: }
! 414: }
! 415:
! 416: start_ioerr_simulation(iSave, iSave2);
! 417: }
! 418:
! 419: sqlite3_free(aData);
! 420: return rc;
! 421: }
! 422:
! 423: /*
! 424: ** The first argument to this function is a handle open on a journal file.
! 425: ** This function reads the journal file and adds the page number for each
! 426: ** page in the journal to the Bitvec object passed as the second argument.
! 427: */
! 428: static int readJournalFile(jt_file *p, jt_file *pMain){
! 429: int rc = SQLITE_OK;
! 430: unsigned char zBuf[28];
! 431: sqlite3_file *pReal = p->pReal;
! 432: sqlite3_int64 iOff = 0;
! 433: sqlite3_int64 iSize = p->iMaxOff;
! 434: unsigned char *aPage;
! 435: int iSave;
! 436: int iSave2;
! 437:
! 438: aPage = sqlite3_malloc(pMain->nPagesize);
! 439: if( !aPage ){
! 440: return SQLITE_IOERR_NOMEM;
! 441: }
! 442:
! 443: stop_ioerr_simulation(&iSave, &iSave2);
! 444:
! 445: while( rc==SQLITE_OK && iOff<iSize ){
! 446: u32 nRec, nPage, nSector, nPagesize;
! 447: u32 ii;
! 448:
! 449: /* Read and decode the next journal-header from the journal file. */
! 450: rc = sqlite3OsRead(pReal, zBuf, 28, iOff);
! 451: if( rc!=SQLITE_OK
! 452: || decodeJournalHdr(zBuf, &nRec, &nPage, &nSector, &nPagesize)
! 453: ){
! 454: goto finish_rjf;
! 455: }
! 456: iOff += nSector;
! 457:
! 458: if( nRec==0 ){
! 459: /* A trick. There might be another journal-header immediately
! 460: ** following this one. In this case, 0 records means 0 records,
! 461: ** not "read until the end of the file". See also ticket #2565.
! 462: */
! 463: if( iSize>=(iOff+nSector) ){
! 464: rc = sqlite3OsRead(pReal, zBuf, 28, iOff);
! 465: if( rc!=SQLITE_OK || 0==decodeJournalHdr(zBuf, 0, 0, 0, 0) ){
! 466: continue;
! 467: }
! 468: }
! 469: nRec = (iSize-iOff) / (pMain->nPagesize+8);
! 470: }
! 471:
! 472: /* Read all the records that follow the journal-header just read. */
! 473: for(ii=0; rc==SQLITE_OK && ii<nRec && iOff<iSize; ii++){
! 474: u32 pgno;
! 475: rc = sqlite3OsRead(pReal, zBuf, 4, iOff);
! 476: if( rc==SQLITE_OK ){
! 477: pgno = decodeUint32(zBuf);
! 478: if( pgno>0 && pgno<=pMain->nPage ){
! 479: if( 0==sqlite3BitvecTest(pMain->pWritable, pgno) ){
! 480: rc = sqlite3OsRead(pReal, aPage, pMain->nPagesize, iOff+4);
! 481: if( rc==SQLITE_OK ){
! 482: u32 cksum = genCksum(aPage, pMain->nPagesize);
! 483: assert( cksum==pMain->aCksum[pgno-1] );
! 484: }
! 485: }
! 486: sqlite3BitvecSet(pMain->pWritable, pgno);
! 487: }
! 488: iOff += (8 + pMain->nPagesize);
! 489: }
! 490: }
! 491:
! 492: iOff = ((iOff + (nSector-1)) / nSector) * nSector;
! 493: }
! 494:
! 495: finish_rjf:
! 496: start_ioerr_simulation(iSave, iSave2);
! 497: sqlite3_free(aPage);
! 498: if( rc==SQLITE_IOERR_SHORT_READ ){
! 499: rc = SQLITE_OK;
! 500: }
! 501: return rc;
! 502: }
! 503:
! 504: /*
! 505: ** Write data to an jt-file.
! 506: */
! 507: static int jtWrite(
! 508: sqlite3_file *pFile,
! 509: const void *zBuf,
! 510: int iAmt,
! 511: sqlite_int64 iOfst
! 512: ){
! 513: int rc;
! 514: jt_file *p = (jt_file *)pFile;
! 515: if( p->flags&SQLITE_OPEN_MAIN_JOURNAL ){
! 516: if( iOfst==0 ){
! 517: jt_file *pMain = locateDatabaseHandle(p->zName);
! 518: assert( pMain );
! 519:
! 520: if( iAmt==28 ){
! 521: /* Zeroing the first journal-file header. This is the end of a
! 522: ** transaction. */
! 523: closeTransaction(pMain);
! 524: }else if( iAmt!=12 ){
! 525: /* Writing the first journal header to a journal file. This happens
! 526: ** when a transaction is first started. */
! 527: u8 *z = (u8 *)zBuf;
! 528: pMain->nPage = decodeUint32(&z[16]);
! 529: pMain->nPagesize = decodeUint32(&z[24]);
! 530: if( SQLITE_OK!=(rc=openTransaction(pMain, p)) ){
! 531: return rc;
! 532: }
! 533: }
! 534: }
! 535: if( p->iMaxOff<(iOfst + iAmt) ){
! 536: p->iMaxOff = iOfst + iAmt;
! 537: }
! 538: }
! 539:
! 540: if( p->flags&SQLITE_OPEN_MAIN_DB && p->pWritable ){
! 541: if( iAmt<p->nPagesize
! 542: && p->nPagesize%iAmt==0
! 543: && iOfst>=(PENDING_BYTE+512)
! 544: && iOfst+iAmt<=PENDING_BYTE+p->nPagesize
! 545: ){
! 546: /* No-op. This special case is hit when the backup code is copying a
! 547: ** to a database with a larger page-size than the source database and
! 548: ** it needs to fill in the non-locking-region part of the original
! 549: ** pending-byte page.
! 550: */
! 551: }else{
! 552: u32 pgno = iOfst/p->nPagesize + 1;
! 553: assert( (iAmt==1||iAmt==p->nPagesize) && ((iOfst+iAmt)%p->nPagesize)==0 );
! 554: assert( pgno<=p->nPage || p->nSync>0 );
! 555: assert( pgno>p->nPage || sqlite3BitvecTest(p->pWritable, pgno) );
! 556: }
! 557: }
! 558:
! 559: rc = sqlite3OsWrite(p->pReal, zBuf, iAmt, iOfst);
! 560: if( (p->flags&SQLITE_OPEN_MAIN_JOURNAL) && iAmt==12 ){
! 561: jt_file *pMain = locateDatabaseHandle(p->zName);
! 562: int rc2 = readJournalFile(p, pMain);
! 563: if( rc==SQLITE_OK ) rc = rc2;
! 564: }
! 565: return rc;
! 566: }
! 567:
! 568: /*
! 569: ** Truncate an jt-file.
! 570: */
! 571: static int jtTruncate(sqlite3_file *pFile, sqlite_int64 size){
! 572: jt_file *p = (jt_file *)pFile;
! 573: if( p->flags&SQLITE_OPEN_MAIN_JOURNAL && size==0 ){
! 574: /* Truncating a journal file. This is the end of a transaction. */
! 575: jt_file *pMain = locateDatabaseHandle(p->zName);
! 576: closeTransaction(pMain);
! 577: }
! 578: if( p->flags&SQLITE_OPEN_MAIN_DB && p->pWritable ){
! 579: u32 pgno;
! 580: u32 locking_page = (u32)(PENDING_BYTE/p->nPagesize+1);
! 581: for(pgno=size/p->nPagesize+1; pgno<=p->nPage; pgno++){
! 582: assert( pgno==locking_page || sqlite3BitvecTest(p->pWritable, pgno) );
! 583: }
! 584: }
! 585: return sqlite3OsTruncate(p->pReal, size);
! 586: }
! 587:
! 588: /*
! 589: ** Sync an jt-file.
! 590: */
! 591: static int jtSync(sqlite3_file *pFile, int flags){
! 592: jt_file *p = (jt_file *)pFile;
! 593:
! 594: if( p->flags&SQLITE_OPEN_MAIN_JOURNAL ){
! 595: int rc;
! 596: jt_file *pMain; /* The associated database file */
! 597:
! 598: /* The journal file is being synced. At this point, we inspect the
! 599: ** contents of the file up to this point and set each bit in the
! 600: ** jt_file.pWritable bitvec of the main database file associated with
! 601: ** this journal file.
! 602: */
! 603: pMain = locateDatabaseHandle(p->zName);
! 604: assert(pMain);
! 605:
! 606: /* Set the bitvec values */
! 607: if( pMain->pWritable ){
! 608: pMain->nSync++;
! 609: rc = readJournalFile(p, pMain);
! 610: if( rc!=SQLITE_OK ){
! 611: return rc;
! 612: }
! 613: }
! 614: }
! 615:
! 616: return sqlite3OsSync(p->pReal, flags);
! 617: }
! 618:
! 619: /*
! 620: ** Return the current file-size of an jt-file.
! 621: */
! 622: static int jtFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
! 623: jt_file *p = (jt_file *)pFile;
! 624: return sqlite3OsFileSize(p->pReal, pSize);
! 625: }
! 626:
! 627: /*
! 628: ** Lock an jt-file.
! 629: */
! 630: static int jtLock(sqlite3_file *pFile, int eLock){
! 631: int rc;
! 632: jt_file *p = (jt_file *)pFile;
! 633: rc = sqlite3OsLock(p->pReal, eLock);
! 634: if( rc==SQLITE_OK && eLock>p->eLock ){
! 635: p->eLock = eLock;
! 636: }
! 637: return rc;
! 638: }
! 639:
! 640: /*
! 641: ** Unlock an jt-file.
! 642: */
! 643: static int jtUnlock(sqlite3_file *pFile, int eLock){
! 644: int rc;
! 645: jt_file *p = (jt_file *)pFile;
! 646: rc = sqlite3OsUnlock(p->pReal, eLock);
! 647: if( rc==SQLITE_OK && eLock<p->eLock ){
! 648: p->eLock = eLock;
! 649: }
! 650: return rc;
! 651: }
! 652:
! 653: /*
! 654: ** Check if another file-handle holds a RESERVED lock on an jt-file.
! 655: */
! 656: static int jtCheckReservedLock(sqlite3_file *pFile, int *pResOut){
! 657: jt_file *p = (jt_file *)pFile;
! 658: return sqlite3OsCheckReservedLock(p->pReal, pResOut);
! 659: }
! 660:
! 661: /*
! 662: ** File control method. For custom operations on an jt-file.
! 663: */
! 664: static int jtFileControl(sqlite3_file *pFile, int op, void *pArg){
! 665: jt_file *p = (jt_file *)pFile;
! 666: return p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
! 667: }
! 668:
! 669: /*
! 670: ** Return the sector-size in bytes for an jt-file.
! 671: */
! 672: static int jtSectorSize(sqlite3_file *pFile){
! 673: jt_file *p = (jt_file *)pFile;
! 674: return sqlite3OsSectorSize(p->pReal);
! 675: }
! 676:
! 677: /*
! 678: ** Return the device characteristic flags supported by an jt-file.
! 679: */
! 680: static int jtDeviceCharacteristics(sqlite3_file *pFile){
! 681: jt_file *p = (jt_file *)pFile;
! 682: return sqlite3OsDeviceCharacteristics(p->pReal);
! 683: }
! 684:
! 685: /*
! 686: ** Open an jt file handle.
! 687: */
! 688: static int jtOpen(
! 689: sqlite3_vfs *pVfs,
! 690: const char *zName,
! 691: sqlite3_file *pFile,
! 692: int flags,
! 693: int *pOutFlags
! 694: ){
! 695: int rc;
! 696: jt_file *p = (jt_file *)pFile;
! 697: pFile->pMethods = 0;
! 698: p->pReal = (sqlite3_file *)&p[1];
! 699: p->pReal->pMethods = 0;
! 700: rc = sqlite3OsOpen(g.pVfs, zName, p->pReal, flags, pOutFlags);
! 701: assert( rc==SQLITE_OK || p->pReal->pMethods==0 );
! 702: if( rc==SQLITE_OK ){
! 703: pFile->pMethods = &jt_io_methods;
! 704: p->eLock = 0;
! 705: p->zName = zName;
! 706: p->flags = flags;
! 707: p->pNext = 0;
! 708: p->pWritable = 0;
! 709: p->aCksum = 0;
! 710: enterJtMutex();
! 711: if( zName ){
! 712: p->pNext = g.pList;
! 713: g.pList = p;
! 714: }
! 715: leaveJtMutex();
! 716: }
! 717: return rc;
! 718: }
! 719:
! 720: /*
! 721: ** Delete the file located at zPath. If the dirSync argument is true,
! 722: ** ensure the file-system modifications are synced to disk before
! 723: ** returning.
! 724: */
! 725: static int jtDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
! 726: int nPath = strlen(zPath);
! 727: if( nPath>8 && 0==strcmp("-journal", &zPath[nPath-8]) ){
! 728: /* Deleting a journal file. The end of a transaction. */
! 729: jt_file *pMain = locateDatabaseHandle(zPath);
! 730: if( pMain ){
! 731: closeTransaction(pMain);
! 732: }
! 733: }
! 734:
! 735: return sqlite3OsDelete(g.pVfs, zPath, dirSync);
! 736: }
! 737:
! 738: /*
! 739: ** Test for access permissions. Return true if the requested permission
! 740: ** is available, or false otherwise.
! 741: */
! 742: static int jtAccess(
! 743: sqlite3_vfs *pVfs,
! 744: const char *zPath,
! 745: int flags,
! 746: int *pResOut
! 747: ){
! 748: return sqlite3OsAccess(g.pVfs, zPath, flags, pResOut);
! 749: }
! 750:
! 751: /*
! 752: ** Populate buffer zOut with the full canonical pathname corresponding
! 753: ** to the pathname in zPath. zOut is guaranteed to point to a buffer
! 754: ** of at least (JT_MAX_PATHNAME+1) bytes.
! 755: */
! 756: static int jtFullPathname(
! 757: sqlite3_vfs *pVfs,
! 758: const char *zPath,
! 759: int nOut,
! 760: char *zOut
! 761: ){
! 762: return sqlite3OsFullPathname(g.pVfs, zPath, nOut, zOut);
! 763: }
! 764:
! 765: /*
! 766: ** Open the dynamic library located at zPath and return a handle.
! 767: */
! 768: static void *jtDlOpen(sqlite3_vfs *pVfs, const char *zPath){
! 769: return g.pVfs->xDlOpen(g.pVfs, zPath);
! 770: }
! 771:
! 772: /*
! 773: ** Populate the buffer zErrMsg (size nByte bytes) with a human readable
! 774: ** utf-8 string describing the most recent error encountered associated
! 775: ** with dynamic libraries.
! 776: */
! 777: static void jtDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
! 778: g.pVfs->xDlError(g.pVfs, nByte, zErrMsg);
! 779: }
! 780:
! 781: /*
! 782: ** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
! 783: */
! 784: static void (*jtDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
! 785: return g.pVfs->xDlSym(g.pVfs, p, zSym);
! 786: }
! 787:
! 788: /*
! 789: ** Close the dynamic library handle pHandle.
! 790: */
! 791: static void jtDlClose(sqlite3_vfs *pVfs, void *pHandle){
! 792: g.pVfs->xDlClose(g.pVfs, pHandle);
! 793: }
! 794:
! 795: /*
! 796: ** Populate the buffer pointed to by zBufOut with nByte bytes of
! 797: ** random data.
! 798: */
! 799: static int jtRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
! 800: return sqlite3OsRandomness(g.pVfs, nByte, zBufOut);
! 801: }
! 802:
! 803: /*
! 804: ** Sleep for nMicro microseconds. Return the number of microseconds
! 805: ** actually slept.
! 806: */
! 807: static int jtSleep(sqlite3_vfs *pVfs, int nMicro){
! 808: return sqlite3OsSleep(g.pVfs, nMicro);
! 809: }
! 810:
! 811: /*
! 812: ** Return the current time as a Julian Day number in *pTimeOut.
! 813: */
! 814: static int jtCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
! 815: return g.pVfs->xCurrentTime(g.pVfs, pTimeOut);
! 816: }
! 817: /*
! 818: ** Return the current time as a Julian Day number in *pTimeOut.
! 819: */
! 820: static int jtCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){
! 821: return g.pVfs->xCurrentTimeInt64(g.pVfs, pTimeOut);
! 822: }
! 823:
! 824: /**************************************************************************
! 825: ** Start of public API.
! 826: */
! 827:
! 828: /*
! 829: ** Configure the jt VFS as a wrapper around the VFS named by parameter
! 830: ** zWrap. If the isDefault parameter is true, then the jt VFS is installed
! 831: ** as the new default VFS for SQLite connections. If isDefault is not
! 832: ** true, then the jt VFS is installed as non-default. In this case it
! 833: ** is available via its name, "jt".
! 834: */
! 835: int jt_register(char *zWrap, int isDefault){
! 836: g.pVfs = sqlite3_vfs_find(zWrap);
! 837: if( g.pVfs==0 ){
! 838: return SQLITE_ERROR;
! 839: }
! 840: jt_vfs.szOsFile = sizeof(jt_file) + g.pVfs->szOsFile;
! 841: if( g.pVfs->iVersion==1 ){
! 842: jt_vfs.iVersion = 1;
! 843: }else if( g.pVfs->xCurrentTimeInt64==0 ){
! 844: jt_vfs.xCurrentTimeInt64 = 0;
! 845: }
! 846: sqlite3_vfs_register(&jt_vfs, isDefault);
! 847: return SQLITE_OK;
! 848: }
! 849:
! 850: /*
! 851: ** Uninstall the jt VFS, if it is installed.
! 852: */
! 853: void jt_unregister(void){
! 854: sqlite3_vfs_unregister(&jt_vfs);
! 855: }
! 856:
! 857: #endif
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>