/* ** 2004 May 22 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ****************************************************************************** ** ** This file contains code that is specific to Unix systems. It is used ** for testing SQLite only. */ #if OS_TEST /* This file is used for the test backend only */ #include "sqliteInt.h" #include "os.h" /* Must be first to enable large file support */ #define sqlite3OsOpenReadWrite sqlite3RealOpenReadWrite #define sqlite3OsOpenExclusive sqlite3RealOpenExclusive #define sqlite3OsOpenReadOnly sqlite3RealOpenReadOnly #define sqlite3OsOpenDirectory sqlite3RealOpenDirectory #define sqlite3OsClose sqlite3RealClose #define sqlite3OsRead sqlite3RealRead #define sqlite3OsWrite sqlite3RealWrite #define sqlite3OsSeek sqlite3RealSeek #define sqlite3OsSync sqlite3RealSync #define sqlite3OsTruncate sqlite3RealTruncate #define sqlite3OsFileSize sqlite3RealFileSize #define sqlite3OsLock sqlite3RealLock #define sqlite3OsUnlock sqlite3RealUnlock #define sqlite3OsCheckReservedLock sqlite3RealCheckReservedLock #define OsFile OsRealFile #define OS_UNIX 1 #include "os_unix.c" #undef OS_UNIX #undef OsFile #undef sqlite3OsOpenReadWrite #undef sqlite3OsOpenExclusive #undef sqlite3OsOpenReadOnly #undef sqlite3OsOpenDirectory #undef sqlite3OsClose #undef sqlite3OsRead #undef sqlite3OsWrite #undef sqlite3OsSeek #undef sqlite3OsSync #undef sqlite3OsTruncate #undef sqlite3OsFileSize #undef sqlite3OsLock #undef sqlite3OsUnlock #undef sqlite3OsCheckReservedLock #define BLOCKSIZE 512 #define BLOCK_OFFSET(x) ((x) * BLOCKSIZE) /* ** The following variables control when a simulated crash occurs. ** ** If iCrashDelay is non-zero, then zCrashFile contains (full path) name of ** a file that SQLite will call sqlite3OsSync() on. Each time this happens ** iCrashDelay is decremented. If iCrashDelay is zero after being ** decremented, a "crash" occurs during the sync() operation. ** ** In other words, a crash occurs the iCrashDelay'th time zCrashFile is ** synced. */ static int iCrashDelay = 0; char zCrashFile[256]; /* ** Set the value of the two crash parameters. */ void sqlite3SetCrashParams(int iDelay, char const *zFile){ sqlite3OsEnterMutex(); assert( strlen(zFile)<256 ); strcpy(zCrashFile, zFile); iCrashDelay = iDelay; sqlite3OsLeaveMutex(); } /* ** File zPath is being sync()ed. Return non-zero if this should ** cause a crash. */ static int crashRequired(char const *zPath){ int r; int n; sqlite3OsEnterMutex(); n = strlen(zCrashFile); if( zCrashFile[n-1]=='*' ){ n--; }else if( strlen(zPath)>n ){ n = strlen(zPath); } r = 0; if( iCrashDelay>0 && strncmp(zPath, zCrashFile, n)==0 ){ iCrashDelay--; if( iCrashDelay<=0 ){ r = 1; } } sqlite3OsLeaveMutex(); return r; } static OsTestFile *pAllFiles = 0; /* ** Initialise the os_test.c specific fields of pFile. */ static void initFile(OsFile *id, char const *zName){ OsTestFile *pFile = (OsTestFile *) sqliteMalloc(sizeof(OsTestFile) + strlen(zName)+1); pFile->nMaxWrite = 0; pFile->nBlk = 0; pFile->apBlk = 0; pFile->zName = (char *)(&pFile[1]); strcpy(pFile->zName, zName); *id = pFile; pFile->pNext = pAllFiles; pAllFiles = pFile; } /* ** Undo the work done by initFile. Delete the OsTestFile structure ** and unlink the structure from the pAllFiles list. */ static void closeFile(OsFile *id){ OsTestFile *pFile = *id; if( pFile==pAllFiles ){ pAllFiles = pFile->pNext; }else{ OsTestFile *p; for(p=pAllFiles; p->pNext!=pFile; p=p->pNext ){ assert( p ); } p->pNext = pFile->pNext; } sqliteFree(pFile); *id = 0; } /* ** Return the current seek offset from the start of the file. This ** is unix-only code. */ static i64 osTell(OsTestFile *pFile){ return lseek(pFile->fd.h, 0, SEEK_CUR); } /* ** Load block 'blk' into the cache of pFile. */ static int cacheBlock(OsTestFile *pFile, int blk){ if( blk>=pFile->nBlk ){ int n = ((pFile->nBlk * 2) + 100 + blk); /* if( pFile->nBlk==0 ){ printf("DIRTY %s\n", pFile->zName); } */ pFile->apBlk = (u8 **)sqliteRealloc(pFile->apBlk, n * sizeof(u8*)); if( !pFile->apBlk ) return SQLITE_NOMEM; memset(&pFile->apBlk[pFile->nBlk], 0, (n - pFile->nBlk)*sizeof(u8*)); pFile->nBlk = n; } if( !pFile->apBlk[blk] ){ i64 filesize; int rc; u8 *p = sqliteMalloc(BLOCKSIZE); if( !p ) return SQLITE_NOMEM; pFile->apBlk[blk] = p; rc = sqlite3RealFileSize(&pFile->fd, &filesize); if( rc!=SQLITE_OK ) return rc; if( BLOCK_OFFSET(blk)fd, blk*BLOCKSIZE); if( BLOCK_OFFSET(blk+1)>filesize ){ len = filesize - BLOCK_OFFSET(blk); } if( rc!=SQLITE_OK ) return rc; rc = sqlite3RealRead(&pFile->fd, p, len); if( rc!=SQLITE_OK ) return rc; } } return SQLITE_OK; } /* #define TRACE_WRITECACHE */ /* ** Write the cache of pFile to disk. If crash is non-zero, randomly ** skip blocks when writing. The cache is deleted before returning. */ static int writeCache2(OsTestFile *pFile, int crash){ int i; int nMax = pFile->nMaxWrite; i64 offset; int rc = SQLITE_OK; offset = osTell(pFile); for(i=0; inBlk; i++){ u8 *p = pFile->apBlk[i]; if( p ){ int skip = 0; int trash = 0; if( crash ){ char random; sqlite3Randomness(1, &random); if( random & 0x01 ){ if( random & 0x02 ){ trash = 1; #ifdef TRACE_WRITECACHE printf("Trashing block %d of %s\n", i, pFile->zName); #endif }else{ skip = 1; #ifdef TRACE_WRITECACHE printf("Skiping block %d of %s\n", i, pFile->zName); #endif } }else{ #ifdef TRACE_WRITECACHE printf("Writing block %d of %s\n", i, pFile->zName); #endif } } if( rc==SQLITE_OK ){ rc = sqlite3RealSeek(&pFile->fd, BLOCK_OFFSET(i)); } if( rc==SQLITE_OK && !skip ){ int len = BLOCKSIZE; if( BLOCK_OFFSET(i+1)>nMax ){ len = nMax-BLOCK_OFFSET(i); } if( len>0 ){ if( trash ){ sqlite3Randomness(len, p); } rc = sqlite3RealWrite(&pFile->fd, p, len); } } sqliteFree(p); } } sqliteFree(pFile->apBlk); pFile->nBlk = 0; pFile->apBlk = 0; pFile->nMaxWrite = 0; if( rc==SQLITE_OK ){ rc = sqlite3RealSeek(&pFile->fd, offset); } return rc; } /* ** Write the cache to disk. */ static int writeCache(OsTestFile *pFile){ if( pFile->apBlk ){ int c = crashRequired(pFile->zName); if( c ){ OsTestFile *p; #ifdef TRACE_WRITECACHE printf("\nCrash during sync of %s\n", pFile->zName); #endif for(p=pAllFiles; p; p=p->pNext){ writeCache2(p, 1); } exit(-1); }else{ return writeCache2(pFile, 0); } } return SQLITE_OK; } /* ** Close the file. */ int sqlite3OsClose(OsFile *id){ if( !(*id) ) return SQLITE_OK; if( (*id)->fd.isOpen ){ /* printf("CLOSE %s (%d blocks)\n", (*id)->zName, (*id)->nBlk); */ writeCache(*id); sqlite3RealClose(&(*id)->fd); } closeFile(id); return SQLITE_OK; } int sqlite3OsRead(OsFile *id, void *pBuf, int amt){ i64 offset; /* The current offset from the start of the file */ i64 end; /* The byte just past the last byte read */ int blk; /* Block number the read starts on */ int i; u8 *zCsr; int rc = SQLITE_OK; OsTestFile *pFile = *id; offset = osTell(pFile); end = offset+amt; blk = (offset/BLOCKSIZE); zCsr = (u8 *)pBuf; for(i=blk; i*BLOCKSIZE end ){ len = len - (BLOCK_OFFSET(i+1)-end); } if( inBlk && pFile->apBlk[i]){ u8 *pBlk = pFile->apBlk[i]; memcpy(zCsr, &pBlk[off], len); }else{ rc = sqlite3RealSeek(&pFile->fd, BLOCK_OFFSET(i) + off); if( rc!=SQLITE_OK ) return rc; rc = sqlite3RealRead(&pFile->fd, zCsr, len); if( rc!=SQLITE_OK ) return rc; } zCsr += len; } assert( zCsr==&((u8 *)pBuf)[amt] ); rc = sqlite3RealSeek(&pFile->fd, end); return rc; } int sqlite3OsWrite(OsFile *id, const void *pBuf, int amt){ i64 offset; /* The current offset from the start of the file */ i64 end; /* The byte just past the last byte written */ int blk; /* Block number the write starts on */ int i; const u8 *zCsr; int rc = SQLITE_OK; OsTestFile *pFile = *id; offset = osTell(pFile); end = offset+amt; blk = (offset/BLOCKSIZE); zCsr = (u8 *)pBuf; for(i=blk; i*BLOCKSIZEapBlk[i]; assert( pBlk ); if( BLOCK_OFFSET(i) < offset ){ off = offset-BLOCK_OFFSET(i); } len = BLOCKSIZE - off; if( BLOCK_OFFSET(i+1) > end ){ len = len - (BLOCK_OFFSET(i+1)-end); } memcpy(&pBlk[off], zCsr, len); zCsr += len; } if( pFile->nMaxWritenMaxWrite = end; } assert( zCsr==&((u8 *)pBuf)[amt] ); rc = sqlite3RealSeek(&pFile->fd, end); return rc; } /* ** Sync the file. First flush the write-cache to disk, then call the ** real sync() function. */ int sqlite3OsSync(OsFile *id){ int rc; /* printf("SYNC %s (%d blocks)\n", (*id)->zName, (*id)->nBlk); */ rc = writeCache(*id); if( rc!=SQLITE_OK ) return rc; rc = sqlite3RealSync(&(*id)->fd); return rc; } /* ** Truncate the file. Set the internal OsFile.nMaxWrite variable to the new ** file size to ensure that nothing in the write-cache past this point ** is written to disk. */ int sqlite3OsTruncate(OsFile *id, i64 nByte){ (*id)->nMaxWrite = nByte; return sqlite3RealTruncate(&(*id)->fd, nByte); } /* ** Return the size of the file. If the cache contains a write that extended ** the file, then return this size instead of the on-disk size. */ int sqlite3OsFileSize(OsFile *id, i64 *pSize){ int rc = sqlite3RealFileSize(&(*id)->fd, pSize); if( rc==SQLITE_OK && pSize && *pSize<(*id)->nMaxWrite ){ *pSize = (*id)->nMaxWrite; } return rc; } /* ** The three functions used to open files. All that is required is to ** initialise the os_test.c specific fields and then call the corresponding ** os_unix.c function to really open the file. */ int sqlite3OsOpenReadWrite(const char *zFilename, OsFile *id, int *pReadonly){ initFile(id, zFilename); return sqlite3RealOpenReadWrite(zFilename, &(*id)->fd, pReadonly); } int sqlite3OsOpenExclusive(const char *zFilename, OsFile *id, int delFlag){ initFile(id, zFilename); return sqlite3RealOpenExclusive(zFilename, &(*id)->fd, delFlag); } int sqlite3OsOpenReadOnly(const char *zFilename, OsFile *id){ initFile(id, zFilename); return sqlite3RealOpenReadOnly(zFilename, &(*id)->fd); } /* ** These six function calls are passed straight through to the os_unix.c ** backend. */ int sqlite3OsSeek(OsFile *id, i64 offset){ return sqlite3RealSeek(&(*id)->fd, offset); } int sqlite3OsCheckReservedLock(OsFile *id){ return sqlite3RealCheckReservedLock(&(*id)->fd); } int sqlite3OsLock(OsFile *id, int locktype){ return sqlite3RealLock(&(*id)->fd, locktype); } int sqlite3OsUnlock(OsFile *id, int locktype){ return sqlite3RealUnlock(&(*id)->fd, locktype); } int sqlite3OsOpenDirectory(const char *zDirname, OsFile *id){ return sqlite3RealOpenDirectory(zDirname, &(*id)->fd); } #endif /* OS_TEST */