// // The developer of the original code and/or files is Tripwire, Inc. // Portions created by Tripwire, Inc. are copyright (C) 2000 Tripwire, // Inc. Tripwire is a registered trademark of Tripwire, Inc. All rights // reserved. // // This program is free software. The contents of this file are subject // to the terms of the GNU General Public License as published by the // Free Software Foundation; either version 2 of the License, or (at your // option) any later version. You may redistribute it and/or modify it // only in compliance with the GNU General Public License. // // This program is distributed in the hope that it will be useful. // However, this program is distributed AS-IS WITHOUT ANY // WARRANTY; INCLUDING THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS // FOR A PARTICULAR PURPOSE. Please see the GNU General Public License // for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, // USA. // // Nothing in the GNU General Public License or any other license to use // the code or files shall permit you to use Tripwire's trademarks, // service marks, or other intellectual property without Tripwire's // prior written consent. // // If you have any questions, please contact Tripwire, Inc. at either // info@tripwire.org or www.tripwire.org. // /////////////////////////////////////////////////////////////////////////////// // blockfile.cpp #include "stddb.h" #ifndef __TCHAR_H #error #endif #include "blockfile.h" #include "core/archive.h" #include "core/debug.h" #include "core/archive.h" #include //----------------------------------------------------------------------------- // cBlockFile //----------------------------------------------------------------------------- /////////////////////////////////////////////////////////////////////////////// // ctor /////////////////////////////////////////////////////////////////////////////// cBlockFile::cBlockFile() : mNumPages (-1), mNumBlocks (-1), mTimer (0), mpArchive (0) { #ifdef _BLOCKFILE_DEBUG mNumBlockWrite = 0; mNumBlockRead = 0; mNumPageFault = 0; mNumPageRequests = 0; #endif //_BLOCKFILE_DEBUG } /////////////////////////////////////////////////////////////////////////////// // dtor /////////////////////////////////////////////////////////////////////////////// cBlockFile::~cBlockFile() { Close(); } /////////////////////////////////////////////////////////////////////////////// // Open /////////////////////////////////////////////////////////////////////////////// void cBlockFile::Open( const TSTRING& fileName, int numPages, bool bTruncate) //throw (eArchive) { ASSERT( numPages > 0 ); ASSERT( ! mpArchive ); cFileArchive* pArch = new cFileArchive; pArch->OpenReadWrite( fileName.c_str(), ( bTruncate ? cFileArchive::FA_OPEN_TRUNCATE : 0 ) ); Open( pArch, numPages ); } void cBlockFile::Open( cBidirArchive* pArch, int numPages ) //throw (eArchive) { ASSERT( numPages > 0 ); ASSERT( ! mpArchive ); // // keep my sanity... // pArch->Seek( 0, cBidirArchive::BEGINNING ); // // initialize the paged blocks list... // mpArchive = pArch; mNumPages = numPages; mvPagedBlocks.resize( mNumPages ); ASSERT( mvPagedBlocks.capacity() == mNumPages ); // make sure that we didn't alloc too many! // // if the file is newly created, set its size to that of a single block // if( mpArchive->Length() == 0 ) { mpArchive->WriteBlob( mvPagedBlocks[0].GetData(), GetBlockSize() ); } // // make sure that the file is an appropriate length // TODO -- what is the correct thing to do if this fails? right now, I assert, but // perhaps I should throw an exception or increase the file size to the next block interval ASSERT( mpArchive->Length() % GetBlockSize() == 0 ); mNumBlocks = mpArchive->Length() / GetBlockSize(); } /////////////////////////////////////////////////////////////////////////////// // Close /////////////////////////////////////////////////////////////////////////////// void cBlockFile::Close() { if( mpArchive ) { Flush(); delete mpArchive; mpArchive = 0; } } /////////////////////////////////////////////////////////////////////////////// // Flush /////////////////////////////////////////////////////////////////////////////// void cBlockFile::Flush() { ASSERT( mpArchive ); for( BlockVector::iterator i = mvPagedBlocks.begin(); i != mvPagedBlocks.end(); i++ ) { FlushBlock( &(*i) ); } } /////////////////////////////////////////////////////////////////////////////// // GetBlock /////////////////////////////////////////////////////////////////////////////// cBlockFile::Block* cBlockFile::GetBlock( int blockNum ) //throw (eArchive) { #ifdef _BLOCKFILE_DEBUG mNumPageRequests++; AssertValid(); #endif // // debug stuff // cDebug d( "cBlockFile::GetBlock" ); d.TraceNever( "Request for block %d\n", blockNum ); //TODO -- I should really throw an exception here... ASSERT( (blockNum >= 0) && (blockNum < GetNumBlocks()) ); ASSERT( mpArchive ); // // first, increment the timer; if it flips, reset everybody's access times to 0. // mTimer++; if( mTimer == 0 ) { for( BlockVector::iterator i = mvPagedBlocks.begin(); i != mvPagedBlocks.end(); i++ ) { i->SetTimestamp( 0 ); } } // // now, see if the desired block is in memory... // for( BlockVector::iterator i = mvPagedBlocks.begin(); i != mvPagedBlocks.end(); i++ ) { if( i->GetBlockNum() == blockNum ) { // this is easy; just return the block. i->SetTimestamp( mTimer ); d.TraceNever( "\tBlock %d was in memory.\n", blockNum ); return &(*i); } } // // ok, we are going to have to page it into memory; look for the page with the earliest timestamp to // remove... // #ifdef _BLOCKFILE_DEBUG mNumPageFault++; #endif d.TraceNever( "\tBlock %d was not in memory; paging it in\n", blockNum ); uint32 earliestTime = mvPagedBlocks[0].GetTimestamp(); BlockVector::iterator it = mvPagedBlocks.begin(); BlockVector::iterator earliestIter = it; it++; // since we don't want to check the first one ASSERT(it != mvPagedBlocks.end()); for( ; it != mvPagedBlocks.end(); it++ ) { if( it->GetTimestamp() < earliestTime ) { earliestIter = it; earliestTime = it->GetTimestamp(); } } ASSERT( (earliestIter >= mvPagedBlocks.begin()) && (earliestIter < mvPagedBlocks.end()) ); // // flush the page data to disk if it is dirty... // FlushBlock( &(*earliestIter) ); d.TraceNever( "\tPaging out block %d\n", earliestIter->GetBlockNum() ); // // ok, now we can read in the new page... // earliestIter->SetBlockNum ( blockNum ); earliestIter->Read ( *mpArchive ); earliestIter->SetTimestamp ( mTimer ); // // this variable keeps track of how many block reads we do. #ifdef _BLOCKFILE_DEBUG mNumBlockRead++; AssertValid(); #endif return &(*earliestIter); } /////////////////////////////////////////////////////////////////////////////// // CreateBlock /////////////////////////////////////////////////////////////////////////////// cBlockFile::Block* cBlockFile::CreateBlock() { // debug stuff // ASSERT( mpArchive ); cDebug d( "cBlockFile::CreateBlock()" ); d.TraceDetail( "Creating new block (number %d)\n", GetNumBlocks() ); char emptyBlock[BLOCK_SIZE]; memset( emptyBlock, 0, BLOCK_SIZE ); // // write empty data in the new block's location // //ASSERT(GetNumBlocks() * GetBlockSize() <= mpArchive->Length()); d.TraceDetail("Seeking to %d * %d = %d\n", GetNumBlocks(), GetBlockSize(), GetNumBlocks() * GetBlockSize()); mpArchive->Seek ( GetNumBlocks() * GetBlockSize(), cBidirArchive::BEGINNING ); mpArchive->WriteBlob ( emptyBlock, BLOCK_SIZE ); // // now, page it in... // mNumBlocks++; return GetBlock( GetNumBlocks() - 1 ); } /////////////////////////////////////////////////////////////////////////////// // DestroyLastBlock /////////////////////////////////////////////////////////////////////////////// void cBlockFile::DestroyLastBlock( ) { //TODO -- implement this!!! ASSERT( false ); /* mNumBlocks--; mpArchive->Seek( mNumBlocks * BLOCK_SIZE, cBidirArchive::BEGINNING ); mpArchive->Truncate(); AssertValid(); */ } //----------------------------------------------------------------------------- // Debug methods... //----------------------------------------------------------------------------- #ifdef _BLOCKFILE_DEBUG /////////////////////////////////////////////////////////////////////////////// // TraceContents /////////////////////////////////////////////////////////////////////////////// void cBlockFile::TraceContents(int dl) const { cDebug d( "cBlockFile::TraceContents" ); if( dl == -1 ) dl = cDebug::D_DEBUG; d.Trace( dl, "Block size: %d\n", GetBlockSize() ); d.Trace( dl, "Number of blocks: %d\n", mNumBlocks ); d.Trace( dl, "Current timer: %d\n", mTimer ); // // trace out the profiling info... // d.Trace( dl, "Number of page writes: %d\n", mNumBlockWrite ); d.Trace( dl, "Number of page reads: %d\n", mNumBlockRead ); d.Trace( dl, "Number of page faults: %d\n", mNumPageFault ); d.Trace( dl, "Number of page requests:%d\n", mNumPageRequests ); d.Trace( dl, "Number of pages: %d\n", mNumPages ); d.Trace( dl, "-------------------------\n"); // // trace out all the block information... // int cnt = 0; for( BlockVector::const_iterator i = mvPagedBlocks.begin(); i != mvPagedBlocks.end(); i++, cnt++ ) { d.Trace( dl, "Page[%d] Block Num=%d Timestamp=%d Dirty=%s\n", cnt, i->GetBlockNum(), i->GetTimestamp(), i->IsDirty() ? "true" : "false"); } d.Trace( dl, "-------------------------\n"); } /////////////////////////////////////////////////////////////////////////////// // CreateBlock /////////////////////////////////////////////////////////////////////////////// void cBlockFile::AssertValid() const { if( ! mpArchive ) return; // // make sure the archive length and block count match up // #ifdef _DEBUG if (mpArchive->Length() != (GetBlockSize() * GetNumBlocks())) { cDebug d("cBlockFile::AssertValid"); d.Trace(cDebug::D_DEBUG, "FATAL ERROR!!!\n"); d.Trace(cDebug::D_DEBUG, "mpArchive->Length() = %d \n\t!= blocksize * numblocks = %d * %d\n", int(mpArchive->Length()), int(GetBlockSize()), int(GetNumBlocks())); d.Trace(cDebug::D_DEBUG, "now tracing the contents...\n"); TraceContents(cDebug::D_DEBUG); } #endif ASSERT( mpArchive->Length() == (GetBlockSize() * GetNumBlocks()) ); // // iterate through the pages... // for( BlockVector::const_iterator i = mvPagedBlocks.begin(); i != mvPagedBlocks.end(); i++) { // make sure the block number is valid // int blockNum = i->GetBlockNum(); ASSERT( (blockNum == cBlock::INVALID_NUM) || ( (blockNum >= 0) && (blockNum < GetNumBlocks()) ) ); // // make sure the time stamp is less than or equal to the timer time... // ASSERT( i->GetTimestamp() <= mTimer ); // // assert that the guard bytes haven't been modified i->AssertValid(); } } #endif //_BLOCKFILE_DEBUG