diff options
Diffstat (limited to 'sot/source/sdstor/stgdir.cxx')
-rw-r--r-- | sot/source/sdstor/stgdir.cxx | 1054 |
1 files changed, 1054 insertions, 0 deletions
diff --git a/sot/source/sdstor/stgdir.cxx b/sot/source/sdstor/stgdir.cxx new file mode 100644 index 000000000000..f093dc60cbe7 --- /dev/null +++ b/sot/source/sdstor/stgdir.cxx @@ -0,0 +1,1054 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sot.hxx" + +#include <string.h> // memcpy() + +#include "stg.hxx" +#include "stgelem.hxx" +#include "stgcache.hxx" +#include "stgstrms.hxx" +#include "stgdir.hxx" +#include "stgio.hxx" + + +//////////////////////////// class StgDirEntry ///////////////////////////// + +// This class holds the dir entry data and maintains dirty flags for both +// the entry and the data. + +// Transacted mode for streams: On the first write, a temp stream pTmpStrm +// is created and operated on. A commit moves pTmpStrm to pCurStrm, which +// is used for subsequent reads. A new write creates a new copy of pTmpStrm +// based on pCurStrm. Reverting throws away pTmpStrm. +// Transacted mode for storages: A copy of the dir ents is kept in aSave. +// Committing means copying aEntry to aSave. Reverting means to copy aSave +// to aEntry, delete newly created entries and to reactivate removed entries. + +// Problem der Implementation: Keine Hierarchischen commits. Daher nur +// insgesamt transaktionsorientert oder direkt. + +StgDirEntry::StgDirEntry( const void* pFrom, BOOL * pbOk ) : StgAvlNode() +{ + *pbOk = aEntry.Load( pFrom ); + + InitMembers(); +} + +StgDirEntry::StgDirEntry( const StgEntry& r ) : StgAvlNode(), aEntry( r ) +{ + InitMembers(); +} + +// Helper for all ctors + +void StgDirEntry::InitMembers() +{ + aSave = aEntry; + pUp = + pDown = NULL; + ppRoot = NULL; + pStgStrm = NULL; + pCurStrm = + pTmpStrm = NULL; + nPos = + nEntry = + nRefCnt = 0; + nMode = STREAM_READ; + bDirect = TRUE; + bInvalid = + bCreated = + bRenamed = + bRemoved = + bTemp = + bDirty = + bZombie = FALSE; +} + +StgDirEntry::~StgDirEntry() +{ + Close(); + delete pCurStrm; + delete pStgStrm; + delete pDown; +} + +// Comparison function + +short StgDirEntry::Compare( const StgAvlNode* p ) const +{ + const StgDirEntry* pEntry = (const StgDirEntry*) p; + return aEntry.Compare( pEntry->aEntry ); +} + +// Enumerate the entry numbers. +// n is incremented to show the total # of entries. +// These number are later used as page numbers when storing +// the TOC tree into the TOC stream. Remember that aSave is +// stored, not aEntry. + +void StgDirEntry::Enum( INT32& n ) +{ + INT32 nLeft = STG_FREE, nRight = STG_FREE, nDown = STG_FREE; + nEntry = n++; + if( pLeft ) + { + ((StgDirEntry*) pLeft)->Enum( n ); nLeft = ((StgDirEntry*) pLeft)->nEntry; + } + if( pRight ) + { + ((StgDirEntry*) pRight)->Enum( n ); nRight = ((StgDirEntry*) pRight)->nEntry; + } + if( pDown ) + { + pDown->Enum( n ); nDown = pDown->nEntry; + } + aSave.SetLeaf( STG_LEFT, nLeft ); + aSave.SetLeaf( STG_RIGHT, nRight ); + aSave.SetLeaf( STG_CHILD, nDown ); +} + +// Delete all temporary entries before writing the TOC stream. +// Until now Deltem is never called with bForce True + +void StgDirEntry::DelTemp( BOOL bForce ) +{ + if( pLeft ) + ((StgDirEntry*) pLeft)->DelTemp( FALSE ); + if( pRight ) + ((StgDirEntry*) pRight)->DelTemp( FALSE ); + if( pDown ) + { + // If the storage is dead, of course all elements are dead, too + if( bInvalid && aEntry.GetType() == STG_STORAGE ) + bForce = TRUE; + pDown->DelTemp( bForce ); + } + if( ( bForce || bInvalid ) + && ( aEntry.GetType() != STG_ROOT ) /* && ( nRefCnt <= 1 ) */ ) + { + Close(); + if( pUp ) + { + // this deletes the element if refcnt == 0! + BOOL bDel = nRefCnt == 0; + StgAvlNode::Remove( (StgAvlNode**) &pUp->pDown, this, bDel ); + if( !bDel ) + { + pLeft = pRight = pDown = 0; + bInvalid = bZombie = TRUE; + } + } + } +} + +// Save the tree into the given dir stream + +BOOL StgDirEntry::Store( StgDirStrm& rStrm ) +{ + void* pEntry = rStrm.GetEntry( nEntry, TRUE ); + if( !pEntry ) + return FALSE; + // Do not store the current (maybe not commited) entry + aSave.Store( pEntry ); + if( pLeft ) + if( !((StgDirEntry*) pLeft)->Store( rStrm ) ) + return FALSE; + if( pRight ) + if( !((StgDirEntry*) pRight)->Store( rStrm ) ) + return FALSE; + if( pDown ) + if( !pDown->Store( rStrm ) ) + return FALSE; + return TRUE; +} + +BOOL StgDirEntry::StoreStream( StgIo& rIo ) +{ + if( aEntry.GetType() == STG_STREAM || aEntry.GetType() == STG_ROOT ) + { + if( bInvalid ) + { + // Delete the stream if needed + if( !pStgStrm ) + { + OpenStream( rIo ); + delete pStgStrm, pStgStrm = NULL; + } + else + pStgStrm->SetSize( 0 ); + } + // or write the data stream + else if( !Tmp2Strm() ) + return FALSE; + } + return TRUE; +} + +// Save all dirty streams + +BOOL StgDirEntry::StoreStreams( StgIo& rIo ) +{ + if( !StoreStream( rIo ) ) + return FALSE; + if( pLeft ) + if( !((StgDirEntry*) pLeft)->StoreStreams( rIo ) ) + return FALSE; + if( pRight ) + if( !((StgDirEntry*) pRight)->StoreStreams( rIo ) ) + return FALSE; + if( pDown ) + if( !pDown->StoreStreams( rIo ) ) + return FALSE; + return TRUE; +} + +// Revert all directory entries after failure to write the TOC stream + +void StgDirEntry::RevertAll() +{ + aEntry = aSave; + if( pLeft ) + ((StgDirEntry*) pLeft)->RevertAll(); + if( pRight ) + ((StgDirEntry*) pRight)->RevertAll(); + if( pDown ) + pDown->RevertAll(); +} + +// Look if any element of the tree is dirty + +BOOL StgDirEntry::IsDirty() +{ + if( bDirty || bInvalid ) + return TRUE; + if( pLeft && ((StgDirEntry*) pLeft)->IsDirty() ) + return TRUE; + if( pRight && ((StgDirEntry*) pRight)->IsDirty() ) + return TRUE; + if( pDown && pDown->IsDirty() ) + return TRUE; + return FALSE; +} + +// Set up a stream. + +void StgDirEntry::OpenStream( StgIo& rIo, BOOL bForceBig ) +{ + INT32 nThreshold = (USHORT) rIo.aHdr.GetThreshold(); + delete pStgStrm; + if( !bForceBig && aEntry.GetSize() < nThreshold ) + pStgStrm = new StgSmallStrm( rIo, this ); + else + pStgStrm = new StgDataStrm( rIo, this ); + if( bInvalid && aEntry.GetSize() ) + { + // This entry has invalid data, so delete that data + SetSize( 0L ); +// bRemoved = bInvalid = FALSE; + } + nPos = 0; +} + +// Close the open stream without committing. If the entry is marked as +// temporary, delete it. +// Do not delete pCurStrm here! +// (TLX:??? Zumindest pStgStrm muss deleted werden.) + +void StgDirEntry::Close() +{ + delete pTmpStrm; + pTmpStrm = NULL; +// nRefCnt = 0; + bInvalid = bTemp; +} + +// Get the current stream size + +INT32 StgDirEntry::GetSize() +{ + INT32 n; + if( pTmpStrm ) + n = pTmpStrm->GetSize(); + else if( pCurStrm ) + n = pCurStrm->GetSize(); + else n = aEntry.GetSize(); + return n; +} + +// Set the stream size. This means also creating a temp stream. + +BOOL StgDirEntry::SetSize( INT32 nNewSize ) +{ + if ( + !( nMode & STREAM_WRITE ) || + (!bDirect && !pTmpStrm && !Strm2Tmp()) + ) + { + return FALSE; + } + + if( nNewSize < nPos ) + nPos = nNewSize; + if( pTmpStrm ) + { + pTmpStrm->SetSize( nNewSize ); + pStgStrm->GetIo().SetError( pTmpStrm->GetError() ); + return BOOL( pTmpStrm->GetError() == SVSTREAM_OK ); + } + else + { + BOOL bRes = FALSE; + StgIo& rIo = pStgStrm->GetIo(); + INT32 nThreshold = rIo.aHdr.GetThreshold(); + // ensure the correct storage stream! + StgStrm* pOld = NULL; + USHORT nOldSize = 0; + if( nNewSize >= nThreshold && pStgStrm->IsSmallStrm() ) + { + pOld = pStgStrm; + nOldSize = (USHORT) pOld->GetSize(); + pStgStrm = new StgDataStrm( rIo, STG_EOF, 0 ); + } + else if( nNewSize < nThreshold && !pStgStrm->IsSmallStrm() ) + { + pOld = pStgStrm; + nOldSize = (USHORT) nNewSize; + pStgStrm = new StgSmallStrm( rIo, STG_EOF, 0 ); + } + // now set the new size + if( pStgStrm->SetSize( nNewSize ) ) + { + // did we create a new stream? + if( pOld ) + { + // if so, we probably need to copy the old data + if( nOldSize ) + { + void* pBuf = new BYTE[ nOldSize ]; + pOld->Pos2Page( 0L ); + pStgStrm->Pos2Page( 0L ); + if( pOld->Read( pBuf, nOldSize ) + && pStgStrm->Write( pBuf, nOldSize ) ) + bRes = TRUE; + delete[] static_cast<BYTE*>(pBuf); + } + else + bRes = TRUE; + if( bRes ) + { + pOld->SetSize( 0 ); + delete pOld; + pStgStrm->Pos2Page( nPos ); + pStgStrm->SetEntry( *this ); + } + else + { + pStgStrm->SetSize( 0 ); + delete pStgStrm; + pStgStrm = pOld; + } + } + else + { + pStgStrm->Pos2Page( nPos ); + bRes = TRUE; + } + } + return bRes; + } +} + +// Seek. On negative values, seek to EOF. + +INT32 StgDirEntry::Seek( INT32 nNew ) +{ + if( pTmpStrm ) + { + if( nNew < 0 ) + nNew = pTmpStrm->GetSize(); + nNew = pTmpStrm->Seek( nNew ); + } + else if( pCurStrm ) + { + if( nNew < 0 ) + nNew = pCurStrm->GetSize(); + nNew = pCurStrm->Seek( nNew ); + } + else + { + INT32 nSize = aEntry.GetSize(); + + if( nNew < 0 ) + nNew = nSize; + + // try to enlarge, the readonly streams should not allow this + if( nNew > nSize ) + { + if ( !( nMode & STREAM_WRITE ) || !SetSize( nNew ) ) + { + OSL_ENSURE( nMode & STREAM_WRITE, "Trying to resize readonly stream by seeking, could be a wrong offset!" ); + return nPos; + } + else + return Seek( nNew ); + } + pStgStrm->Pos2Page( nNew ); + nNew = pStgStrm->GetPos(); + } + return nPos = nNew; +} + +// Read + +INT32 StgDirEntry::Read( void* p, INT32 nLen ) +{ + if( nLen <= 0 ) + return 0; + if( pTmpStrm ) + nLen = pTmpStrm->Read( p, nLen ); + else if( pCurStrm ) + nLen = pCurStrm->Read( p, nLen ); + else + nLen = pStgStrm->Read( p, nLen ); + nPos += nLen; + return nLen; +} + +// Write + +INT32 StgDirEntry::Write( const void* p, INT32 nLen ) +{ + if( nLen <= 0 || !( nMode & STREAM_WRITE ) ) + return 0; + + // Was this stream committed internally and reopened in direct mode? + if( bDirect && ( pCurStrm || pTmpStrm ) && !Tmp2Strm() ) + return 0; + // Is this stream opened in transacted mode? Do we have to make a copy? + if( !bDirect && !pTmpStrm && !Strm2Tmp() ) + return 0; + if( pTmpStrm ) + { + nLen = pTmpStrm->Write( p, nLen ); + pStgStrm->GetIo().SetError( pTmpStrm->GetError() ); + } + else + { + INT32 nNew = nPos + nLen; + if( nNew > pStgStrm->GetSize() ) + { + if( !SetSize( nNew ) ) + return 0L; + pStgStrm->Pos2Page( nPos ); + } + nLen = pStgStrm->Write( p, nLen ); + } + nPos += nLen; + return nLen; +} + +// Copy the data of one entry into another entry. + +void StgDirEntry::Copy( StgDirEntry& rDest ) +{ + INT32 n = GetSize(); + if( rDest.SetSize( n ) && n ) + { + BYTE aTempBytes[ 4096 ]; + void* p = static_cast<void*>( aTempBytes ); + Seek( 0L ); + rDest.Seek( 0L ); + while( n ) + { + INT32 nn = n; + if( nn > 4096 ) + nn = 4096; + if( Read( p, nn ) != nn ) + break; + if( rDest.Write( p, nn ) != nn ) + break; + n -= nn; + } + } +} + +void StgDirEntry::Copy( BaseStorageStream& rDest ) +{ + INT32 n = GetSize(); + if( rDest.SetSize( n ) && n ) + { + ULONG Pos = rDest.Tell(); + BYTE aTempBytes[ 4096 ]; + void* p = static_cast<void*>( aTempBytes ); + Seek( 0L ); + rDest.Seek( 0L ); + while( n ) + { + INT32 nn = n; + if( nn > 4096 ) + nn = 4096; + if( Read( p, nn ) != nn ) + break; + if( sal::static_int_cast<INT32>(rDest.Write( p, nn )) != nn ) + break; + n -= nn; + } + rDest.Seek( Pos ); // ?! Seems to be undocumented ! + } +} + +// Commit this entry + +BOOL StgDirEntry::Commit() +{ + // OSL_ENSURE( nMode & STREAM_WRITE, "Trying to commit readonly stream!" ); + + aSave = aEntry; + BOOL bRes = TRUE; + if( aEntry.GetType() == STG_STREAM ) + { + if( pTmpStrm ) + delete pCurStrm, pCurStrm = pTmpStrm, pTmpStrm = NULL; + if( bRemoved ) + // Delete the stream if needed + if( pStgStrm ) + pStgStrm->SetSize( 0 ); + } + else if( aEntry.GetType() == STG_STORAGE && bDirect && bRes ) + { + StgIterator aIter( *this ); + for( StgDirEntry* p = aIter.First(); p && bRes; p = aIter.Next() ) + bRes = p->Commit(); + } + return bRes; +} + +// Revert the entry + +BOOL StgDirEntry::Revert() +{ + aEntry = aSave; + switch( aEntry.GetType() ) + { + case STG_STREAM: + if( pCurStrm ) + delete pTmpStrm, pTmpStrm = pCurStrm, pCurStrm = NULL; + break; + case STG_STORAGE: + { + BOOL bSomeRenamed = FALSE; + StgIterator aOIter( *this ); + StgDirEntry* op = aOIter.First(); + while( op ) + { + op->aEntry = op->aSave; + op->bDirty = FALSE; + bSomeRenamed = BOOL( bSomeRenamed | op->bRenamed ); + // Remove any new entries + if( op->bCreated ) + { + op->bCreated = FALSE; + op->Close(); + op->bInvalid = TRUE; + } + // Reactivate any removed entries + else if( op->bRemoved ) + op->bRemoved = op->bInvalid = op->bTemp = FALSE; + op = aOIter.Next(); + } + // Resort all renamed entries + if( bSomeRenamed ) + { + StgIterator aIter( *this ); + StgDirEntry* p = aIter.First(); + while( p ) + { + if( p->bRenamed ) + { + StgAvlNode::Move + ( (StgAvlNode**) &p->pUp->pDown, + (StgAvlNode**) &p->pUp->pDown, p ); + p->bRenamed = FALSE; + } + p = aIter.Next(); + } + } + DelTemp( FALSE ); + break; + } + case STG_EMPTY: + case STG_LOCKBYTES: + case STG_PROPERTY: + case STG_ROOT: + break; + } + return TRUE; +} + +// Copy the stg stream to the temp stream + +BOOL StgDirEntry::Strm2Tmp() +{ + if( !pTmpStrm ) + { + ULONG n = 0; + if( pCurStrm ) + { + // It was already commited once + pTmpStrm = new StgTmpStrm; + if( pTmpStrm->GetError() == SVSTREAM_OK && pTmpStrm->Copy( *pCurStrm ) ) + return TRUE; + n = 1; // indicates error + } + else + { + n = aEntry.GetSize(); + pTmpStrm = new StgTmpStrm( n ); + if( pTmpStrm->GetError() == SVSTREAM_OK ) + { + if( n ) + { + BYTE aTempBytes[ 4096 ]; + void* p = static_cast<void*>( aTempBytes ); + pStgStrm->Pos2Page( 0L ); + while( n ) + { + ULONG nn = n; + if( nn > 4096 ) + nn = 4096; + if( (ULONG) pStgStrm->Read( p, nn ) != nn ) + break; + if( pTmpStrm->Write( p, nn ) != nn ) + break; + n -= nn; + } + pStgStrm->Pos2Page( nPos ); + pTmpStrm->Seek( nPos ); + } + } + else + n = 1; + } + if( n ) + { + pStgStrm->GetIo().SetError( pTmpStrm->GetError() ); + delete pTmpStrm; + pTmpStrm = NULL; + return FALSE; + } + } + return TRUE; +} + +// Copy the temp stream to the stg stream during the final commit + +BOOL StgDirEntry::Tmp2Strm() +{ + // We did commit once, but have not written since then + if( !pTmpStrm ) + pTmpStrm = pCurStrm, pCurStrm = NULL; + if( pTmpStrm ) + { + ULONG n = pTmpStrm->GetSize(); + StgStrm* pNewStrm; + StgIo& rIo = pStgStrm->GetIo(); + ULONG nThreshold = (ULONG) rIo.aHdr.GetThreshold(); + if( n < nThreshold ) + pNewStrm = new StgSmallStrm( rIo, STG_EOF, 0 ); + else + pNewStrm = new StgDataStrm( rIo, STG_EOF, 0 ); + if( pNewStrm->SetSize( n ) ) + { + BYTE p[ 4096 ]; + pTmpStrm->Seek( 0L ); + while( n ) + { + ULONG nn = n; + if( nn > 4096 ) + nn = 4096; + if( pTmpStrm->Read( p, nn ) != nn ) + break; + if( (ULONG) pNewStrm->Write( p, nn ) != nn ) + break; + n -= nn; + } + if( n ) + { + pTmpStrm->Seek( nPos ); + pStgStrm->GetIo().SetError( pTmpStrm->GetError() ); + delete pNewStrm; + return FALSE; + } + else + { + pStgStrm->SetSize( 0L ); + delete pStgStrm; + pStgStrm = pNewStrm; + pNewStrm->SetEntry( *this ); + pNewStrm->Pos2Page( nPos ); + delete pTmpStrm; + delete pCurStrm; + pTmpStrm = pCurStrm = NULL; + aSave = aEntry; + } + } + } + return TRUE; +} + +// Check if the given entry is contained in this entry + +BOOL StgDirEntry::IsContained( StgDirEntry* pStg ) +{ + if( aEntry.GetType() == STG_STORAGE ) + { + StgIterator aIter( *this ); + StgDirEntry* p = aIter.First(); + while( p ) + { + if( !p->aEntry.Compare( pStg->aEntry ) ) + return FALSE; + if( p->aEntry.GetType() == STG_STORAGE ) + if( !p->IsContained( pStg ) ) + return FALSE; + p = aIter.Next(); + } + } + return TRUE; +} + +// Invalidate all open entries by setting the RefCount to 0. If the bDel +// flag is set, also set the invalid flag to indicate deletion during the +// next dir stream flush. + +void StgDirEntry::Invalidate( BOOL bDel ) +{ +// nRefCnt = 0; + if( bDel ) + bRemoved = bInvalid = TRUE; + switch( aEntry.GetType() ) + { + case STG_STORAGE: + case STG_ROOT: + { + StgIterator aIter( *this ); + for( StgDirEntry* p = aIter.First(); p; p = aIter.Next() ) + p->Invalidate( bDel ); + break; + } + default: + break; + } +} + +///////////////////////////// class StgDirStrm //////////////////////////// + +// This specialized stream is the maintenance stream for the directory tree. + +StgDirStrm::StgDirStrm( StgIo& r ) + : StgDataStrm( r, r.aHdr.GetTOCStart(), -1 ) + , pRoot( NULL ) + , nEntries( 0 ) +{ + if( r.GetError() ) + return; + nEntries = nPageSize / STGENTRY_SIZE; + if( nStart == STG_EOF ) + { + StgEntry aRoot; + aRoot.Init(); + aRoot.SetName( String::CreateFromAscii( RTL_CONSTASCII_STRINGPARAM( "Root Entry" ) ) ); + aRoot.SetType( STG_ROOT ); + pRoot = new StgDirEntry( aRoot ); + pRoot->SetDirty(); + } + else + { + // temporarily use this instance as owner, so + // the TOC pages can be removed. + pEntry = (StgDirEntry*) this; // just for a bit pattern + SetupEntry( 0, pRoot ); + rIo.Revert( pEntry ); + pEntry = NULL; + } +} + +StgDirStrm::~StgDirStrm() +{ + delete pRoot; +} + +// Recursively parse the directory tree during reading the TOC stream + +void StgDirStrm::SetupEntry( INT32 n, StgDirEntry* pUpper ) +{ + void* p = ( n == STG_FREE ) ? NULL : GetEntry( n ); + if( p ) + { + BOOL bOk(FALSE); + StgDirEntry* pCur = new StgDirEntry( p, &bOk ); + + if( !bOk ) + { + delete pCur; + rIo.SetError( SVSTREAM_GENERALERROR ); + // an error occured + return; + } + + // better it is + if( !pUpper ) + pCur->aEntry.SetType( STG_ROOT ); + + INT32 nLeft = pCur->aEntry.GetLeaf( STG_LEFT ); + INT32 nRight = pCur->aEntry.GetLeaf( STG_RIGHT ); + // substorage? + INT32 nLeaf = STG_FREE; + if( pCur->aEntry.GetType() == STG_STORAGE || pCur->aEntry.GetType() == STG_ROOT ) + { + nLeaf = pCur->aEntry.GetLeaf( STG_CHILD ); + if (nLeaf != STG_FREE && nLeaf == n) + { + delete pCur; + rIo.SetError( SVSTREAM_GENERALERROR ); + return; + } + } + + if( nLeaf != 0 && nLeft != 0 && nRight != 0 ) + { + if( StgAvlNode::Insert + ( (StgAvlNode**) ( pUpper ? &pUpper->pDown : &pRoot ), pCur ) ) + { + pCur->pUp = pUpper; + pCur->ppRoot = &pRoot; + } + else + { + rIo.SetError( SVSTREAM_CANNOT_MAKE ); + delete pCur; pCur = NULL; + return; + } + SetupEntry( nLeft, pUpper ); + SetupEntry( nRight, pUpper ); + SetupEntry( nLeaf, pCur ); + } + } +} + +// Extend or shrink the directory stream. + +BOOL StgDirStrm::SetSize( INT32 nBytes ) +{ + // Always allocate full pages + nBytes = ( ( nBytes + nPageSize - 1 ) / nPageSize ) * nPageSize; + return StgStrm::SetSize( nBytes ); +} + +// Save the TOC stream into a new substream after saving all data streams + +BOOL StgDirStrm::Store() +{ + if( !pRoot->IsDirty() ) + return TRUE; + if( !pRoot->StoreStreams( rIo ) ) + return FALSE; + // After writing all streams, the data FAT stream has changed, + // so we have to commit the root again + pRoot->Commit(); + // We want a completely new stream, so fake an empty stream + INT32 nOldStart = nStart; // save for later deletion + INT32 nOldSize = nSize; + nStart = nPage = STG_EOF; + nSize = nPos = 0; + nOffset = 0; + // Delete all temporary entries + pRoot->DelTemp( FALSE ); + // set the entry numbers + INT32 n = 0; + pRoot->Enum( n ); + if( !SetSize( n * STGENTRY_SIZE ) ) + { + nStart = nOldStart; nSize = nOldSize; + pRoot->RevertAll(); + return FALSE; + } + // set up the cache elements for the new stream + if( !Copy( STG_FREE, nSize ) ) + { + pRoot->RevertAll(); + return FALSE; + } + // Write the data to the new stream + if( !pRoot->Store( *this ) ) + { + pRoot->RevertAll(); + return FALSE; + } + // fill any remaining entries with empty data + INT32 ne = nSize / STGENTRY_SIZE; + StgEntry aEmpty; + aEmpty.Init(); + while( n < ne ) + { + void* p = GetEntry( n++, TRUE ); + if( !p ) + { + pRoot->RevertAll(); + return FALSE; + } + aEmpty.Store( p ); + } + // Now we can release the old stream + pFat->FreePages( nOldStart, TRUE ); + rIo.aHdr.SetTOCStart( nStart ); + return TRUE; +} + +// Get a dir entry. + +void* StgDirStrm::GetEntry( INT32 n, BOOL bDirty ) +{ + n *= STGENTRY_SIZE; + if( n >= nSize ) + return NULL; + return GetPtr( n, TRUE, bDirty ); +} + +// Find a dir entry. + +StgDirEntry* StgDirStrm::Find( StgDirEntry& rStg, const String& rName ) +{ + if( rStg.pDown ) + { + StgEntry aEntry; + aEntry.Init(); + if( !aEntry.SetName( rName ) ) + { + rIo.SetError( SVSTREAM_GENERALERROR ); + return NULL; + } + // Look in the directory attached to the entry + StgDirEntry aTest( aEntry ); + return (StgDirEntry*) rStg.pDown->Find( &aTest ); + } + else + return NULL; +} + +// Create a new entry. + +StgDirEntry* StgDirStrm::Create + ( StgDirEntry& rStg, const String& rName, StgEntryType eType ) +{ + StgEntry aEntry; + aEntry.Init(); + aEntry.SetType( eType ); + if( !aEntry.SetName( rName ) ) + { + rIo.SetError( SVSTREAM_GENERALERROR ); + return NULL; + } + StgDirEntry* pRes = Find( rStg, rName ); + if( pRes ) + { + if( !pRes->bInvalid ) + { + rIo.SetError( SVSTREAM_CANNOT_MAKE ); + return NULL; + } + pRes->bInvalid = + pRes->bRemoved = + pRes->bTemp = FALSE; + pRes->bCreated = + pRes->bDirty = TRUE; + } + else + { + pRes = new StgDirEntry( aEntry ); + if( StgAvlNode::Insert( (StgAvlNode**) &rStg.pDown, pRes ) ) + { + pRes->pUp = &rStg; + pRes->ppRoot = &pRoot; + pRes->bCreated = + pRes->bDirty = TRUE; + } + else + { + rIo.SetError( SVSTREAM_CANNOT_MAKE ); + delete pRes; pRes = NULL; + } + } + return pRes; +} + +// Rename the given entry. + +BOOL StgDirStrm::Rename( StgDirEntry& rStg, const String& rOld, const String& rNew ) +{ + StgDirEntry* p = Find( rStg, rOld ); + if( p ) + { + + if( !StgAvlNode::Remove( (StgAvlNode**) &rStg.pDown, p, FALSE ) ) + return FALSE; + p->aEntry.SetName( rNew ); + if( !StgAvlNode::Insert( (StgAvlNode**) &rStg.pDown, p ) ) + return FALSE; + p->bRenamed = p->bDirty = TRUE; + return TRUE; + } + else + { + rIo.SetError( SVSTREAM_FILE_NOT_FOUND ); + return FALSE; + } +} + +// Move the given entry to a different storage. + +BOOL StgDirStrm::Move( StgDirEntry& rStg1, StgDirEntry& rStg2, const String& rName ) +{ + StgDirEntry* p = Find( rStg1, rName ); + if( p ) + { + if( !StgAvlNode::Move + ( (StgAvlNode**) &rStg1.pDown, (StgAvlNode**) &rStg2.pDown, p ) ) + return FALSE; + p->bDirty = TRUE; + return TRUE; + } + else + { + rIo.SetError( SVSTREAM_FILE_NOT_FOUND ); + return FALSE; + } +} + |