| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_sot.hxx" |
| |
| #include <string.h> // memcpy() |
| |
| #include "sot/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* pBuffer, sal_uInt32 nBufferLen, sal_Bool * pbOk ) : StgAvlNode() |
| { |
| *pbOk = aEntry.Load( pBuffer, nBufferLen ); |
| |
| 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 = sal_True; |
| bInvalid = |
| bCreated = |
| bRenamed = |
| bRemoved = |
| bTemp = |
| bDirty = |
| bZombie = sal_False; |
| } |
| |
| StgDirEntry::~StgDirEntry() |
| { |
| Close(); |
| delete pCurStrm; |
| delete pStgStrm; |
| delete pDown; |
| } |
| |
| // Comparison function |
| |
| short StgDirEntry::Compare( const StgAvlNode* p ) const |
| { |
| short nResult = -1; |
| if ( p ) |
| { |
| const StgDirEntry* pEntry = (const StgDirEntry*) p; |
| nResult = aEntry.Compare( pEntry->aEntry ); |
| } |
| return nResult; |
| } |
| |
| // 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( sal_Int32& n ) |
| { |
| sal_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( sal_Bool bForce ) |
| { |
| if( pLeft ) |
| ((StgDirEntry*) pLeft)->DelTemp( sal_False ); |
| if( pRight ) |
| ((StgDirEntry*) pRight)->DelTemp( sal_False ); |
| if( pDown ) |
| { |
| // If the storage is dead, of course all elements are dead, too |
| if( bInvalid && aEntry.GetType() == STG_STORAGE ) |
| bForce = sal_True; |
| pDown->DelTemp( bForce ); |
| } |
| if( ( bForce || bInvalid ) |
| && ( aEntry.GetType() != STG_ROOT ) /* && ( nRefCnt <= 1 ) */ ) |
| { |
| Close(); |
| if( pUp ) |
| { |
| // this deletes the element if refcnt == 0! |
| sal_Bool bDel = nRefCnt == 0; |
| StgAvlNode::Remove( (StgAvlNode**) &pUp->pDown, this, bDel ); |
| if( !bDel ) |
| { |
| pLeft = pRight = pDown = 0; |
| bInvalid = bZombie = sal_True; |
| } |
| } |
| } |
| } |
| |
| // Save the tree into the given dir stream |
| |
| sal_Bool StgDirEntry::Store( StgDirStrm& rStrm ) |
| { |
| void* pEntry = rStrm.GetEntry( nEntry, sal_True ); |
| if( !pEntry ) |
| return sal_False; |
| // Do not store the current (maybe not commited) entry |
| aSave.Store( pEntry ); |
| if( pLeft ) |
| if( !((StgDirEntry*) pLeft)->Store( rStrm ) ) |
| return sal_False; |
| if( pRight ) |
| if( !((StgDirEntry*) pRight)->Store( rStrm ) ) |
| return sal_False; |
| if( pDown ) |
| if( !pDown->Store( rStrm ) ) |
| return sal_False; |
| return sal_True; |
| } |
| |
| sal_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 sal_False; |
| } |
| return sal_True; |
| } |
| |
| // Save all dirty streams |
| |
| sal_Bool StgDirEntry::StoreStreams( StgIo& rIo ) |
| { |
| if( !StoreStream( rIo ) ) |
| return sal_False; |
| if( pLeft ) |
| if( !((StgDirEntry*) pLeft)->StoreStreams( rIo ) ) |
| return sal_False; |
| if( pRight ) |
| if( !((StgDirEntry*) pRight)->StoreStreams( rIo ) ) |
| return sal_False; |
| if( pDown ) |
| if( !pDown->StoreStreams( rIo ) ) |
| return sal_False; |
| return sal_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 |
| |
| sal_Bool StgDirEntry::IsDirty() |
| { |
| if( bDirty || bInvalid ) |
| return sal_True; |
| if( pLeft && ((StgDirEntry*) pLeft)->IsDirty() ) |
| return sal_True; |
| if( pRight && ((StgDirEntry*) pRight)->IsDirty() ) |
| return sal_True; |
| if( pDown && pDown->IsDirty() ) |
| return sal_True; |
| return sal_False; |
| } |
| |
| // Set up a stream. |
| |
| void StgDirEntry::OpenStream( StgIo& rIo, sal_Bool bForceBig ) |
| { |
| sal_Int32 nThreshold = (sal_uInt16) 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 = sal_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 |
| |
| sal_Int32 StgDirEntry::GetSize() |
| { |
| sal_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. |
| |
| sal_Bool StgDirEntry::SetSize( sal_Int32 nNewSize ) |
| { |
| if ( |
| !( nMode & STREAM_WRITE ) || |
| (!bDirect && !pTmpStrm && !Strm2Tmp()) |
| ) |
| { |
| return sal_False; |
| } |
| |
| if( nNewSize < nPos ) |
| nPos = nNewSize; |
| if( pTmpStrm ) |
| { |
| pTmpStrm->SetSize( nNewSize ); |
| pStgStrm->GetIo().SetError( pTmpStrm->GetError() ); |
| return sal_Bool( pTmpStrm->GetError() == SVSTREAM_OK ); |
| } |
| else |
| { |
| OSL_ENSURE( pStgStrm, "The pointer may not be NULL!" ); |
| if ( !pStgStrm ) |
| return sal_False; |
| |
| sal_Bool bRes = sal_False; |
| StgIo& rIo = pStgStrm->GetIo(); |
| sal_Int32 nThreshold = rIo.aHdr.GetThreshold(); |
| // ensure the correct storage stream! |
| StgStrm* pOld = NULL; |
| sal_uInt16 nOldSize = 0; |
| if( nNewSize >= nThreshold && pStgStrm->IsSmallStrm() ) |
| { |
| pOld = pStgStrm; |
| nOldSize = (sal_uInt16) pOld->GetSize(); |
| pStgStrm = new StgDataStrm( rIo, STG_EOF, 0 ); |
| } |
| else if( nNewSize < nThreshold && !pStgStrm->IsSmallStrm() ) |
| { |
| pOld = pStgStrm; |
| nOldSize = (sal_uInt16) 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 sal_uInt8[ nOldSize ]; |
| pOld->Pos2Page( 0L ); |
| pStgStrm->Pos2Page( 0L ); |
| if( pOld->Read( pBuf, nOldSize ) |
| && pStgStrm->Write( pBuf, nOldSize ) ) |
| bRes = sal_True; |
| delete[] static_cast<sal_uInt8*>(pBuf); |
| } |
| else |
| bRes = sal_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 = sal_True; |
| } |
| } |
| return bRes; |
| } |
| } |
| |
| // Seek. On negative values, seek to EOF. |
| |
| sal_Int32 StgDirEntry::Seek( sal_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 |
| { |
| OSL_ENSURE( pStgStrm, "The pointer may not be NULL!" ); |
| if ( !pStgStrm ) |
| return nPos; |
| |
| sal_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 |
| |
| sal_Int32 StgDirEntry::Read( void* p, sal_Int32 nLen ) |
| { |
| if( nLen <= 0 ) |
| return 0; |
| if( pTmpStrm ) |
| nLen = pTmpStrm->Read( p, nLen ); |
| else if( pCurStrm ) |
| nLen = pCurStrm->Read( p, nLen ); |
| else |
| { |
| OSL_ENSURE( pStgStrm, "The pointer may not be NULL!" ); |
| if ( !pStgStrm ) |
| return 0; |
| |
| nLen = pStgStrm->Read( p, nLen ); |
| } |
| |
| nPos += nLen; |
| return nLen; |
| } |
| |
| // Write |
| |
| sal_Int32 StgDirEntry::Write( const void* p, sal_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; |
| |
| OSL_ENSURE( pStgStrm, "The pointer may not be NULL!" ); |
| if ( !pStgStrm ) |
| return 0; |
| |
| if( pTmpStrm ) |
| { |
| nLen = pTmpStrm->Write( p, nLen ); |
| pStgStrm->GetIo().SetError( pTmpStrm->GetError() ); |
| } |
| else |
| { |
| sal_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 ) |
| { |
| sal_Int32 n = GetSize(); |
| if( rDest.SetSize( n ) && n ) |
| { |
| sal_uInt8 aTempBytes[ 4096 ]; |
| void* p = static_cast<void*>( aTempBytes ); |
| Seek( 0L ); |
| rDest.Seek( 0L ); |
| while( n ) |
| { |
| sal_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 ) |
| { |
| sal_Int32 n = GetSize(); |
| if( rDest.SetSize( n ) && n ) |
| { |
| sal_uLong Pos = rDest.Tell(); |
| sal_uInt8 aTempBytes[ 4096 ]; |
| void* p = static_cast<void*>( aTempBytes ); |
| Seek( 0L ); |
| rDest.Seek( 0L ); |
| while( n ) |
| { |
| sal_Int32 nn = n; |
| if( nn > 4096 ) |
| nn = 4096; |
| if( Read( p, nn ) != nn ) |
| break; |
| if( sal::static_int_cast<sal_Int32>(rDest.Write( p, nn )) != nn ) |
| break; |
| n -= nn; |
| } |
| rDest.Seek( Pos ); // ?! Seems to be undocumented ! |
| } |
| } |
| |
| // Commit this entry |
| |
| sal_Bool StgDirEntry::Commit() |
| { |
| // OSL_ENSURE( nMode & STREAM_WRITE, "Trying to commit readonly stream!" ); |
| |
| aSave = aEntry; |
| sal_Bool bRes = sal_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 |
| |
| sal_Bool StgDirEntry::Revert() |
| { |
| aEntry = aSave; |
| switch( aEntry.GetType() ) |
| { |
| case STG_STREAM: |
| if( pCurStrm ) |
| delete pTmpStrm, pTmpStrm = pCurStrm, pCurStrm = NULL; |
| break; |
| case STG_STORAGE: |
| { |
| sal_Bool bSomeRenamed = sal_False; |
| StgIterator aOIter( *this ); |
| StgDirEntry* op = aOIter.First(); |
| while( op ) |
| { |
| op->aEntry = op->aSave; |
| op->bDirty = sal_False; |
| bSomeRenamed = sal_Bool( bSomeRenamed | op->bRenamed ); |
| // Remove any new entries |
| if( op->bCreated ) |
| { |
| op->bCreated = sal_False; |
| op->Close(); |
| op->bInvalid = sal_True; |
| } |
| // Reactivate any removed entries |
| else if( op->bRemoved ) |
| op->bRemoved = op->bInvalid = op->bTemp = sal_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 = sal_False; |
| } |
| p = aIter.Next(); |
| } |
| } |
| DelTemp( sal_False ); |
| break; |
| } |
| case STG_EMPTY: |
| case STG_LOCKBYTES: |
| case STG_PROPERTY: |
| case STG_ROOT: |
| break; |
| } |
| return sal_True; |
| } |
| |
| // Copy the stg stream to the temp stream |
| |
| sal_Bool StgDirEntry::Strm2Tmp() |
| { |
| if( !pTmpStrm ) |
| { |
| sal_uLong n = 0; |
| if( pCurStrm ) |
| { |
| // It was already commited once |
| pTmpStrm = new StgTmpStrm; |
| if( pTmpStrm->GetError() == SVSTREAM_OK && pTmpStrm->Copy( *pCurStrm ) ) |
| return sal_True; |
| n = 1; // indicates error |
| } |
| else |
| { |
| n = aEntry.GetSize(); |
| pTmpStrm = new StgTmpStrm( n ); |
| if( pTmpStrm->GetError() == SVSTREAM_OK ) |
| { |
| if( n ) |
| { |
| OSL_ENSURE( pStgStrm, "The pointer may not be NULL!" ); |
| if ( !pStgStrm ) |
| return sal_False; |
| |
| sal_uInt8 aTempBytes[ 4096 ]; |
| void* p = static_cast<void*>( aTempBytes ); |
| pStgStrm->Pos2Page( 0L ); |
| while( n ) |
| { |
| sal_uLong nn = n; |
| if( nn > 4096 ) |
| nn = 4096; |
| if( (sal_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 ) |
| { |
| OSL_ENSURE( pStgStrm, "The pointer may not be NULL!" ); |
| if ( pStgStrm ) |
| pStgStrm->GetIo().SetError( pTmpStrm->GetError() ); |
| |
| delete pTmpStrm; |
| pTmpStrm = NULL; |
| return sal_False; |
| } |
| } |
| return sal_True; |
| } |
| |
| // Copy the temp stream to the stg stream during the final commit |
| |
| sal_Bool StgDirEntry::Tmp2Strm() |
| { |
| // We did commit once, but have not written since then |
| if( !pTmpStrm ) |
| pTmpStrm = pCurStrm, pCurStrm = NULL; |
| if( pTmpStrm ) |
| { |
| OSL_ENSURE( pStgStrm, "The pointer may not be NULL!" ); |
| if ( !pStgStrm ) |
| return sal_False; |
| sal_uLong n = pTmpStrm->GetSize(); |
| StgStrm* pNewStrm; |
| StgIo& rIo = pStgStrm->GetIo(); |
| sal_uLong nThreshold = (sal_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 ) ) |
| { |
| sal_uInt8 p[ 4096 ]; |
| pTmpStrm->Seek( 0L ); |
| while( n ) |
| { |
| sal_uLong nn = n; |
| if( nn > 4096 ) |
| nn = 4096; |
| if( pTmpStrm->Read( p, nn ) != nn ) |
| break; |
| if( (sal_uLong) pNewStrm->Write( p, nn ) != nn ) |
| break; |
| n -= nn; |
| } |
| if( n ) |
| { |
| pTmpStrm->Seek( nPos ); |
| pStgStrm->GetIo().SetError( pTmpStrm->GetError() ); |
| delete pNewStrm; |
| return sal_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 sal_True; |
| } |
| |
| // Check if the given entry is contained in this entry |
| |
| sal_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 sal_False; |
| if( p->aEntry.GetType() == STG_STORAGE ) |
| if( !p->IsContained( pStg ) ) |
| return sal_False; |
| p = aIter.Next(); |
| } |
| } |
| return sal_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( sal_Bool bDel ) |
| { |
| // nRefCnt = 0; |
| if( bDel ) |
| bRemoved = bInvalid = sal_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, nSize/STGENTRY_SIZE, 0); |
| rIo.Revert( pEntry ); |
| pEntry = NULL; |
| } |
| } |
| |
| StgDirStrm::~StgDirStrm() |
| { |
| delete pRoot; |
| } |
| |
| // Recursively parse the directory tree during reading the TOC stream |
| |
| void StgDirStrm::SetupEntry ( |
| const sal_Int32 n, |
| StgDirEntry* pUpper, |
| const sal_Int32 nEntryCount, |
| const sal_Int32 nDepth) |
| { |
| if (nDepth > nEntryCount) |
| { |
| // Tree grew higher than there are different nodes. Looks like |
| // something is wrong with the file. Return now to avoid |
| // infinite recursion. |
| return; |
| } |
| else if (n>=nEntryCount || (n<0 && n!=STG_FREE)) |
| { |
| // n has an invalid value. Don't access the corresponding |
| // stream content. |
| return; |
| } |
| |
| void* p = ( n == STG_FREE ) ? NULL : GetEntry( n ); |
| if( p ) |
| { |
| sal_Bool bOk(sal_False); |
| StgDirEntry* pCur = new StgDirEntry( p, STGENTRY_SIZE, &bOk ); |
| |
| if( !bOk ) |
| { |
| delete pCur; |
| rIo.SetError( SVSTREAM_GENERALERROR ); |
| // an error occured |
| return; |
| } |
| |
| // better it is |
| if( !pUpper ) |
| pCur->aEntry.SetType( STG_ROOT ); |
| |
| sal_Int32 nLeft = pCur->aEntry.GetLeaf( STG_LEFT ); |
| sal_Int32 nRight = pCur->aEntry.GetLeaf( STG_RIGHT ); |
| // substorage? |
| sal_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, nEntryCount, nDepth+1); |
| SetupEntry( nRight, pUpper, nEntryCount, nDepth+1); |
| SetupEntry( nLeaf, pCur, nEntryCount, nDepth+1); |
| } |
| } |
| } |
| |
| // Extend or shrink the directory stream. |
| |
| sal_Bool StgDirStrm::SetSize( sal_Int32 nBytes ) |
| { |
| // Always allocate full pages |
| if ( nBytes < 0 ) |
| nBytes = 0; |
| |
| nBytes = ( ( nBytes + nPageSize - 1 ) / nPageSize ) * nPageSize; |
| return StgStrm::SetSize( nBytes ); |
| } |
| |
| // Save the TOC stream into a new substream after saving all data streams |
| |
| sal_Bool StgDirStrm::Store() |
| { |
| if( !pRoot || !pRoot->IsDirty() ) |
| return sal_True; |
| if( !pRoot->StoreStreams( rIo ) ) |
| return sal_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 |
| sal_Int32 nOldStart = nStart; // save for later deletion |
| sal_Int32 nOldSize = nSize; |
| nStart = nPage = STG_EOF; |
| nSize = nPos = 0; |
| nOffset = 0; |
| // Delete all temporary entries |
| pRoot->DelTemp( sal_False ); |
| // set the entry numbers |
| sal_Int32 n = 0; |
| pRoot->Enum( n ); |
| if( !SetSize( n * STGENTRY_SIZE ) ) |
| { |
| nStart = nOldStart; nSize = nOldSize; |
| pRoot->RevertAll(); |
| return sal_False; |
| } |
| // set up the cache elements for the new stream |
| if( !Copy( STG_FREE, nSize ) ) |
| { |
| pRoot->RevertAll(); |
| return sal_False; |
| } |
| // Write the data to the new stream |
| if( !pRoot->Store( *this ) ) |
| { |
| pRoot->RevertAll(); |
| return sal_False; |
| } |
| // fill any remaining entries with empty data |
| sal_Int32 ne = nSize / STGENTRY_SIZE; |
| StgEntry aEmpty; |
| aEmpty.Init(); |
| while( n < ne ) |
| { |
| void* p = GetEntry( n++, sal_True ); |
| if( !p ) |
| { |
| pRoot->RevertAll(); |
| return sal_False; |
| } |
| aEmpty.Store( p ); |
| } |
| // Now we can release the old stream |
| pFat->FreePages( nOldStart, sal_True ); |
| rIo.aHdr.SetTOCStart( nStart ); |
| return sal_True; |
| } |
| |
| // Get a dir entry. |
| |
| void* StgDirStrm::GetEntry( sal_Int32 n, sal_Bool bDirty ) |
| { |
| if( n < 0 ) |
| return NULL; |
| |
| n *= STGENTRY_SIZE; |
| if( n < 0 && n >= nSize ) |
| return NULL; |
| return GetPtr( n, sal_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 = sal_False; |
| pRes->bCreated = |
| pRes->bDirty = sal_True; |
| } |
| else |
| { |
| pRes = new StgDirEntry( aEntry ); |
| if( StgAvlNode::Insert( (StgAvlNode**) &rStg.pDown, pRes ) ) |
| { |
| pRes->pUp = &rStg; |
| pRes->ppRoot = &pRoot; |
| pRes->bCreated = |
| pRes->bDirty = sal_True; |
| } |
| else |
| { |
| rIo.SetError( SVSTREAM_CANNOT_MAKE ); |
| delete pRes; pRes = NULL; |
| } |
| } |
| return pRes; |
| } |
| |
| // Rename the given entry. |
| |
| sal_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, sal_False ) ) |
| return sal_False; |
| p->aEntry.SetName( rNew ); |
| if( !StgAvlNode::Insert( (StgAvlNode**) &rStg.pDown, p ) ) |
| return sal_False; |
| p->bRenamed = p->bDirty = sal_True; |
| return sal_True; |
| } |
| else |
| { |
| rIo.SetError( SVSTREAM_FILE_NOT_FOUND ); |
| return sal_False; |
| } |
| } |
| |
| // Move the given entry to a different storage. |
| |
| sal_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 sal_False; |
| p->bDirty = sal_True; |
| return sal_True; |
| } |
| else |
| { |
| rIo.SetError( SVSTREAM_FILE_NOT_FOUND ); |
| return sal_False; |
| } |
| } |
| |