1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: stgdir.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sot.hxx"
34 #include <string.h> // memcpy()
37 #include "stgelem.hxx"
38 #include "stgcache.hxx"
39 #include "stgstrms.hxx"
44 //////////////////////////// class StgDirEntry /////////////////////////////
46 // This class holds the dir entry data and maintains dirty flags for both
47 // the entry and the data.
49 // Transacted mode for streams: On the first write, a temp stream pTmpStrm
50 // is created and operated on. A commit moves pTmpStrm to pCurStrm, which
51 // is used for subsequent reads. A new write creates a new copy of pTmpStrm
52 // based on pCurStrm. Reverting throws away pTmpStrm.
53 // Transacted mode for storages: A copy of the dir ents is kept in aSave.
54 // Committing means copying aEntry to aSave. Reverting means to copy aSave
55 // to aEntry, delete newly created entries and to reactivate removed entries.
57 // Problem der Implementation: Keine Hierarchischen commits. Daher nur
58 // insgesamt transaktionsorientert oder direkt.
60 StgDirEntry::StgDirEntry( const void* pFrom
, BOOL
* pbOk
) : StgAvlNode()
62 *pbOk
= aEntry
.Load( pFrom
);
67 StgDirEntry::StgDirEntry( const StgEntry
& r
) : StgAvlNode(), aEntry( r
)
72 // Helper for all ctors
74 void StgDirEntry::InitMembers()
97 StgDirEntry::~StgDirEntry()
105 // Comparison function
107 short StgDirEntry::Compare( const StgAvlNode
* p
) const
109 const StgDirEntry
* pEntry
= (const StgDirEntry
*) p
;
110 return aEntry
.Compare( pEntry
->aEntry
);
113 // Enumerate the entry numbers.
114 // n is incremented to show the total # of entries.
115 // These number are later used as page numbers when storing
116 // the TOC tree into the TOC stream. Remember that aSave is
117 // stored, not aEntry.
119 void StgDirEntry::Enum( INT32
& n
)
121 INT32 nLeft
= STG_FREE
, nRight
= STG_FREE
, nDown
= STG_FREE
;
125 ((StgDirEntry
*) pLeft
)->Enum( n
); nLeft
= ((StgDirEntry
*) pLeft
)->nEntry
;
129 ((StgDirEntry
*) pRight
)->Enum( n
); nRight
= ((StgDirEntry
*) pRight
)->nEntry
;
133 pDown
->Enum( n
); nDown
= pDown
->nEntry
;
135 aSave
.SetLeaf( STG_LEFT
, nLeft
);
136 aSave
.SetLeaf( STG_RIGHT
, nRight
);
137 aSave
.SetLeaf( STG_CHILD
, nDown
);
140 // Delete all temporary entries before writing the TOC stream.
141 // Until now Deltem is never called with bForce True
143 void StgDirEntry::DelTemp( BOOL bForce
)
146 ((StgDirEntry
*) pLeft
)->DelTemp( FALSE
);
148 ((StgDirEntry
*) pRight
)->DelTemp( FALSE
);
151 // If the storage is dead, of course all elements are dead, too
152 if( bInvalid
&& aEntry
.GetType() == STG_STORAGE
)
154 pDown
->DelTemp( bForce
);
156 if( ( bForce
|| bInvalid
)
157 && ( aEntry
.GetType() != STG_ROOT
) /* && ( nRefCnt <= 1 ) */ )
162 // this deletes the element if refcnt == 0!
163 BOOL bDel
= nRefCnt
== 0;
164 StgAvlNode::Remove( (StgAvlNode
**) &pUp
->pDown
, this, bDel
);
167 pLeft
= pRight
= pDown
= 0;
168 bInvalid
= bZombie
= TRUE
;
174 // Save the tree into the given dir stream
176 BOOL
StgDirEntry::Store( StgDirStrm
& rStrm
)
178 void* pEntry
= rStrm
.GetEntry( nEntry
, TRUE
);
181 // Do not store the current (maybe not commited) entry
182 aSave
.Store( pEntry
);
184 if( !((StgDirEntry
*) pLeft
)->Store( rStrm
) )
187 if( !((StgDirEntry
*) pRight
)->Store( rStrm
) )
190 if( !pDown
->Store( rStrm
) )
195 BOOL
StgDirEntry::StoreStream( StgIo
& rIo
)
197 if( aEntry
.GetType() == STG_STREAM
|| aEntry
.GetType() == STG_ROOT
)
201 // Delete the stream if needed
205 delete pStgStrm
, pStgStrm
= NULL
;
208 pStgStrm
->SetSize( 0 );
210 // or write the data stream
211 else if( !Tmp2Strm() )
217 // Save all dirty streams
219 BOOL
StgDirEntry::StoreStreams( StgIo
& rIo
)
221 if( !StoreStream( rIo
) )
224 if( !((StgDirEntry
*) pLeft
)->StoreStreams( rIo
) )
227 if( !((StgDirEntry
*) pRight
)->StoreStreams( rIo
) )
230 if( !pDown
->StoreStreams( rIo
) )
235 // Revert all directory entries after failure to write the TOC stream
237 void StgDirEntry::RevertAll()
241 ((StgDirEntry
*) pLeft
)->RevertAll();
243 ((StgDirEntry
*) pRight
)->RevertAll();
248 // Look if any element of the tree is dirty
250 BOOL
StgDirEntry::IsDirty()
252 if( bDirty
|| bInvalid
)
254 if( pLeft
&& ((StgDirEntry
*) pLeft
)->IsDirty() )
256 if( pRight
&& ((StgDirEntry
*) pRight
)->IsDirty() )
258 if( pDown
&& pDown
->IsDirty() )
265 void StgDirEntry::OpenStream( StgIo
& rIo
, BOOL bForceBig
)
267 INT32 nThreshold
= (USHORT
) rIo
.aHdr
.GetThreshold();
269 if( !bForceBig
&& aEntry
.GetSize() < nThreshold
)
270 pStgStrm
= new StgSmallStrm( rIo
, this );
272 pStgStrm
= new StgDataStrm( rIo
, this );
273 if( bInvalid
&& aEntry
.GetSize() )
275 // This entry has invalid data, so delete that data
277 // bRemoved = bInvalid = FALSE;
282 // Close the open stream without committing. If the entry is marked as
283 // temporary, delete it.
284 // Do not delete pCurStrm here!
285 // (TLX:??? Zumindest pStgStrm muss deleted werden.)
287 void StgDirEntry::Close()
295 // Get the current stream size
297 INT32
StgDirEntry::GetSize()
301 n
= pTmpStrm
->GetSize();
303 n
= pCurStrm
->GetSize();
304 else n
= aEntry
.GetSize();
308 // Set the stream size. This means also creating a temp stream.
310 BOOL
StgDirEntry::SetSize( INT32 nNewSize
)
313 !( nMode
& STREAM_WRITE
) ||
314 (!bDirect
&& !pTmpStrm
&& !Strm2Tmp())
320 if( nNewSize
< nPos
)
324 pTmpStrm
->SetSize( nNewSize
);
325 pStgStrm
->GetIo().SetError( pTmpStrm
->GetError() );
326 return BOOL( pTmpStrm
->GetError() == SVSTREAM_OK
);
331 StgIo
& rIo
= pStgStrm
->GetIo();
332 INT32 nThreshold
= rIo
.aHdr
.GetThreshold();
333 // ensure the correct storage stream!
334 StgStrm
* pOld
= NULL
;
336 if( nNewSize
>= nThreshold
&& pStgStrm
->IsSmallStrm() )
339 nOldSize
= (USHORT
) pOld
->GetSize();
340 pStgStrm
= new StgDataStrm( rIo
, STG_EOF
, 0 );
342 else if( nNewSize
< nThreshold
&& !pStgStrm
->IsSmallStrm() )
345 nOldSize
= (USHORT
) nNewSize
;
346 pStgStrm
= new StgSmallStrm( rIo
, STG_EOF
, 0 );
348 // now set the new size
349 if( pStgStrm
->SetSize( nNewSize
) )
351 // did we create a new stream?
354 // if so, we probably need to copy the old data
357 void* pBuf
= new BYTE
[ nOldSize
];
358 pOld
->Pos2Page( 0L );
359 pStgStrm
->Pos2Page( 0L );
360 if( pOld
->Read( pBuf
, nOldSize
)
361 && pStgStrm
->Write( pBuf
, nOldSize
) )
363 delete[] static_cast<BYTE
*>(pBuf
);
371 pStgStrm
->Pos2Page( nPos
);
372 pStgStrm
->SetEntry( *this );
376 pStgStrm
->SetSize( 0 );
383 pStgStrm
->Pos2Page( nPos
);
391 // Seek. On negative values, seek to EOF.
393 INT32
StgDirEntry::Seek( INT32 nNew
)
398 nNew
= pTmpStrm
->GetSize();
399 nNew
= pTmpStrm
->Seek( nNew
);
404 nNew
= pCurStrm
->GetSize();
405 nNew
= pCurStrm
->Seek( nNew
);
409 INT32 nSize
= aEntry
.GetSize();
414 // try to enlarge, the readonly streams should not allow this
417 if ( !( nMode
& STREAM_WRITE
) || !SetSize( nNew
) )
419 OSL_ENSURE( nMode
& STREAM_WRITE
, "Trying to resize readonly stream by seeking, could be a wrong offset!" );
425 pStgStrm
->Pos2Page( nNew
);
426 nNew
= pStgStrm
->GetPos();
433 INT32
StgDirEntry::Read( void* p
, INT32 nLen
)
438 nLen
= pTmpStrm
->Read( p
, nLen
);
440 nLen
= pCurStrm
->Read( p
, nLen
);
442 nLen
= pStgStrm
->Read( p
, nLen
);
449 INT32
StgDirEntry::Write( const void* p
, INT32 nLen
)
451 if( nLen
<= 0 || !( nMode
& STREAM_WRITE
) )
454 // Was this stream committed internally and reopened in direct mode?
455 if( bDirect
&& ( pCurStrm
|| pTmpStrm
) && !Tmp2Strm() )
457 // Is this stream opened in transacted mode? Do we have to make a copy?
458 if( !bDirect
&& !pTmpStrm
&& !Strm2Tmp() )
462 nLen
= pTmpStrm
->Write( p
, nLen
);
463 pStgStrm
->GetIo().SetError( pTmpStrm
->GetError() );
467 INT32 nNew
= nPos
+ nLen
;
468 if( nNew
> pStgStrm
->GetSize() )
470 if( !SetSize( nNew
) )
472 pStgStrm
->Pos2Page( nPos
);
474 nLen
= pStgStrm
->Write( p
, nLen
);
480 // Copy the data of one entry into another entry.
482 void StgDirEntry::Copy( StgDirEntry
& rDest
)
485 if( rDest
.SetSize( n
) && n
)
487 BYTE aTempBytes
[ 4096 ];
488 void* p
= static_cast<void*>( aTempBytes
);
496 if( Read( p
, nn
) != nn
)
498 if( rDest
.Write( p
, nn
) != nn
)
505 void StgDirEntry::Copy( BaseStorageStream
& rDest
)
508 if( rDest
.SetSize( n
) && n
)
510 ULONG Pos
= rDest
.Tell();
511 BYTE aTempBytes
[ 4096 ];
512 void* p
= static_cast<void*>( aTempBytes
);
520 if( Read( p
, nn
) != nn
)
522 if( sal::static_int_cast
<INT32
>(rDest
.Write( p
, nn
)) != nn
)
526 rDest
.Seek( Pos
); // ?! Seems to be undocumented !
532 BOOL
StgDirEntry::Commit()
534 // OSL_ENSURE( nMode & STREAM_WRITE, "Trying to commit readonly stream!" );
538 if( aEntry
.GetType() == STG_STREAM
)
541 delete pCurStrm
, pCurStrm
= pTmpStrm
, pTmpStrm
= NULL
;
543 // Delete the stream if needed
545 pStgStrm
->SetSize( 0 );
547 else if( aEntry
.GetType() == STG_STORAGE
&& bDirect
&& bRes
)
549 StgIterator
aIter( *this );
550 for( StgDirEntry
* p
= aIter
.First(); p
&& bRes
; p
= aIter
.Next() )
558 BOOL
StgDirEntry::Revert()
561 switch( aEntry
.GetType() )
565 delete pTmpStrm
, pTmpStrm
= pCurStrm
, pCurStrm
= NULL
;
569 BOOL bSomeRenamed
= FALSE
;
570 StgIterator
aOIter( *this );
571 StgDirEntry
* op
= aOIter
.First();
574 op
->aEntry
= op
->aSave
;
576 bSomeRenamed
= BOOL( bSomeRenamed
| op
->bRenamed
);
577 // Remove any new entries
580 op
->bCreated
= FALSE
;
584 // Reactivate any removed entries
585 else if( op
->bRemoved
)
586 op
->bRemoved
= op
->bInvalid
= op
->bTemp
= FALSE
;
589 // Resort all renamed entries
592 StgIterator
aIter( *this );
593 StgDirEntry
* p
= aIter
.First();
599 ( (StgAvlNode
**) &p
->pUp
->pDown
,
600 (StgAvlNode
**) &p
->pUp
->pDown
, p
);
618 // Copy the stg stream to the temp stream
620 BOOL
StgDirEntry::Strm2Tmp()
627 // It was already commited once
628 pTmpStrm
= new StgTmpStrm
;
629 if( pTmpStrm
->GetError() == SVSTREAM_OK
&& pTmpStrm
->Copy( *pCurStrm
) )
631 n
= 1; // indicates error
635 n
= aEntry
.GetSize();
636 pTmpStrm
= new StgTmpStrm( n
);
637 if( pTmpStrm
->GetError() == SVSTREAM_OK
)
641 BYTE aTempBytes
[ 4096 ];
642 void* p
= static_cast<void*>( aTempBytes
);
643 pStgStrm
->Pos2Page( 0L );
649 if( (ULONG
) pStgStrm
->Read( p
, nn
) != nn
)
651 if( pTmpStrm
->Write( p
, nn
) != nn
)
655 pStgStrm
->Pos2Page( nPos
);
656 pTmpStrm
->Seek( nPos
);
664 pStgStrm
->GetIo().SetError( pTmpStrm
->GetError() );
673 // Copy the temp stream to the stg stream during the final commit
675 BOOL
StgDirEntry::Tmp2Strm()
677 // We did commit once, but have not written since then
679 pTmpStrm
= pCurStrm
, pCurStrm
= NULL
;
682 ULONG n
= pTmpStrm
->GetSize();
684 StgIo
& rIo
= pStgStrm
->GetIo();
685 ULONG nThreshold
= (ULONG
) rIo
.aHdr
.GetThreshold();
687 pNewStrm
= new StgSmallStrm( rIo
, STG_EOF
, 0 );
689 pNewStrm
= new StgDataStrm( rIo
, STG_EOF
, 0 );
690 if( pNewStrm
->SetSize( n
) )
693 pTmpStrm
->Seek( 0L );
699 if( pTmpStrm
->Read( p
, nn
) != nn
)
701 if( (ULONG
) pNewStrm
->Write( p
, nn
) != nn
)
707 pTmpStrm
->Seek( nPos
);
708 pStgStrm
->GetIo().SetError( pTmpStrm
->GetError() );
714 pStgStrm
->SetSize( 0L );
717 pNewStrm
->SetEntry( *this );
718 pNewStrm
->Pos2Page( nPos
);
721 pTmpStrm
= pCurStrm
= NULL
;
729 // Check if the given entry is contained in this entry
731 BOOL
StgDirEntry::IsContained( StgDirEntry
* pStg
)
733 if( aEntry
.GetType() == STG_STORAGE
)
735 StgIterator
aIter( *this );
736 StgDirEntry
* p
= aIter
.First();
739 if( !p
->aEntry
.Compare( pStg
->aEntry
) )
741 if( p
->aEntry
.GetType() == STG_STORAGE
)
742 if( !p
->IsContained( pStg
) )
750 // Invalidate all open entries by setting the RefCount to 0. If the bDel
751 // flag is set, also set the invalid flag to indicate deletion during the
752 // next dir stream flush.
754 void StgDirEntry::Invalidate( BOOL bDel
)
758 bRemoved
= bInvalid
= TRUE
;
759 switch( aEntry
.GetType() )
764 StgIterator
aIter( *this );
765 for( StgDirEntry
* p
= aIter
.First(); p
; p
= aIter
.Next() )
766 p
->Invalidate( bDel
);
774 ///////////////////////////// class StgDirStrm ////////////////////////////
776 // This specialized stream is the maintenance stream for the directory tree.
778 StgDirStrm::StgDirStrm( StgIo
& r
)
779 : StgDataStrm( r
, r
.aHdr
.GetTOCStart(), -1 )
785 nEntries
= nPageSize
/ STGENTRY_SIZE
;
786 if( nStart
== STG_EOF
)
790 aRoot
.SetName( String::CreateFromAscii( RTL_CONSTASCII_STRINGPARAM( "Root Entry" ) ) );
791 aRoot
.SetType( STG_ROOT
);
792 pRoot
= new StgDirEntry( aRoot
);
797 // temporarily use this instance as owner, so
798 // the TOC pages can be removed.
799 pEntry
= (StgDirEntry
*) this; // just for a bit pattern
800 SetupEntry( 0, pRoot
);
801 rIo
.Revert( pEntry
);
806 StgDirStrm::~StgDirStrm()
811 // Recursively parse the directory tree during reading the TOC stream
813 void StgDirStrm::SetupEntry( INT32 n
, StgDirEntry
* pUpper
)
815 void* p
= ( n
== STG_FREE
) ? NULL
: GetEntry( n
);
819 StgDirEntry
* pCur
= new StgDirEntry( p
, &bOk
);
824 rIo
.SetError( SVSTREAM_GENERALERROR
);
831 pCur
->aEntry
.SetType( STG_ROOT
);
833 INT32 nLeft
= pCur
->aEntry
.GetLeaf( STG_LEFT
);
834 INT32 nRight
= pCur
->aEntry
.GetLeaf( STG_RIGHT
);
836 INT32 nLeaf
= STG_FREE
;
837 if( pCur
->aEntry
.GetType() == STG_STORAGE
|| pCur
->aEntry
.GetType() == STG_ROOT
)
839 nLeaf
= pCur
->aEntry
.GetLeaf( STG_CHILD
);
840 if (nLeaf
!= STG_FREE
&& nLeaf
== n
)
843 rIo
.SetError( SVSTREAM_GENERALERROR
);
848 if( nLeaf
!= 0 && nLeft
!= 0 && nRight
!= 0 )
850 if( StgAvlNode::Insert
851 ( (StgAvlNode
**) ( pUpper
? &pUpper
->pDown
: &pRoot
), pCur
) )
854 pCur
->ppRoot
= &pRoot
;
858 rIo
.SetError( SVSTREAM_CANNOT_MAKE
);
859 delete pCur
; pCur
= NULL
;
862 SetupEntry( nLeft
, pUpper
);
863 SetupEntry( nRight
, pUpper
);
864 SetupEntry( nLeaf
, pCur
);
869 // Extend or shrink the directory stream.
871 BOOL
StgDirStrm::SetSize( INT32 nBytes
)
873 // Always allocate full pages
874 nBytes
= ( ( nBytes
+ nPageSize
- 1 ) / nPageSize
) * nPageSize
;
875 return StgStrm::SetSize( nBytes
);
878 // Save the TOC stream into a new substream after saving all data streams
880 BOOL
StgDirStrm::Store()
882 if( !pRoot
->IsDirty() )
884 if( !pRoot
->StoreStreams( rIo
) )
886 // After writing all streams, the data FAT stream has changed,
887 // so we have to commit the root again
889 // We want a completely new stream, so fake an empty stream
890 INT32 nOldStart
= nStart
; // save for later deletion
891 INT32 nOldSize
= nSize
;
892 nStart
= nPage
= STG_EOF
;
895 // Delete all temporary entries
896 pRoot
->DelTemp( FALSE
);
897 // set the entry numbers
900 if( !SetSize( n
* STGENTRY_SIZE
) )
902 nStart
= nOldStart
; nSize
= nOldSize
;
906 // set up the cache elements for the new stream
907 if( !Copy( STG_FREE
, nSize
) )
912 // Write the data to the new stream
913 if( !pRoot
->Store( *this ) )
918 // fill any remaining entries with empty data
919 INT32 ne
= nSize
/ STGENTRY_SIZE
;
924 void* p
= GetEntry( n
++, TRUE
);
932 // Now we can release the old stream
933 pFat
->FreePages( nOldStart
, TRUE
);
934 rIo
.aHdr
.SetTOCStart( nStart
);
940 void* StgDirStrm::GetEntry( INT32 n
, BOOL bDirty
)
945 return GetPtr( n
, TRUE
, bDirty
);
950 StgDirEntry
* StgDirStrm::Find( StgDirEntry
& rStg
, const String
& rName
)
956 if( !aEntry
.SetName( rName
) )
958 rIo
.SetError( SVSTREAM_GENERALERROR
);
961 // Look in the directory attached to the entry
962 StgDirEntry
aTest( aEntry
);
963 return (StgDirEntry
*) rStg
.pDown
->Find( &aTest
);
969 // Create a new entry.
971 StgDirEntry
* StgDirStrm::Create
972 ( StgDirEntry
& rStg
, const String
& rName
, StgEntryType eType
)
976 aEntry
.SetType( eType
);
977 if( !aEntry
.SetName( rName
) )
979 rIo
.SetError( SVSTREAM_GENERALERROR
);
982 StgDirEntry
* pRes
= Find( rStg
, rName
);
985 if( !pRes
->bInvalid
)
987 rIo
.SetError( SVSTREAM_CANNOT_MAKE
);
998 pRes
= new StgDirEntry( aEntry
);
999 if( StgAvlNode::Insert( (StgAvlNode
**) &rStg
.pDown
, pRes
) )
1002 pRes
->ppRoot
= &pRoot
;
1004 pRes
->bDirty
= TRUE
;
1008 rIo
.SetError( SVSTREAM_CANNOT_MAKE
);
1009 delete pRes
; pRes
= NULL
;
1015 // Rename the given entry.
1017 BOOL
StgDirStrm::Rename( StgDirEntry
& rStg
, const String
& rOld
, const String
& rNew
)
1019 StgDirEntry
* p
= Find( rStg
, rOld
);
1023 if( !StgAvlNode::Remove( (StgAvlNode
**) &rStg
.pDown
, p
, FALSE
) )
1025 p
->aEntry
.SetName( rNew
);
1026 if( !StgAvlNode::Insert( (StgAvlNode
**) &rStg
.pDown
, p
) )
1028 p
->bRenamed
= p
->bDirty
= TRUE
;
1033 rIo
.SetError( SVSTREAM_FILE_NOT_FOUND
);
1038 // Move the given entry to a different storage.
1040 BOOL
StgDirStrm::Move( StgDirEntry
& rStg1
, StgDirEntry
& rStg2
, const String
& rName
)
1042 StgDirEntry
* p
= Find( rStg1
, rName
);
1045 if( !StgAvlNode::Move
1046 ( (StgAvlNode
**) &rStg1
.pDown
, (StgAvlNode
**) &rStg2
.pDown
, p
) )
1053 rIo
.SetError( SVSTREAM_FILE_NOT_FOUND
);