bump product version to 4.1.6.2
[LibreOffice.git] / sot / source / sdstor / stgdir.cxx
blobe79e8f0a880e4b32a8b851b68f43beaf1123f4f4
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <string.h> // memcpy()
23 #include "sot/stg.hxx"
24 #include "stgelem.hxx"
25 #include "stgcache.hxx"
26 #include "stgstrms.hxx"
27 #include "stgdir.hxx"
28 #include "stgio.hxx"
31 //////////////////////////// class StgDirEntry /////////////////////////////
33 // This class holds the dir entry data and maintains dirty flags for both
34 // the entry and the data.
36 // Transacted mode for streams: On the first write, a temp stream pTmpStrm
37 // is created and operated on. A commit moves pTmpStrm to pCurStrm, which
38 // is used for subsequent reads. A new write creates a new copy of pTmpStrm
39 // based on pCurStrm. Reverting throws away pTmpStrm.
40 // Transacted mode for storages: A copy of the dir ents is kept in aSave.
41 // Committing means copying aEntry to aSave. Reverting means to copy aSave
42 // to aEntry, delete newly created entries and to reactivate removed entries.
44 // Problem der Implementation: Keine Hierarchischen commits. Daher nur
45 // insgesamt transaktionsorientert oder direkt.
47 StgDirEntry::StgDirEntry( const void* pBuffer, sal_uInt32 nBufferLen, bool * pbOk ) : StgAvlNode()
49 *pbOk = aEntry.Load( pBuffer, nBufferLen );
51 InitMembers();
54 StgDirEntry::StgDirEntry( const StgEntry& r ) : StgAvlNode(), aEntry( r )
56 InitMembers();
59 // Helper for all ctors
61 void StgDirEntry::InitMembers()
63 aSave = aEntry;
64 pUp =
65 pDown = NULL;
66 ppRoot = NULL;
67 pStgStrm = NULL;
68 pCurStrm =
69 pTmpStrm = NULL;
70 nPos =
71 nEntry =
72 nRefCnt = 0;
73 nMode = STREAM_READ;
74 bDirect = true;
75 bInvalid =
76 bCreated =
77 bRenamed =
78 bRemoved =
79 bTemp =
80 bDirty =
81 bZombie = false;
84 StgDirEntry::~StgDirEntry()
86 Close();
87 delete pCurStrm;
88 delete pStgStrm;
89 delete pDown;
92 // Comparison function
94 short StgDirEntry::Compare( const StgAvlNode* p ) const
96 short nResult = -1;
97 if ( p )
99 const StgDirEntry* pEntry = (const StgDirEntry*) p;
100 nResult = aEntry.Compare( pEntry->aEntry );
102 return nResult;
105 // Enumerate the entry numbers.
106 // n is incremented to show the total # of entries.
107 // These number are later used as page numbers when storing
108 // the TOC tree into the TOC stream. Remember that aSave is
109 // stored, not aEntry.
111 void StgDirEntry::Enum( sal_Int32& n )
113 sal_Int32 nLeft = STG_FREE, nRight = STG_FREE, nDown = STG_FREE;
114 nEntry = n++;
115 if( pLeft )
117 ((StgDirEntry*) pLeft)->Enum( n ); nLeft = ((StgDirEntry*) pLeft)->nEntry;
119 if( pRight )
121 ((StgDirEntry*) pRight)->Enum( n ); nRight = ((StgDirEntry*) pRight)->nEntry;
123 if( pDown )
125 pDown->Enum( n ); nDown = pDown->nEntry;
127 aSave.SetLeaf( STG_LEFT, nLeft );
128 aSave.SetLeaf( STG_RIGHT, nRight );
129 aSave.SetLeaf( STG_CHILD, nDown );
132 // Delete all temporary entries before writing the TOC stream.
133 // Until now Deltem is never called with bForce True
135 void StgDirEntry::DelTemp( bool bForce )
137 if( pLeft )
138 ((StgDirEntry*) pLeft)->DelTemp( false );
139 if( pRight )
140 ((StgDirEntry*) pRight)->DelTemp( false );
141 if( pDown )
143 // If the storage is dead, of course all elements are dead, too
144 if( bInvalid && aEntry.GetType() == STG_STORAGE )
145 bForce = true;
146 pDown->DelTemp( bForce );
148 if( ( bForce || bInvalid )
149 && ( aEntry.GetType() != STG_ROOT ) /* && ( nRefCnt <= 1 ) */ )
151 Close();
152 if( pUp )
154 // this deletes the element if refcnt == 0!
155 bool bDel = nRefCnt == 0;
156 StgAvlNode::Remove( (StgAvlNode**) &pUp->pDown, this, bDel );
157 if( !bDel )
159 pLeft = pRight = pDown = 0;
160 bInvalid = bZombie = true;
166 // Save the tree into the given dir stream
168 bool StgDirEntry::Store( StgDirStrm& rStrm )
170 void* pEntry = rStrm.GetEntry( nEntry, true );
171 if( !pEntry )
172 return false;
173 // Do not store the current (maybe not commited) entry
174 aSave.Store( pEntry );
175 if( pLeft )
176 if( !((StgDirEntry*) pLeft)->Store( rStrm ) )
177 return false;
178 if( pRight )
179 if( !((StgDirEntry*) pRight)->Store( rStrm ) )
180 return false;
181 if( pDown )
182 if( !pDown->Store( rStrm ) )
183 return false;
184 return true;
187 bool StgDirEntry::StoreStream( StgIo& rIo )
189 if( aEntry.GetType() == STG_STREAM || aEntry.GetType() == STG_ROOT )
191 if( bInvalid )
193 // Delete the stream if needed
194 if( !pStgStrm )
196 OpenStream( rIo );
197 delete pStgStrm, pStgStrm = NULL;
199 else
200 pStgStrm->SetSize( 0 );
202 // or write the data stream
203 else if( !Tmp2Strm() )
204 return false;
206 return true;
209 // Save all dirty streams
211 bool StgDirEntry::StoreStreams( StgIo& rIo )
213 if( !StoreStream( rIo ) )
214 return false;
215 if( pLeft )
216 if( !((StgDirEntry*) pLeft)->StoreStreams( rIo ) )
217 return false;
218 if( pRight )
219 if( !((StgDirEntry*) pRight)->StoreStreams( rIo ) )
220 return false;
221 if( pDown )
222 if( !pDown->StoreStreams( rIo ) )
223 return false;
224 return true;
227 // Revert all directory entries after failure to write the TOC stream
229 void StgDirEntry::RevertAll()
231 aEntry = aSave;
232 if( pLeft )
233 ((StgDirEntry*) pLeft)->RevertAll();
234 if( pRight )
235 ((StgDirEntry*) pRight)->RevertAll();
236 if( pDown )
237 pDown->RevertAll();
240 // Look if any element of the tree is dirty
242 bool StgDirEntry::IsDirty()
244 if( bDirty || bInvalid )
245 return true;
246 if( pLeft && ((StgDirEntry*) pLeft)->IsDirty() )
247 return true;
248 if( pRight && ((StgDirEntry*) pRight)->IsDirty() )
249 return true;
250 if( pDown && pDown->IsDirty() )
251 return true;
252 return false;
255 // Set up a stream.
257 void StgDirEntry::OpenStream( StgIo& rIo, bool bForceBig )
259 sal_Int32 nThreshold = (sal_uInt16) rIo.aHdr.GetThreshold();
260 delete pStgStrm;
261 if( !bForceBig && aEntry.GetSize() < nThreshold )
262 pStgStrm = new StgSmallStrm( rIo, *this );
263 else
264 pStgStrm = new StgDataStrm( rIo, *this );
265 if( bInvalid && aEntry.GetSize() )
267 // This entry has invalid data, so delete that data
268 SetSize( 0L );
269 // bRemoved = bInvalid = false;
271 nPos = 0;
274 // Close the open stream without committing. If the entry is marked as
275 // temporary, delete it.
276 // Do not delete pCurStrm here!
277 // (TLX:??? Zumindest pStgStrm muss deleted werden.)
279 void StgDirEntry::Close()
281 delete pTmpStrm;
282 pTmpStrm = NULL;
283 // nRefCnt = 0;
284 bInvalid = bTemp;
287 // Get the current stream size
289 sal_Int32 StgDirEntry::GetSize()
291 sal_Int32 n;
292 if( pTmpStrm )
293 n = pTmpStrm->GetSize();
294 else if( pCurStrm )
295 n = pCurStrm->GetSize();
296 else n = aEntry.GetSize();
297 return n;
300 // Set the stream size. This means also creating a temp stream.
302 bool StgDirEntry::SetSize( sal_Int32 nNewSize )
304 if (
305 !( nMode & STREAM_WRITE ) ||
306 (!bDirect && !pTmpStrm && !Strm2Tmp())
309 return false;
312 if( nNewSize < nPos )
313 nPos = nNewSize;
314 if( pTmpStrm )
316 pTmpStrm->SetSize( nNewSize );
317 pStgStrm->GetIo().SetError( pTmpStrm->GetError() );
318 return pTmpStrm->GetError() == SVSTREAM_OK;
320 else
322 OSL_ENSURE( pStgStrm, "The pointer may not be NULL!" );
323 if ( !pStgStrm )
324 return false;
326 bool bRes = false;
327 StgIo& rIo = pStgStrm->GetIo();
328 sal_Int32 nThreshold = rIo.aHdr.GetThreshold();
329 // ensure the correct storage stream!
330 StgStrm* pOld = NULL;
331 sal_uInt16 nOldSize = 0;
332 if( nNewSize >= nThreshold && pStgStrm->IsSmallStrm() )
334 pOld = pStgStrm;
335 nOldSize = (sal_uInt16) pOld->GetSize();
336 pStgStrm = new StgDataStrm( rIo, STG_EOF, 0 );
338 else if( nNewSize < nThreshold && !pStgStrm->IsSmallStrm() )
340 pOld = pStgStrm;
341 nOldSize = (sal_uInt16) nNewSize;
342 pStgStrm = new StgSmallStrm( rIo, STG_EOF, 0 );
344 // now set the new size
345 if( pStgStrm->SetSize( nNewSize ) )
347 // did we create a new stream?
348 if( pOld )
350 // if so, we probably need to copy the old data
351 if( nOldSize )
353 void* pBuf = new sal_uInt8[ nOldSize ];
354 pOld->Pos2Page( 0L );
355 pStgStrm->Pos2Page( 0L );
356 if( pOld->Read( pBuf, nOldSize )
357 && pStgStrm->Write( pBuf, nOldSize ) )
358 bRes = true;
359 delete[] static_cast<sal_uInt8*>(pBuf);
361 else
362 bRes = true;
363 if( bRes )
365 pOld->SetSize( 0 );
366 delete pOld;
367 pStgStrm->Pos2Page( nPos );
368 pStgStrm->SetEntry( *this );
370 else
372 pStgStrm->SetSize( 0 );
373 delete pStgStrm;
374 pStgStrm = pOld;
377 else
379 pStgStrm->Pos2Page( nPos );
380 bRes = true;
383 return bRes;
387 // Seek. On negative values, seek to EOF.
389 sal_Int32 StgDirEntry::Seek( sal_Int32 nNew )
391 if( pTmpStrm )
393 if( nNew < 0 )
394 nNew = pTmpStrm->GetSize();
395 nNew = pTmpStrm->Seek( nNew );
397 else if( pCurStrm )
399 if( nNew < 0 )
400 nNew = pCurStrm->GetSize();
401 nNew = pCurStrm->Seek( nNew );
403 else
405 OSL_ENSURE( pStgStrm, "The pointer may not be NULL!" );
406 if ( !pStgStrm )
407 return nPos;
409 sal_Int32 nSize = aEntry.GetSize();
411 if( nNew < 0 )
412 nNew = nSize;
414 // try to enlarge, the readonly streams should not allow this
415 if( nNew > nSize )
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!" );
420 return nPos;
422 else
423 return Seek( nNew );
425 pStgStrm->Pos2Page( nNew );
426 nNew = pStgStrm->GetPos();
429 return nPos = nNew;
432 // Read
434 sal_Int32 StgDirEntry::Read( void* p, sal_Int32 nLen )
436 if( nLen <= 0 )
437 return 0;
438 if( pTmpStrm )
439 nLen = pTmpStrm->Read( p, nLen );
440 else if( pCurStrm )
441 nLen = pCurStrm->Read( p, nLen );
442 else
444 OSL_ENSURE( pStgStrm, "The pointer may not be NULL!" );
445 if ( !pStgStrm )
446 return 0;
448 nLen = pStgStrm->Read( p, nLen );
451 nPos += nLen;
452 return nLen;
455 // Write
457 sal_Int32 StgDirEntry::Write( const void* p, sal_Int32 nLen )
459 if( nLen <= 0 || !( nMode & STREAM_WRITE ) )
460 return 0;
462 // Was this stream committed internally and reopened in direct mode?
463 if( bDirect && ( pCurStrm || pTmpStrm ) && !Tmp2Strm() )
464 return 0;
465 // Is this stream opened in transacted mode? Do we have to make a copy?
466 if( !bDirect && !pTmpStrm && !Strm2Tmp() )
467 return 0;
469 OSL_ENSURE( pStgStrm, "The pointer may not be NULL!" );
470 if ( !pStgStrm )
471 return 0;
473 if( pTmpStrm )
475 nLen = pTmpStrm->Write( p, nLen );
476 pStgStrm->GetIo().SetError( pTmpStrm->GetError() );
478 else
480 sal_Int32 nNew = nPos + nLen;
481 if( nNew > pStgStrm->GetSize() )
483 if( !SetSize( nNew ) )
484 return 0L;
485 pStgStrm->Pos2Page( nPos );
487 nLen = pStgStrm->Write( p, nLen );
489 nPos += nLen;
490 return nLen;
493 void StgDirEntry::Copy( BaseStorageStream& rDest )
495 sal_Int32 n = GetSize();
496 if( rDest.SetSize( n ) && n )
498 sal_uLong Pos = rDest.Tell();
499 sal_uInt8 aTempBytes[ 4096 ];
500 void* p = static_cast<void*>( aTempBytes );
501 Seek( 0L );
502 rDest.Seek( 0L );
503 while( n )
505 sal_Int32 nn = n;
506 if( nn > 4096 )
507 nn = 4096;
508 if( Read( p, nn ) != nn )
509 break;
510 if( sal::static_int_cast<sal_Int32>(rDest.Write( p, nn )) != nn )
511 break;
512 n -= nn;
514 rDest.Seek( Pos ); // ?! Seems to be undocumented !
518 // Commit this entry
520 bool StgDirEntry::Commit()
522 // OSL_ENSURE( nMode & STREAM_WRITE, "Trying to commit readonly stream!" );
524 aSave = aEntry;
525 bool bRes = true;
526 if( aEntry.GetType() == STG_STREAM )
528 if( pTmpStrm )
529 delete pCurStrm, pCurStrm = pTmpStrm, pTmpStrm = NULL;
530 if( bRemoved )
531 // Delete the stream if needed
532 if( pStgStrm )
533 pStgStrm->SetSize( 0 );
535 else if( aEntry.GetType() == STG_STORAGE && bDirect && bRes )
537 StgIterator aIter( *this );
538 for( StgDirEntry* p = aIter.First(); p && bRes; p = aIter.Next() )
539 bRes = p->Commit();
541 return bRes;
544 // Revert the entry
546 bool StgDirEntry::Revert()
548 aEntry = aSave;
549 switch( aEntry.GetType() )
551 case STG_STREAM:
552 if( pCurStrm )
553 delete pTmpStrm, pTmpStrm = pCurStrm, pCurStrm = NULL;
554 break;
555 case STG_STORAGE:
557 bool bSomeRenamed = false;
558 StgIterator aOIter( *this );
559 StgDirEntry* op = aOIter.First();
560 while( op )
562 op->aEntry = op->aSave;
563 op->bDirty = false;
564 bSomeRenamed = ( bSomeRenamed | op->bRenamed );
565 // Remove any new entries
566 if( op->bCreated )
568 op->bCreated = false;
569 op->Close();
570 op->bInvalid = true;
572 // Reactivate any removed entries
573 else if( op->bRemoved )
574 op->bRemoved = op->bInvalid = op->bTemp = false;
575 op = aOIter.Next();
577 // Resort all renamed entries
578 if( bSomeRenamed )
580 StgIterator aIter( *this );
581 StgDirEntry* p = aIter.First();
582 while( p )
584 if( p->bRenamed )
586 StgAvlNode::Move
587 ( (StgAvlNode**) &p->pUp->pDown,
588 (StgAvlNode**) &p->pUp->pDown, p );
589 p->bRenamed = false;
591 p = aIter.Next();
594 DelTemp( false );
595 break;
597 case STG_EMPTY:
598 case STG_LOCKBYTES:
599 case STG_PROPERTY:
600 case STG_ROOT:
601 break;
603 return true;
606 // Copy the stg stream to the temp stream
608 bool StgDirEntry::Strm2Tmp()
610 if( !pTmpStrm )
612 sal_uLong n = 0;
613 if( pCurStrm )
615 // It was already commited once
616 pTmpStrm = new StgTmpStrm;
617 if( pTmpStrm->GetError() == SVSTREAM_OK && pTmpStrm->Copy( *pCurStrm ) )
618 return true;
619 n = 1; // indicates error
621 else
623 n = aEntry.GetSize();
624 pTmpStrm = new StgTmpStrm( n );
625 if( pTmpStrm->GetError() == SVSTREAM_OK )
627 if( n )
629 OSL_ENSURE( pStgStrm, "The pointer may not be NULL!" );
630 if ( !pStgStrm )
631 return false;
633 sal_uInt8 aTempBytes[ 4096 ];
634 void* p = static_cast<void*>( aTempBytes );
635 pStgStrm->Pos2Page( 0L );
636 while( n )
638 sal_uLong nn = n;
639 if( nn > 4096 )
640 nn = 4096;
641 if( (sal_uLong) pStgStrm->Read( p, nn ) != nn )
642 break;
643 if( pTmpStrm->Write( p, nn ) != nn )
644 break;
645 n -= nn;
647 pStgStrm->Pos2Page( nPos );
648 pTmpStrm->Seek( nPos );
651 else
652 n = 1;
655 if( n )
657 OSL_ENSURE( pStgStrm, "The pointer may not be NULL!" );
658 if ( pStgStrm )
659 pStgStrm->GetIo().SetError( pTmpStrm->GetError() );
661 delete pTmpStrm;
662 pTmpStrm = NULL;
663 return false;
666 return true;
669 // Copy the temp stream to the stg stream during the final commit
671 bool StgDirEntry::Tmp2Strm()
673 // We did commit once, but have not written since then
674 if( !pTmpStrm )
675 pTmpStrm = pCurStrm, pCurStrm = NULL;
676 if( pTmpStrm )
678 OSL_ENSURE( pStgStrm, "The pointer may not be NULL!" );
679 if ( !pStgStrm )
680 return false;
681 sal_uLong n = pTmpStrm->GetSize();
682 StgStrm* pNewStrm;
683 StgIo& rIo = pStgStrm->GetIo();
684 sal_uLong nThreshold = (sal_uLong) rIo.aHdr.GetThreshold();
685 if( n < nThreshold )
686 pNewStrm = new StgSmallStrm( rIo, STG_EOF, 0 );
687 else
688 pNewStrm = new StgDataStrm( rIo, STG_EOF, 0 );
689 if( pNewStrm->SetSize( n ) )
691 sal_uInt8 p[ 4096 ];
692 pTmpStrm->Seek( 0L );
693 while( n )
695 sal_uLong nn = n;
696 if( nn > 4096 )
697 nn = 4096;
698 if( pTmpStrm->Read( p, nn ) != nn )
699 break;
700 if( (sal_uLong) pNewStrm->Write( p, nn ) != nn )
701 break;
702 n -= nn;
704 if( n )
706 pTmpStrm->Seek( nPos );
707 pStgStrm->GetIo().SetError( pTmpStrm->GetError() );
708 delete pNewStrm;
709 return false;
711 else
713 pStgStrm->SetSize( 0L );
714 delete pStgStrm;
715 pStgStrm = pNewStrm;
716 pNewStrm->SetEntry( *this );
717 pNewStrm->Pos2Page( nPos );
718 delete pTmpStrm;
719 delete pCurStrm;
720 pTmpStrm = pCurStrm = NULL;
721 aSave = aEntry;
725 return true;
728 // Check if the given entry is contained in this entry
730 bool StgDirEntry::IsContained( StgDirEntry* pStg )
732 if( aEntry.GetType() == STG_STORAGE )
734 StgIterator aIter( *this );
735 StgDirEntry* p = aIter.First();
736 while( p )
738 if( !p->aEntry.Compare( pStg->aEntry ) )
739 return false;
740 if( p->aEntry.GetType() == STG_STORAGE )
741 if( !p->IsContained( pStg ) )
742 return false;
743 p = aIter.Next();
746 return true;
749 // Invalidate all open entries by setting the RefCount to 0. If the bDel
750 // flag is set, also set the invalid flag to indicate deletion during the
751 // next dir stream flush.
753 void StgDirEntry::Invalidate( bool bDel )
755 // nRefCnt = 0;
756 if( bDel )
757 bRemoved = bInvalid = true;
758 switch( aEntry.GetType() )
760 case STG_STORAGE:
761 case STG_ROOT:
763 StgIterator aIter( *this );
764 for( StgDirEntry* p = aIter.First(); p; p = aIter.Next() )
765 p->Invalidate( bDel );
766 break;
768 default:
769 break;
773 ///////////////////////////// class StgDirStrm ////////////////////////////
775 // This specialized stream is the maintenance stream for the directory tree.
777 StgDirStrm::StgDirStrm( StgIo& r )
778 : StgDataStrm( r, r.aHdr.GetTOCStart(), -1 )
779 , pRoot( NULL )
780 , nEntries( 0 )
782 if( r.GetError() )
783 return;
784 nEntries = nPageSize / STGENTRY_SIZE;
785 if( nStart == STG_EOF )
787 StgEntry aRoot;
788 aRoot.Init();
789 aRoot.SetName( OUString("Root Entry") );
790 aRoot.SetType( STG_ROOT );
791 pRoot = new StgDirEntry( aRoot );
792 pRoot->SetDirty();
794 else
796 // temporarily use this instance as owner, so
797 // the TOC pages can be removed.
798 pEntry = (StgDirEntry*) this; // just for a bit pattern
799 SetupEntry( 0, pRoot );
800 pEntry = NULL;
804 StgDirStrm::~StgDirStrm()
806 delete pRoot;
809 // Recursively parse the directory tree during reading the TOC stream
811 void StgDirStrm::SetupEntry( sal_Int32 n, StgDirEntry* pUpper )
813 void* p = ( n == STG_FREE ) ? NULL : GetEntry( n );
814 if( p )
816 bool bOk(false);
817 StgDirEntry* pCur = new StgDirEntry( p, STGENTRY_SIZE, &bOk );
819 if( !bOk )
821 delete pCur;
822 rIo.SetError( SVSTREAM_GENERALERROR );
823 // an error occurred
824 return;
827 // better it is
828 if( !pUpper )
829 pCur->aEntry.SetType( STG_ROOT );
831 sal_Int32 nLeft = pCur->aEntry.GetLeaf( STG_LEFT );
832 sal_Int32 nRight = pCur->aEntry.GetLeaf( STG_RIGHT );
833 // substorage?
834 sal_Int32 nLeaf = STG_FREE;
835 if( pCur->aEntry.GetType() == STG_STORAGE || pCur->aEntry.GetType() == STG_ROOT )
837 nLeaf = pCur->aEntry.GetLeaf( STG_CHILD );
838 if (nLeaf != STG_FREE && nLeaf == n)
840 delete pCur;
841 rIo.SetError( SVSTREAM_GENERALERROR );
842 return;
846 if( nLeaf != 0 && nLeft != 0 && nRight != 0 )
848 //fdo#41642 Do we need to check full chain upwards for loops ?
849 if (pUpper)
851 if (pUpper->aEntry.GetLeaf(STG_CHILD) == nLeaf)
853 OSL_FAIL("Leaf node of upper StgDirEntry is same as current StgDirEntry's leaf node. Circular entry chain, discarding link");
854 delete pCur;
855 return;
858 StgDirEntry *pUpperUpper = pUpper->pUp;
859 if (pUpperUpper && pUpperUpper->aEntry.GetLeaf(STG_CHILD) == nLeaf)
861 OSL_FAIL("Leaf node of upper-upper StgDirEntry is same as current StgDirEntry's leaf node. Circular entry chain, discarding link");
862 delete pCur;
863 return;
867 if( StgAvlNode::Insert
868 ( (StgAvlNode**) ( pUpper ? &pUpper->pDown : &pRoot ), pCur ) )
870 pCur->pUp = pUpper;
871 pCur->ppRoot = &pRoot;
873 else
875 // bnc#682484: There are some really broken docs out there
876 // that contain duplicate entries in 'Directory' section
877 // so don't set the error flag here and just skip those
878 // (was: rIo.SetError( SVSTREAM_CANNOT_MAKE );)
879 delete pCur;
880 return;
882 SetupEntry( nLeft, pUpper );
883 SetupEntry( nRight, pUpper );
884 SetupEntry( nLeaf, pCur );
886 else
888 delete pCur;
893 // Extend or shrink the directory stream.
895 bool StgDirStrm::SetSize( sal_Int32 nBytes )
897 // Always allocate full pages
898 if ( nBytes < 0 )
899 nBytes = 0;
901 nBytes = ( ( nBytes + nPageSize - 1 ) / nPageSize ) * nPageSize;
902 return StgStrm::SetSize( nBytes );
905 // Save the TOC stream into a new substream after saving all data streams
907 bool StgDirStrm::Store()
909 if( !pRoot || !pRoot->IsDirty() )
910 return true;
911 if( !pRoot->StoreStreams( rIo ) )
912 return false;
913 // After writing all streams, the data FAT stream has changed,
914 // so we have to commit the root again
915 pRoot->Commit();
916 // We want a completely new stream, so fake an empty stream
917 sal_Int32 nOldStart = nStart; // save for later deletion
918 sal_Int32 nOldSize = nSize;
919 nStart = nPage = STG_EOF;
920 nSize = nPos = 0;
921 nOffset = 0;
922 // Delete all temporary entries
923 pRoot->DelTemp( false );
924 // set the entry numbers
925 sal_Int32 n = 0;
926 pRoot->Enum( n );
927 if( !SetSize( n * STGENTRY_SIZE ) )
929 nStart = nOldStart; nSize = nOldSize;
930 pRoot->RevertAll();
931 return false;
933 // set up the cache elements for the new stream
934 if( !Copy( STG_FREE, nSize ) )
936 pRoot->RevertAll();
937 return false;
939 // Write the data to the new stream
940 if( !pRoot->Store( *this ) )
942 pRoot->RevertAll();
943 return false;
945 // fill any remaining entries with empty data
946 sal_Int32 ne = nSize / STGENTRY_SIZE;
947 StgEntry aEmpty;
948 aEmpty.Init();
949 while( n < ne )
951 void* p = GetEntry( n++, true );
952 if( !p )
954 pRoot->RevertAll();
955 return false;
957 aEmpty.Store( p );
959 // Now we can release the old stream
960 pFat->FreePages( nOldStart, true );
961 rIo.aHdr.SetTOCStart( nStart );
962 return true;
965 // Get a dir entry.
967 void* StgDirStrm::GetEntry( sal_Int32 n, bool bDirty )
969 if( n < 0 )
970 return NULL;
972 n *= STGENTRY_SIZE;
973 if( n >= nSize )
974 return NULL;
975 return GetPtr( n, true, bDirty );
978 // Find a dir entry.
980 StgDirEntry* StgDirStrm::Find( StgDirEntry& rStg, const String& rName )
982 if( rStg.pDown )
984 StgEntry aEntry;
985 aEntry.Init();
986 if( !aEntry.SetName( rName ) )
988 rIo.SetError( SVSTREAM_GENERALERROR );
989 return NULL;
991 // Look in the directory attached to the entry
992 StgDirEntry aTest( aEntry );
993 return (StgDirEntry*) rStg.pDown->Find( &aTest );
995 else
996 return NULL;
999 // Create a new entry.
1001 StgDirEntry* StgDirStrm::Create
1002 ( StgDirEntry& rStg, const String& rName, StgEntryType eType )
1004 StgEntry aEntry;
1005 aEntry.Init();
1006 aEntry.SetType( eType );
1007 if( !aEntry.SetName( rName ) )
1009 rIo.SetError( SVSTREAM_GENERALERROR );
1010 return NULL;
1012 StgDirEntry* pRes = Find( rStg, rName );
1013 if( pRes )
1015 if( !pRes->bInvalid )
1017 rIo.SetError( SVSTREAM_CANNOT_MAKE );
1018 return NULL;
1020 pRes->bInvalid =
1021 pRes->bRemoved =
1022 pRes->bTemp = false;
1023 pRes->bCreated =
1024 pRes->bDirty = true;
1026 else
1028 pRes = new StgDirEntry( aEntry );
1029 if( StgAvlNode::Insert( (StgAvlNode**) &rStg.pDown, pRes ) )
1031 pRes->pUp = &rStg;
1032 pRes->ppRoot = &pRoot;
1033 pRes->bCreated =
1034 pRes->bDirty = true;
1036 else
1038 rIo.SetError( SVSTREAM_CANNOT_MAKE );
1039 delete pRes; pRes = NULL;
1042 return pRes;
1045 // Rename the given entry.
1047 bool StgDirStrm::Rename( StgDirEntry& rStg, const String& rOld, const String& rNew )
1049 StgDirEntry* p = Find( rStg, rOld );
1050 if( p )
1053 if( !StgAvlNode::Remove( (StgAvlNode**) &rStg.pDown, p, false ) )
1054 return false;
1055 p->aEntry.SetName( rNew );
1056 if( !StgAvlNode::Insert( (StgAvlNode**) &rStg.pDown, p ) )
1057 return false;
1058 p->bRenamed = p->bDirty = true;
1059 return true;
1061 else
1063 rIo.SetError( SVSTREAM_FILE_NOT_FOUND );
1064 return false;
1068 // Move the given entry to a different storage.
1070 bool StgDirStrm::Move( StgDirEntry& rStg1, StgDirEntry& rStg2, const String& rName )
1072 StgDirEntry* p = Find( rStg1, rName );
1073 if( p )
1075 if( !StgAvlNode::Move
1076 ( (StgAvlNode**) &rStg1.pDown, (StgAvlNode**) &rStg2.pDown, p ) )
1077 return false;
1078 p->bDirty = true;
1079 return true;
1081 else
1083 rIo.SetError( SVSTREAM_FILE_NOT_FOUND );
1084 return false;
1088 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */