/* ** If pFile is currently larger than iSize bytes, then truncate it to ** exactly iSize bytes. If pFile is not larger than iSize bytes, then ** this function is a no-op. ** ** Return SQLITE_OK if everything is successful, or an SQLite error ** code if an error occurs. */ static int backupTruncateFile(sqlite3_file *pFile, i64 iSize){ i64 iCurrent; int rc = sqlite3OsFileSize(pFile, &iCurrent); if( rc==SQLITE_OK && iCurrent>iSize ){ rc = sqlite3OsTruncate(pFile, iSize); } return rc; }
/* ** Truncate the file. */ static int jrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){ int rc = SQLITE_OK; JournalFile *p = (JournalFile *)pJfd; if( p->pReal ){ rc = sqlite3OsTruncate(p->pReal, size); }else if( size<p->iSize ){ p->iSize = size; } return rc; }
/* ** Truncate an jt-file. */ static int jtTruncate(sqlite3_file *pFile, sqlite_int64 size){ jt_file *p = (jt_file *)pFile; if( p->flags&SQLITE_OPEN_MAIN_JOURNAL && size==0 ){ /* Truncating a journal file. This is the end of a transaction. */ jt_file *pMain = locateDatabaseHandle(p->zName); closeTransaction(pMain); } if( p->flags&SQLITE_OPEN_MAIN_DB && p->pWritable ){ u32 pgno; u32 locking_page = (u32)(PENDING_BYTE/p->nPagesize+1); for(pgno=size/p->nPagesize+1; pgno<=p->nPage; pgno++){ assert( pgno==locking_page || sqlite3BitvecTest(p->pWritable, pgno) ); } } return sqlite3OsTruncate(p->pReal, size); }
/* ** Truncate an tvfs-file. */ static int tvfsTruncate(sqlite3_file *pFile, sqlite_int64 size){ int rc = SQLITE_OK; TestvfsFd *pFd = tvfsGetFd(pFile); Testvfs *p = (Testvfs *)pFd->pVfs->pAppData; if( p->pScript && p->mask&TESTVFS_TRUNCATE_MASK ){ tvfsExecTcl(p, "xTruncate", Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0, 0 ); tvfsResultCode(p, &rc); } if( rc==SQLITE_OK ){ rc = sqlite3OsTruncate(pFd->pReal, size); } return rc; }
/* ** Flush the write-list as if xSync() had been called on file handle ** pFile. If isCrash is true, simulate a crash. */ static int writeListSync(CrashFile *pFile, int isCrash){ int rc = SQLITE_OK; int iDc = g.iDeviceCharacteristics; WriteBuffer *pWrite; WriteBuffer **ppPtr; /* If this is not a crash simulation, set pFinal to point to the ** last element of the write-list that is associated with file handle ** pFile. ** ** If this is a crash simulation, set pFinal to an arbitrarily selected ** element of the write-list. */ WriteBuffer *pFinal = 0; if( !isCrash ){ for(pWrite=g.pWriteList; pWrite; pWrite=pWrite->pNext){ if( pWrite->pFile==pFile ){ pFinal = pWrite; } } }else if( iDc&(SQLITE_IOCAP_SEQUENTIAL|SQLITE_IOCAP_SAFE_APPEND) ){ int nWrite = 0; int iFinal; for(pWrite=g.pWriteList; pWrite; pWrite=pWrite->pNext) nWrite++; sqlite3Randomness(sizeof(int), &iFinal); iFinal = ((iFinal<0)?-1*iFinal:iFinal)%nWrite; for(pWrite=g.pWriteList; iFinal>0; pWrite=pWrite->pNext) iFinal--; pFinal = pWrite; } #ifdef TRACE_CRASHTEST printf("Sync %s (is %s crash)\n", pFile->zName, (isCrash?"a":"not a")); #endif ppPtr = &g.pWriteList; for(pWrite=*ppPtr; rc==SQLITE_OK && pWrite; pWrite=*ppPtr){ sqlite3_file *pRealFile = pWrite->pFile->pRealFile; /* (eAction==1) -> write block out normally, ** (eAction==2) -> do nothing, ** (eAction==3) -> trash sectors. */ int eAction = 0; if( !isCrash ){ eAction = 2; if( (pWrite->pFile==pFile || iDc&SQLITE_IOCAP_SEQUENTIAL) ){ eAction = 1; } }else{ char random; sqlite3Randomness(1, &random); /* Do not select option 3 (sector trashing) if the IOCAP_ATOMIC flag ** is set or this is an OsTruncate(), not an Oswrite(). */ if( (iDc&SQLITE_IOCAP_ATOMIC) || (pWrite->zBuf==0) ){ random &= 0x01; } /* If IOCAP_SEQUENTIAL is set and this is not the final entry ** in the truncated write-list, always select option 1 (write ** out correctly). */ if( (iDc&SQLITE_IOCAP_SEQUENTIAL && pWrite!=pFinal) ){ random = 0; } /* If IOCAP_SAFE_APPEND is set and this OsWrite() operation is ** an append (first byte of the written region is 1 byte past the ** current EOF), always select option 1 (write out correctly). */ if( iDc&SQLITE_IOCAP_SAFE_APPEND && pWrite->zBuf ){ i64 iSize; sqlite3OsFileSize(pRealFile, &iSize); if( iSize==pWrite->iOffset ){ random = 0; } } if( (random&0x06)==0x06 ){ eAction = 3; }else{ eAction = ((random&0x01)?2:1); } } switch( eAction ){ case 1: { /* Write out correctly */ if( pWrite->zBuf ){ rc = sqlite3OsWrite( pRealFile, pWrite->zBuf, pWrite->nBuf, pWrite->iOffset ); }else{ rc = sqlite3OsTruncate(pRealFile, pWrite->iOffset); } *ppPtr = pWrite->pNext; #ifdef TRACE_CRASHTEST if( isCrash ){ printf("Writing %d bytes @ %d (%s)\n", pWrite->nBuf, (int)pWrite->iOffset, pWrite->pFile->zName ); } #endif sqlite3_free(pWrite); break; } case 2: { /* Do nothing */ ppPtr = &pWrite->pNext; #ifdef TRACE_CRASHTEST if( isCrash ){ printf("Omiting %d bytes @ %d (%s)\n", pWrite->nBuf, (int)pWrite->iOffset, pWrite->pFile->zName ); } #endif break; } case 3: { /* Trash sectors */ u8 *zGarbage; int iFirst = (pWrite->iOffset/g.iSectorSize); int iLast = (pWrite->iOffset+pWrite->nBuf-1)/g.iSectorSize; assert(pWrite->zBuf); #ifdef TRACE_CRASHTEST printf("Trashing %d sectors @ sector %d (%s)\n", 1+iLast-iFirst, iFirst, pWrite->pFile->zName ); #endif zGarbage = sqlite3_malloc(g.iSectorSize); if( zGarbage ){ sqlite3_int64 i; for(i=iFirst; rc==SQLITE_OK && i<=iLast; i++){ sqlite3Randomness(g.iSectorSize, zGarbage); rc = sqlite3OsWrite( pRealFile, zGarbage, g.iSectorSize, i*g.iSectorSize ); } sqlite3_free(zGarbage); }else{ rc = SQLITE_NOMEM; } ppPtr = &pWrite->pNext; break; } default: assert(!"Cannot happen"); } if( pWrite==pFinal ) break; } if( rc==SQLITE_OK && isCrash ){ exit(-1); } for(pWrite=g.pWriteList; pWrite && pWrite->pNext; pWrite=pWrite->pNext); g.pWriteListEnd = pWrite; return rc; }
/* ** Truncate an devsym-file. */ static int devsymTruncate(sqlite3_file *pFile, sqlite_int64 size){ devsym_file *p = (devsym_file *)pFile; return sqlite3OsTruncate(p->pReal, size); }
//Read/Write/Seek/Truncate test void Test2() { sqlite3_vfs* vfs = sqlite3_vfs_find(KSymbianVfsNameZ); TEST(vfs != NULL); sqlite3_file* osFile = (sqlite3_file*)User::Alloc(vfs->szOsFile); TEST(osFile != NULL); //Creating a new file int err = sqlite3OsDelete(vfs, KTestFile1Z, 0); TEST2(err, SQLITE_OK); int res = 0; err = sqlite3OsAccess(vfs, KTestFile1Z, SQLITE_ACCESS_EXISTS, &res); TEST2(err, SQLITE_OK); TEST2(res, 0); err = sqlite3OsOpen(vfs, KTestFile1Z, osFile, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0); TEST2(err, SQLITE_OK); //Writing at the beginning of the file err = sqlite3OsWrite(osFile, "123456", 6, 0); TEST2(err, SQLITE_OK); //Verify the written data char data[20]; err = sqlite3OsRead(osFile, data, 6, 0); TEST2(err, SQLITE_OK); err = memcmp(data, "123456", 6); TEST2(err, 0); //Writing at beyond the end of the file err = sqlite3OsWrite(osFile, "abcdefgh", 8, 100); TEST2(err, SQLITE_OK); //Verify the written data err = sqlite3OsRead(osFile, data, 8, 100); TEST2(err, SQLITE_OK); err = memcmp(data, "abcdefgh", 8); TEST2(err, 0); //Truncate the file err = sqlite3OsTruncate(osFile, 3); TEST2(err, SQLITE_OK); //Write more data err = sqlite3OsWrite(osFile, "xyz", 3, 3); TEST2(err, SQLITE_OK); //Verify the written data err = sqlite3OsRead(osFile, data, 6, 0); TEST2(err, SQLITE_OK); err = memcmp(data, "123xyz", 6); TEST2(err, 0); //Check the file size TInt64 fileSize = 0; err = sqlite3OsFileSize(osFile, &fileSize); TEST2(err, SQLITE_OK); TEST(fileSize == 6); //FileControl - lock type int lockType = -1; err = osFile->pMethods->xFileControl(osFile, SQLITE_FCNTL_LOCKSTATE, &lockType); TEST2(err, SQLITE_OK); TEST2(lockType, NO_LOCK); //FileControl - set callback - NULL callback err = osFile->pMethods->xFileControl(osFile, KSqlFcntlRegisterFreePageCallback, 0); TEST2(err, SQLITE_ERROR); //FileControl - set callback - invalid callback object TSqlFreePageCallback cbck; err = osFile->pMethods->xFileControl(osFile, KSqlFcntlRegisterFreePageCallback, &cbck); TEST2(err, SQLITE_ERROR); //FileControl - invalid op-code err = osFile->pMethods->xFileControl(osFile, 90234, 0); TEST2(err, SQLITE_ERROR); //Close the file err = sqlite3OsClose(osFile); TEST2(err, SQLITE_OK); // err = sqlite3OsDelete(vfs, KTestFile1Z, 0); TEST2(err, SQLITE_OK); User::Free(osFile); }
/* ** This procedure runs in a separate thread, reading messages off of the ** write queue and processing them one by one. ** ** If async.writerHaltNow is true, then this procedure exits ** after processing a single message. ** ** If async.writerHaltWhenIdle is true, then this procedure exits when ** the write queue is empty. ** ** If both of the above variables are false, this procedure runs ** indefinately, waiting for operations to be added to the write queue ** and processing them in the order in which they arrive. ** ** An artifical delay of async.ioDelay milliseconds is inserted before ** each write operation in order to simulate the effect of a slow disk. ** ** Only one instance of this procedure may be running at a time. */ static void *asyncWriterThread(void *NotUsed){ AsyncWrite *p = 0; int rc = SQLITE_OK; int holdingMutex = 0; if( pthread_mutex_trylock(&async.writerMutex) ){ return 0; } while( async.writerHaltNow==0 ){ OsFile *pBase = 0; if( !holdingMutex ){ pthread_mutex_lock(&async.queueMutex); } while( (p = async.pQueueFirst)==0 ){ pthread_cond_broadcast(&async.emptySignal); if( async.writerHaltWhenIdle ){ pthread_mutex_unlock(&async.queueMutex); break; }else{ ASYNC_TRACE(("IDLE\n")); pthread_cond_wait(&async.queueSignal, &async.queueMutex); ASYNC_TRACE(("WAKEUP\n")); } } if( p==0 ) break; holdingMutex = 1; /* Right now this thread is holding the mutex on the write-op queue. ** Variable 'p' points to the first entry in the write-op queue. In ** the general case, we hold on to the mutex for the entire body of ** the loop. ** ** However in the cases enumerated below, we relinquish the mutex, ** perform the IO, and then re-request the mutex before removing 'p' from ** the head of the write-op queue. The idea is to increase concurrency with ** sqlite threads. ** ** * An ASYNC_CLOSE operation. ** * An ASYNC_OPENEXCLUSIVE operation. For this one, we relinquish ** the mutex, call the underlying xOpenExclusive() function, then ** re-aquire the mutex before seting the AsyncFile.pBaseRead ** variable. ** * ASYNC_SYNC and ASYNC_WRITE operations, if ** SQLITE_ASYNC_TWO_FILEHANDLES was set at compile time and two ** file-handles are open for the particular file being "synced". */ if( async.ioError!=SQLITE_OK && p->op!=ASYNC_CLOSE ){ p->op = ASYNC_NOOP; } if( p->pFile ){ pBase = p->pFile->pBaseWrite; if( p->op==ASYNC_CLOSE || p->op==ASYNC_OPENEXCLUSIVE || (pBase && (p->op==ASYNC_SYNC || p->op==ASYNC_WRITE) ) ){ pthread_mutex_unlock(&async.queueMutex); holdingMutex = 0; } if( !pBase ){ pBase = p->pFile->pBaseRead; } } switch( p->op ){ case ASYNC_NOOP: break; case ASYNC_WRITE: assert( pBase ); ASYNC_TRACE(("WRITE %s %d bytes at %d\n", p->pFile->zName, p->nByte, p->iOffset)); rc = sqlite3OsSeek(pBase, p->iOffset); if( rc==SQLITE_OK ){ rc = sqlite3OsWrite(pBase, (const void *)(p->zBuf), p->nByte); } break; case ASYNC_SYNC: assert( pBase ); ASYNC_TRACE(("SYNC %s\n", p->pFile->zName)); rc = sqlite3OsSync(pBase, p->nByte); break; case ASYNC_TRUNCATE: assert( pBase ); ASYNC_TRACE(("TRUNCATE %s to %d bytes\n", p->pFile->zName, p->iOffset)); rc = sqlite3OsTruncate(pBase, p->iOffset); break; case ASYNC_CLOSE: ASYNC_TRACE(("CLOSE %s\n", p->pFile->zName)); sqlite3OsClose(&p->pFile->pBaseWrite); sqlite3OsClose(&p->pFile->pBaseRead); sqlite3OsFree(p->pFile); break; case ASYNC_OPENDIRECTORY: assert( pBase ); ASYNC_TRACE(("OPENDIR %s\n", p->zBuf)); sqlite3OsOpenDirectory(pBase, p->zBuf); break; case ASYNC_SETFULLSYNC: assert( pBase ); ASYNC_TRACE(("SETFULLSYNC %s %d\n", p->pFile->zName, p->nByte)); sqlite3OsSetFullSync(pBase, p->nByte); break; case ASYNC_DELETE: ASYNC_TRACE(("DELETE %s\n", p->zBuf)); rc = xOrigDelete(p->zBuf); break; case ASYNC_SYNCDIRECTORY: ASYNC_TRACE(("SYNCDIR %s\n", p->zBuf)); rc = xOrigSyncDirectory(p->zBuf); break; case ASYNC_OPENEXCLUSIVE: { AsyncFile *pFile = p->pFile; int delFlag = ((p->iOffset)?1:0); OsFile *pBase = 0; ASYNC_TRACE(("OPEN %s delFlag=%d\n", p->zBuf, delFlag)); assert(pFile->pBaseRead==0 && pFile->pBaseWrite==0); rc = xOrigOpenExclusive(p->zBuf, &pBase, delFlag); assert( holdingMutex==0 ); pthread_mutex_lock(&async.queueMutex); holdingMutex = 1; if( rc==SQLITE_OK ){ pFile->pBaseRead = pBase; } break; } default: assert(!"Illegal value for AsyncWrite.op"); } /* If we didn't hang on to the mutex during the IO op, obtain it now ** so that the AsyncWrite structure can be safely removed from the ** global write-op queue. */ if( !holdingMutex ){ pthread_mutex_lock(&async.queueMutex); holdingMutex = 1; } /* ASYNC_TRACE(("UNLINK %p\n", p)); */ if( p==async.pQueueLast ){ async.pQueueLast = 0; } async.pQueueFirst = p->pNext; sqlite3OsFree(p); assert( holdingMutex ); /* An IO error has occured. We cannot report the error back to the ** connection that requested the I/O since the error happened ** asynchronously. The connection has already moved on. There ** really is nobody to report the error to. ** ** The file for which the error occured may have been a database or ** journal file. Regardless, none of the currently queued operations ** associated with the same database should now be performed. Nor should ** any subsequently requested IO on either a database or journal file ** handle for the same database be accepted until the main database ** file handle has been closed and reopened. ** ** Furthermore, no further IO should be queued or performed on any file ** handle associated with a database that may have been part of a ** multi-file transaction that included the database associated with ** the IO error (i.e. a database ATTACHed to the same handle at some ** point in time). */ if( rc!=SQLITE_OK ){ async.ioError = rc; } /* Drop the queue mutex before continuing to the next write operation ** in order to give other threads a chance to work with the write queue. */ if( !async.pQueueFirst || !async.ioError ){ sqlite3ApiExit(0, 0); pthread_mutex_unlock(&async.queueMutex); holdingMutex = 0; if( async.ioDelay>0 ){ sqlite3OsSleep(async.ioDelay); }else{ sched_yield(); } } } pthread_mutex_unlock(&async.writerMutex); return 0; }