xref: /aoo42x/main/sot/source/sdstor/stgdir.cxx (revision 046d9d1f)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sot.hxx"
26 
27 #include <string.h>     // memcpy()
28 
29 #include "sot/stg.hxx"
30 #include "stgelem.hxx"
31 #include "stgcache.hxx"
32 #include "stgstrms.hxx"
33 #include "stgdir.hxx"
34 #include "stgio.hxx"
35 
36 
37 //////////////////////////// class StgDirEntry /////////////////////////////
38 
39 // This class holds the dir entry data and maintains dirty flags for both
40 // the entry and the data.
41 
42 // Transacted mode for streams: On the first write, a temp stream pTmpStrm
43 // is created and operated on. A commit moves pTmpStrm to pCurStrm, which
44 // is used for subsequent reads. A new write creates a new copy of pTmpStrm
45 // based on pCurStrm. Reverting throws away pTmpStrm.
46 // Transacted mode for storages: A copy of the dir ents is kept in aSave.
47 // Committing means copying aEntry to aSave. Reverting means to copy aSave
48 // to aEntry, delete newly created entries and to reactivate removed entries.
49 
50 // Problem der Implementation: Keine Hierarchischen commits. Daher nur
51 // insgesamt transaktionsorientert oder direkt.
52 
53 StgDirEntry::StgDirEntry( const void* pFrom, sal_Bool * pbOk ) : StgAvlNode()
54 {
55 	*pbOk = aEntry.Load( pFrom );
56 
57 	InitMembers();
58 }
59 
60 StgDirEntry::StgDirEntry( const StgEntry& r ) : StgAvlNode(), aEntry( r )
61 {
62 	InitMembers();
63 }
64 
65 // Helper for all ctors
66 
67 void StgDirEntry::InitMembers()
68 {
69 	aSave 		= aEntry;
70     pUp 		=
71 	pDown  		= NULL;
72     ppRoot 		= NULL;
73 	pStgStrm 	= NULL;
74 	pCurStrm	=
75 	pTmpStrm	= NULL;
76 	nPos		=
77 	nEntry 		=
78 	nRefCnt 	= 0;
79 	nMode  		= STREAM_READ;
80 	bDirect 	= sal_True;
81 	bInvalid	=
82 	bCreated	=
83 	bRenamed 	=
84 	bRemoved	=
85 	bTemp  		=
86 	bDirty 		=
87 	bZombie     = sal_False;
88 }
89 
90 StgDirEntry::~StgDirEntry()
91 {
92 	Close();
93 	delete pCurStrm;
94 	delete pStgStrm;
95 	delete pDown;
96 }
97 
98 // Comparison function
99 
100 short StgDirEntry::Compare( const StgAvlNode* p ) const
101 {
102     const StgDirEntry* pEntry = (const StgDirEntry*) p;
103     return aEntry.Compare( pEntry->aEntry );
104 }
105 
106 // Enumerate the entry numbers.
107 // n is incremented to show the total # of entries.
108 // These number are later used as page numbers when storing
109 // the TOC tree into the TOC stream. Remember that aSave is
110 // stored, not aEntry.
111 
112 void StgDirEntry::Enum( sal_Int32& n )
113 {
114     sal_Int32 nLeft = STG_FREE, nRight = STG_FREE, nDown = STG_FREE;
115     nEntry = n++;
116     if( pLeft )
117     {
118         ((StgDirEntry*) pLeft)->Enum( n ); nLeft = ((StgDirEntry*) pLeft)->nEntry;
119     }
120     if( pRight )
121     {
122         ((StgDirEntry*) pRight)->Enum( n ); nRight = ((StgDirEntry*) pRight)->nEntry;
123     }
124     if( pDown )
125     {
126         pDown->Enum( n ); nDown = pDown->nEntry;
127     }
128     aSave.SetLeaf( STG_LEFT, nLeft );
129     aSave.SetLeaf( STG_RIGHT, nRight );
130     aSave.SetLeaf( STG_CHILD, nDown );
131 }
132 
133 // Delete all temporary entries before writing the TOC stream.
134 // Until now Deltem is never called with bForce True
135 
136 void StgDirEntry::DelTemp( sal_Bool bForce )
137 {
138 	if( pLeft )
139         ((StgDirEntry*) pLeft)->DelTemp( sal_False );
140     if( pRight )
141         ((StgDirEntry*) pRight)->DelTemp( sal_False );
142     if( pDown )
143 	{
144 		// If the storage is dead, of course all elements are dead, too
145 		if( bInvalid && aEntry.GetType() == STG_STORAGE )
146 			bForce = sal_True;
147         pDown->DelTemp( bForce );
148 	}
149 	if( ( bForce || bInvalid )
150 	 && ( aEntry.GetType() != STG_ROOT ) /* && ( nRefCnt <= 1 ) */ )
151 	{
152 		Close();
153 		if( pUp )
154 		{
155 			// this deletes the element if refcnt == 0!
156 			sal_Bool bDel = nRefCnt == 0;
157 		    StgAvlNode::Remove( (StgAvlNode**) &pUp->pDown, this, bDel );
158 			if( !bDel )
159 			{
160 				pLeft = pRight = pDown = 0;
161 				bInvalid = bZombie = sal_True;
162 			}
163 		}
164 	}
165 }
166 
167 // Save the tree into the given dir stream
168 
169 sal_Bool StgDirEntry::Store( StgDirStrm& rStrm )
170 {
171 	void* pEntry = rStrm.GetEntry( nEntry, sal_True );
172     if( !pEntry )
173         return sal_False;
174 	// Do not store the current (maybe not commited) entry
175 	aSave.Store( pEntry );
176     if( pLeft )
177         if( !((StgDirEntry*) pLeft)->Store( rStrm ) )
178             return sal_False;
179     if( pRight )
180         if( !((StgDirEntry*) pRight)->Store( rStrm ) )
181             return sal_False;
182     if( pDown )
183         if( !pDown->Store( rStrm ) )
184             return sal_False;
185     return sal_True;
186 }
187 
188 sal_Bool StgDirEntry::StoreStream( StgIo& rIo )
189 {
190 	if( aEntry.GetType() == STG_STREAM || aEntry.GetType() == STG_ROOT )
191 	{
192 		if( bInvalid )
193 		{
194 			// Delete the stream if needed
195 			if( !pStgStrm )
196 			{
197 				OpenStream( rIo );
198 				delete pStgStrm, pStgStrm = NULL;
199 			}
200 			else
201 				pStgStrm->SetSize( 0 );
202 		}
203 		// or write the data stream
204 		else if( !Tmp2Strm() )
205 			return sal_False;
206 	}
207 	return sal_True;
208 }
209 
210 // Save all dirty streams
211 
212 sal_Bool StgDirEntry::StoreStreams( StgIo& rIo )
213 {
214 	if( !StoreStream( rIo ) )
215 		return sal_False;
216 	if( pLeft )
217         if( !((StgDirEntry*) pLeft)->StoreStreams( rIo ) )
218             return sal_False;
219     if( pRight )
220         if( !((StgDirEntry*) pRight)->StoreStreams( rIo ) )
221             return sal_False;
222     if( pDown )
223         if( !pDown->StoreStreams( rIo ) )
224             return sal_False;
225     return sal_True;
226 }
227 
228 // Revert all directory entries after failure to write the TOC stream
229 
230 void StgDirEntry::RevertAll()
231 {
232 	aEntry = aSave;
233 	if( pLeft )
234         ((StgDirEntry*) pLeft)->RevertAll();
235     if( pRight )
236         ((StgDirEntry*) pRight)->RevertAll();
237     if( pDown )
238         pDown->RevertAll();
239 }
240 
241 // Look if any element of the tree is dirty
242 
243 sal_Bool StgDirEntry::IsDirty()
244 {
245     if( bDirty || bInvalid )
246         return sal_True;
247     if( pLeft && ((StgDirEntry*) pLeft)->IsDirty() )
248         return sal_True;
249     if( pRight && ((StgDirEntry*) pRight)->IsDirty() )
250         return sal_True;
251     if( pDown && pDown->IsDirty() )
252         return sal_True;
253     return sal_False;
254 }
255 
256 // Set up a stream.
257 
258 void StgDirEntry::OpenStream( StgIo& rIo, sal_Bool bForceBig )
259 {
260 	sal_Int32 nThreshold = (sal_uInt16) rIo.aHdr.GetThreshold();
261 	delete pStgStrm;
262 	if( !bForceBig && aEntry.GetSize() < nThreshold )
263 		pStgStrm = new StgSmallStrm( rIo, this );
264 	else
265 		pStgStrm = new StgDataStrm( rIo, this );
266 	if( bInvalid && aEntry.GetSize() )
267 	{
268 		// This entry has invalid data, so delete that data
269 		SetSize( 0L );
270 //		bRemoved = bInvalid = sal_False;
271 	}
272 	nPos = 0;
273 }
274 
275 // Close the open stream without committing. If the entry is marked as
276 // temporary, delete it.
277 // Do not delete pCurStrm here!
278 // (TLX:??? Zumindest pStgStrm muss deleted werden.)
279 
280 void StgDirEntry::Close()
281 {
282 	delete pTmpStrm;
283 	pTmpStrm = NULL;
284 //	nRefCnt	 = 0;
285 	bInvalid = bTemp;
286 }
287 
288 // Get the current stream size
289 
290 sal_Int32 StgDirEntry::GetSize()
291 {
292 	sal_Int32 n;
293 	if( pTmpStrm )
294 		n = pTmpStrm->GetSize();
295 	else if( pCurStrm )
296 		n = pCurStrm->GetSize();
297 	else n = aEntry.GetSize();
298 	return n;
299 }
300 
301 // Set the stream size. This means also creating a temp stream.
302 
303 sal_Bool StgDirEntry::SetSize( sal_Int32 nNewSize )
304 {
305 	if (
306 	     !( nMode & STREAM_WRITE ) ||
307 	     (!bDirect && !pTmpStrm && !Strm2Tmp())
308 	   )
309 	{
310 		return sal_False;
311 	}
312 
313 	if( nNewSize < nPos )
314 		nPos = nNewSize;
315 	if( pTmpStrm )
316 	{
317 		pTmpStrm->SetSize( nNewSize );
318 		pStgStrm->GetIo().SetError( pTmpStrm->GetError() );
319 		return sal_Bool( pTmpStrm->GetError() == SVSTREAM_OK );
320 	}
321 	else
322 	{
323 		sal_Bool bRes = sal_False;
324 		StgIo& rIo = pStgStrm->GetIo();
325 		sal_Int32 nThreshold = rIo.aHdr.GetThreshold();
326 		// ensure the correct storage stream!
327 		StgStrm* pOld = NULL;
328 		sal_uInt16 nOldSize = 0;
329 		if( nNewSize >= nThreshold && pStgStrm->IsSmallStrm() )
330 		{
331 			pOld = pStgStrm;
332 			nOldSize = (sal_uInt16) pOld->GetSize();
333 			pStgStrm = new StgDataStrm( rIo, STG_EOF, 0 );
334 		}
335 		else if( nNewSize < nThreshold && !pStgStrm->IsSmallStrm() )
336 		{
337 			pOld = pStgStrm;
338 			nOldSize = (sal_uInt16) nNewSize;
339 			pStgStrm = new StgSmallStrm( rIo, STG_EOF, 0 );
340 		}
341 		// now set the new size
342 		if( pStgStrm->SetSize( nNewSize ) )
343 		{
344 			// did we create a new stream?
345 			if( pOld )
346 			{
347 				// if so, we probably need to copy the old data
348 				if( nOldSize )
349 				{
350 					void* pBuf = new sal_uInt8[ nOldSize ];
351 					pOld->Pos2Page( 0L );
352 					pStgStrm->Pos2Page( 0L );
353 					if( pOld->Read( pBuf, nOldSize )
354 					 && pStgStrm->Write( pBuf, nOldSize ) )
355 						bRes = sal_True;
356 					delete[] static_cast<sal_uInt8*>(pBuf);
357 				}
358 				else
359 					bRes = sal_True;
360 				if( bRes )
361 				{
362 					pOld->SetSize( 0 );
363 					delete pOld;
364 					pStgStrm->Pos2Page( nPos );
365 					pStgStrm->SetEntry( *this );
366 				}
367 				else
368 				{
369 					pStgStrm->SetSize( 0 );
370 					delete pStgStrm;
371 					pStgStrm = pOld;
372 				}
373 			}
374 			else
375 			{
376 				pStgStrm->Pos2Page( nPos );
377 				bRes = sal_True;
378 			}
379 		}
380 		return bRes;
381 	}
382 }
383 
384 // Seek. On negative values, seek to EOF.
385 
386 sal_Int32 StgDirEntry::Seek( sal_Int32 nNew )
387 {
388 	if( pTmpStrm )
389 	{
390 		if( nNew < 0 )
391 			nNew = pTmpStrm->GetSize();
392 		nNew = pTmpStrm->Seek( nNew );
393 	}
394 	else if( pCurStrm )
395 	{
396 		if( nNew < 0 )
397 			nNew = pCurStrm->GetSize();
398 		nNew = pCurStrm->Seek( nNew );
399 	}
400 	else
401 	{
402 		sal_Int32 nSize = aEntry.GetSize();
403 
404 		if( nNew < 0 )
405 			nNew = nSize;
406 
407 		// try to enlarge, the readonly streams should not allow this
408 		if( nNew > nSize )
409 		{
410 			if ( !( nMode & STREAM_WRITE ) || !SetSize( nNew ) )
411 			{
412 				OSL_ENSURE( nMode & STREAM_WRITE, "Trying to resize readonly stream by seeking, could be a wrong offset!" );
413 				return nPos;
414 			}
415 			else
416 				return Seek( nNew );
417 		}
418 		pStgStrm->Pos2Page( nNew );
419 		nNew = pStgStrm->GetPos();
420 	}
421 	return nPos = nNew;
422 }
423 
424 // Read
425 
426 sal_Int32 StgDirEntry::Read( void* p, sal_Int32 nLen )
427 {
428 	if( nLen <= 0 )
429 		return 0;
430 	if( pTmpStrm )
431 		nLen = pTmpStrm->Read( p, nLen );
432 	else if( pCurStrm )
433 		nLen = pCurStrm->Read( p, nLen );
434 	else
435 		nLen = pStgStrm->Read( p, nLen );
436 	nPos += nLen;
437 	return nLen;
438 }
439 
440 // Write
441 
442 sal_Int32 StgDirEntry::Write( const void* p, sal_Int32 nLen )
443 {
444 	if( nLen <= 0 || !( nMode & STREAM_WRITE ) )
445 		return 0;
446 
447 	// Was this stream committed internally and reopened in direct mode?
448 	if( bDirect && ( pCurStrm || pTmpStrm ) && !Tmp2Strm() )
449 		return 0;
450 	// Is this stream opened in transacted mode? Do we have to make a copy?
451 	if( !bDirect && !pTmpStrm && !Strm2Tmp() )
452 		return 0;
453 	if( pTmpStrm )
454 	{
455 		nLen = pTmpStrm->Write( p, nLen );
456 		pStgStrm->GetIo().SetError( pTmpStrm->GetError() );
457 	}
458 	else
459 	{
460 		sal_Int32 nNew = nPos + nLen;
461 		if( nNew > pStgStrm->GetSize() )
462 		{
463 			if( !SetSize( nNew ) )
464 				return 0L;
465 			pStgStrm->Pos2Page( nPos );
466 		}
467 		nLen = pStgStrm->Write( p, nLen );
468 	}
469 	nPos += nLen;
470 	return nLen;
471 }
472 
473 // Copy the data of one entry into another entry.
474 
475 void StgDirEntry::Copy( StgDirEntry& rDest )
476 {
477 	sal_Int32 n = GetSize();
478 	if( rDest.SetSize( n ) && n )
479 	{
480 		sal_uInt8 aTempBytes[ 4096 ];
481 		void* p = static_cast<void*>( aTempBytes );
482 		Seek( 0L );
483 		rDest.Seek( 0L );
484 		while( n )
485 		{
486 			sal_Int32 nn = n;
487 			if( nn > 4096 )
488 				nn = 4096;
489 			if( Read( p, nn ) != nn )
490 				break;
491 			if( rDest.Write( p, nn ) != nn )
492 				break;
493 			n -= nn;
494 		}
495 	}
496 }
497 
498 void StgDirEntry::Copy( BaseStorageStream& rDest )
499 {
500 	sal_Int32 n = GetSize();
501 	if( rDest.SetSize( n ) && n )
502 	{
503         sal_uLong Pos = rDest.Tell();
504 		sal_uInt8 aTempBytes[ 4096 ];
505 		void* p = static_cast<void*>( aTempBytes );
506 		Seek( 0L );
507 		rDest.Seek( 0L );
508 		while( n )
509 		{
510 			sal_Int32 nn = n;
511 			if( nn > 4096 )
512 				nn = 4096;
513 			if( Read( p, nn ) != nn )
514 				break;
515 			if( sal::static_int_cast<sal_Int32>(rDest.Write( p, nn )) != nn )
516 				break;
517 			n -= nn;
518 		}
519         rDest.Seek( Pos );             // ?! Seems to be undocumented !
520 	}
521 }
522 
523 // Commit this entry
524 
525 sal_Bool StgDirEntry::Commit()
526 {
527 	// OSL_ENSURE( nMode & STREAM_WRITE, "Trying to commit readonly stream!" );
528 
529 	aSave = aEntry;
530 	sal_Bool bRes = sal_True;
531 	if( aEntry.GetType() == STG_STREAM )
532 	{
533 		if( pTmpStrm )
534 			delete pCurStrm, pCurStrm = pTmpStrm, pTmpStrm = NULL;
535 		if( bRemoved )
536 			// Delete the stream if needed
537 			if( pStgStrm )
538 				pStgStrm->SetSize( 0 );
539 	}
540 	else if( aEntry.GetType() == STG_STORAGE && bDirect && bRes )
541 	{
542 		StgIterator aIter( *this );
543 		for( StgDirEntry* p = aIter.First(); p && bRes; p = aIter.Next() )
544 			bRes = p->Commit();
545 	}
546 	return bRes;
547 }
548 
549 // Revert the entry
550 
551 sal_Bool StgDirEntry::Revert()
552 {
553 	aEntry = aSave;
554     switch( aEntry.GetType() )
555 	{
556 		case STG_STREAM:
557 			if( pCurStrm )
558 				delete pTmpStrm, pTmpStrm = pCurStrm, pCurStrm = NULL;
559 			break;
560 		case STG_STORAGE:
561 		{
562 			sal_Bool bSomeRenamed = sal_False;
563 			StgIterator aOIter( *this );
564 		    StgDirEntry* op = aOIter.First();
565 			while( op )
566 		    {
567 				op->aEntry = op->aSave;
568 				op->bDirty = sal_False;
569 				bSomeRenamed = sal_Bool( bSomeRenamed | op->bRenamed );
570 				// Remove any new entries
571 				if( op->bCreated )
572 				{
573 					op->bCreated = sal_False;
574 					op->Close();
575 					op->bInvalid = sal_True;
576 				}
577 				// Reactivate any removed entries
578 				else if( op->bRemoved )
579 					op->bRemoved = op->bInvalid = op->bTemp = sal_False;
580 				op = aOIter.Next();
581 			}
582 			// Resort all renamed entries
583 			if( bSomeRenamed )
584 			{
585 				StgIterator aIter( *this );
586 			    StgDirEntry* p = aIter.First();
587 				while( p )
588 			    {
589 					if( p->bRenamed )
590 					{
591 						StgAvlNode::Move
592 							( (StgAvlNode**) &p->pUp->pDown,
593 							  (StgAvlNode**) &p->pUp->pDown, p );
594 						p->bRenamed = sal_False;
595 					}
596 					p = aIter.Next();
597 				}
598 			}
599 			DelTemp( sal_False );
600 			break;
601 		}
602         case STG_EMPTY:
603         case STG_LOCKBYTES:
604         case STG_PROPERTY:
605         case STG_ROOT:
606          break;
607 	}
608     return sal_True;
609 }
610 
611 // Copy the stg stream to the temp stream
612 
613 sal_Bool StgDirEntry::Strm2Tmp()
614 {
615 	if( !pTmpStrm )
616 	{
617 		sal_uLong n = 0;
618 		if( pCurStrm )
619 		{
620 			// It was already commited once
621 			pTmpStrm = new StgTmpStrm;
622 			if( pTmpStrm->GetError() == SVSTREAM_OK && pTmpStrm->Copy( *pCurStrm ) )
623 				return sal_True;
624 			n = 1;	// indicates error
625 		}
626 		else
627 		{
628 			n = aEntry.GetSize();
629 			pTmpStrm = new StgTmpStrm( n );
630 			if( pTmpStrm->GetError() == SVSTREAM_OK )
631 			{
632 				if( n )
633 				{
634 					sal_uInt8 aTempBytes[ 4096 ];
635 					void* p = static_cast<void*>( aTempBytes );
636 					pStgStrm->Pos2Page( 0L );
637 					while( n )
638 					{
639 						sal_uLong nn = n;
640 						if( nn > 4096 )
641 							nn = 4096;
642 						if( (sal_uLong) pStgStrm->Read( p, nn ) != nn )
643 							break;
644 						if( pTmpStrm->Write( p, nn ) != nn )
645 							break;
646 						n -= nn;
647 					}
648 					pStgStrm->Pos2Page( nPos );
649 					pTmpStrm->Seek( nPos );
650 				}
651 			}
652 			else
653 				n = 1;
654 		}
655 		if( n )
656 		{
657 			pStgStrm->GetIo().SetError( pTmpStrm->GetError() );
658 			delete pTmpStrm;
659 			pTmpStrm = NULL;
660 			return sal_False;
661 		}
662 	}
663 	return sal_True;
664 }
665 
666 // Copy the temp stream to the stg stream during the final commit
667 
668 sal_Bool StgDirEntry::Tmp2Strm()
669 {
670 	// We did commit once, but have not written since then
671 	if( !pTmpStrm )
672 		pTmpStrm = pCurStrm, pCurStrm = NULL;
673 	if( pTmpStrm )
674 	{
675 		sal_uLong n = pTmpStrm->GetSize();
676 		StgStrm* pNewStrm;
677 		StgIo& rIo = pStgStrm->GetIo();
678 		sal_uLong nThreshold = (sal_uLong) rIo.aHdr.GetThreshold();
679 		if( n < nThreshold )
680 			pNewStrm = new StgSmallStrm( rIo, STG_EOF, 0 );
681 		else
682 			pNewStrm = new StgDataStrm( rIo, STG_EOF, 0 );
683 		if( pNewStrm->SetSize( n ) )
684 		{
685 			sal_uInt8 p[ 4096 ];
686 			pTmpStrm->Seek( 0L );
687 			while( n )
688 			{
689 				sal_uLong nn = n;
690 				if( nn > 4096 )
691 					nn = 4096;
692 				if( pTmpStrm->Read( p, nn ) != nn )
693 					break;
694 				if( (sal_uLong) pNewStrm->Write( p, nn ) != nn )
695 					break;
696 				n -= nn;
697 			}
698 			if( n )
699 			{
700 				pTmpStrm->Seek( nPos );
701 				pStgStrm->GetIo().SetError( pTmpStrm->GetError() );
702 				delete pNewStrm;
703 				return sal_False;
704 			}
705 			else
706 			{
707 				pStgStrm->SetSize( 0L );
708 				delete pStgStrm;
709 				pStgStrm = pNewStrm;
710 				pNewStrm->SetEntry( *this );
711 				pNewStrm->Pos2Page( nPos );
712 				delete pTmpStrm;
713 				delete pCurStrm;
714 				pTmpStrm = pCurStrm = NULL;
715 				aSave = aEntry;
716 			}
717 		}
718 	}
719 	return sal_True;
720 }
721 
722 // Check if the given entry is contained in this entry
723 
724 sal_Bool StgDirEntry::IsContained( StgDirEntry* pStg )
725 {
726     if( aEntry.GetType() == STG_STORAGE )
727     {
728         StgIterator aIter( *this );
729         StgDirEntry* p = aIter.First();
730         while( p )
731         {
732             if( !p->aEntry.Compare( pStg->aEntry ) )
733                 return sal_False;
734             if( p->aEntry.GetType() == STG_STORAGE )
735                 if( !p->IsContained( pStg ) )
736                     return sal_False;
737             p = aIter.Next();
738         }
739     }
740     return sal_True;
741 }
742 
743 // Invalidate all open entries by setting the RefCount to 0. If the bDel
744 // flag is set, also set the invalid flag to indicate deletion during the
745 // next dir stream flush.
746 
747 void StgDirEntry::Invalidate( sal_Bool bDel )
748 {
749 //	nRefCnt = 0;
750 	if( bDel )
751 		bRemoved = bInvalid = sal_True;
752 	switch( aEntry.GetType() )
753 	{
754 		case STG_STORAGE:
755 		case STG_ROOT:
756 		{
757 			StgIterator aIter( *this );
758 			for( StgDirEntry* p = aIter.First(); p; p = aIter.Next() )
759 				p->Invalidate( bDel );
760 			break;
761 		}
762         default:
763             break;
764 	}
765 }
766 
767 ///////////////////////////// class StgDirStrm ////////////////////////////
768 
769 // This specialized stream is the maintenance stream for the directory tree.
770 
771 StgDirStrm::StgDirStrm( StgIo& r )
772           : StgDataStrm( r, r.aHdr.GetTOCStart(), -1 )
773 		  , pRoot( NULL )
774 		  , nEntries( 0 )
775 {
776 	if( r.GetError() )
777 		return;
778     nEntries = nPageSize / STGENTRY_SIZE;
779     if( nStart == STG_EOF )
780     {
781         StgEntry aRoot;
782         aRoot.Init();
783         aRoot.SetName( String::CreateFromAscii( RTL_CONSTASCII_STRINGPARAM( "Root Entry" ) ) );
784         aRoot.SetType( STG_ROOT );
785         pRoot = new StgDirEntry( aRoot );
786         pRoot->SetDirty();
787     }
788     else
789     {
790         // temporarily use this instance as owner, so
791         // the TOC pages can be removed.
792         pEntry = (StgDirEntry*) this; // just for a bit pattern
793         SetupEntry( 0, pRoot );
794         rIo.Revert( pEntry );
795         pEntry = NULL;
796     }
797 }
798 
799 StgDirStrm::~StgDirStrm()
800 {
801     delete pRoot;
802 }
803 
804 // Recursively parse the directory tree during reading the TOC stream
805 
806 void StgDirStrm::SetupEntry( sal_Int32 n, StgDirEntry* pUpper )
807 {
808     void* p = ( n == STG_FREE ) ? NULL : GetEntry( n );
809     if( p )
810     {
811         sal_Bool bOk(sal_False);
812         StgDirEntry* pCur = new StgDirEntry( p, &bOk );
813 
814         if( !bOk )
815         {
816             delete pCur;
817             rIo.SetError( SVSTREAM_GENERALERROR );
818             // an error occured
819             return;
820         }
821 
822         // better it is
823         if( !pUpper )
824             pCur->aEntry.SetType( STG_ROOT );
825 
826         sal_Int32 nLeft = pCur->aEntry.GetLeaf( STG_LEFT );
827         sal_Int32 nRight = pCur->aEntry.GetLeaf( STG_RIGHT );
828         // substorage?
829         sal_Int32 nLeaf = STG_FREE;
830         if( pCur->aEntry.GetType() == STG_STORAGE || pCur->aEntry.GetType() == STG_ROOT )
831         {
832             nLeaf = pCur->aEntry.GetLeaf( STG_CHILD );
833             if (nLeaf != STG_FREE && nLeaf == n)
834             {
835                 delete pCur;
836                 rIo.SetError( SVSTREAM_GENERALERROR );
837                 return;
838             }
839         }
840 
841         if( nLeaf != 0 && nLeft != 0 && nRight != 0 )
842         {
843             if( StgAvlNode::Insert
844                 ( (StgAvlNode**) ( pUpper ? &pUpper->pDown : &pRoot ), pCur ) )
845             {
846                 pCur->pUp    = pUpper;
847                 pCur->ppRoot = &pRoot;
848             }
849             else
850             {
851                 rIo.SetError( SVSTREAM_CANNOT_MAKE );
852                 delete pCur; pCur = NULL;
853                 return;
854             }
855             SetupEntry( nLeft, pUpper );
856             SetupEntry( nRight, pUpper );
857             SetupEntry( nLeaf, pCur );
858         }
859     }
860 }
861 
862 // Extend or shrink the directory stream.
863 
864 sal_Bool StgDirStrm::SetSize( sal_Int32 nBytes )
865 {
866     // Always allocate full pages
867     nBytes = ( ( nBytes + nPageSize - 1 ) / nPageSize ) * nPageSize;
868     return StgStrm::SetSize( nBytes );
869 }
870 
871 // Save the TOC stream into a new substream after saving all data streams
872 
873 sal_Bool StgDirStrm::Store()
874 {
875     if( !pRoot->IsDirty() )
876         return sal_True;
877 	if( !pRoot->StoreStreams( rIo ) )
878 		return sal_False;
879 	// After writing all streams, the data FAT stream has changed,
880 	// so we have to commit the root again
881 	pRoot->Commit();
882 	// We want a completely new stream, so fake an empty stream
883     sal_Int32 nOldStart = nStart;       // save for later deletion
884     sal_Int32 nOldSize  = nSize;
885     nStart = nPage = STG_EOF;
886     nSize  = nPos = 0;
887     nOffset = 0;
888 	// Delete all temporary entries
889 	pRoot->DelTemp( sal_False );
890     // set the entry numbers
891     sal_Int32 n = 0;
892     pRoot->Enum( n );
893     if( !SetSize( n * STGENTRY_SIZE ) )
894     {
895         nStart = nOldStart; nSize = nOldSize;
896 		pRoot->RevertAll();
897         return sal_False;
898     }
899     // set up the cache elements for the new stream
900     if( !Copy( STG_FREE, nSize ) )
901 	{
902 		pRoot->RevertAll();
903         return sal_False;
904 	}
905     // Write the data to the new stream
906     if( !pRoot->Store( *this ) )
907 	{
908 		pRoot->RevertAll();
909         return sal_False;
910 	}
911 	// fill any remaining entries with empty data
912     sal_Int32 ne = nSize / STGENTRY_SIZE;
913 	StgEntry aEmpty;
914 	aEmpty.Init();
915     while( n < ne )
916     {
917         void* p = GetEntry( n++, sal_True );
918         if( !p )
919 		{
920 			pRoot->RevertAll();
921             return sal_False;
922 		}
923 		aEmpty.Store( p );
924     }
925     // Now we can release the old stream
926     pFat->FreePages( nOldStart, sal_True );
927     rIo.aHdr.SetTOCStart( nStart );
928 	return sal_True;
929 }
930 
931 // Get a dir entry.
932 
933 void* StgDirStrm::GetEntry( sal_Int32 n, sal_Bool bDirty )
934 {
935     if( n < 0 )
936         return NULL;
937 
938     n *= STGENTRY_SIZE;
939     if( n < 0 && n >= nSize )
940         return NULL;
941     return GetPtr( n, sal_True, bDirty );
942 }
943 
944 // Find a dir entry.
945 
946 StgDirEntry* StgDirStrm::Find( StgDirEntry& rStg, const String& rName )
947 {
948     if( rStg.pDown )
949     {
950         StgEntry aEntry;
951         aEntry.Init();
952         if( !aEntry.SetName( rName ) )
953         {
954             rIo.SetError( SVSTREAM_GENERALERROR );
955             return NULL;
956         }
957         // Look in the directory attached to the entry
958         StgDirEntry aTest( aEntry );
959         return (StgDirEntry*) rStg.pDown->Find( &aTest );
960     }
961     else
962         return NULL;
963 }
964 
965 // Create a new entry.
966 
967 StgDirEntry* StgDirStrm::Create
968 	( StgDirEntry& rStg, const String& rName, StgEntryType eType )
969 {
970 	StgEntry aEntry;
971     aEntry.Init();
972 	aEntry.SetType( eType );
973     if( !aEntry.SetName( rName ) )
974     {
975         rIo.SetError( SVSTREAM_GENERALERROR );
976         return NULL;
977     }
978     StgDirEntry* pRes = Find( rStg, rName );
979 	if( pRes )
980 	{
981 		if( !pRes->bInvalid )
982 		{
983 			rIo.SetError( SVSTREAM_CANNOT_MAKE );
984 			return NULL;
985 		}
986 		pRes->bInvalid =
987 		pRes->bRemoved =
988 		pRes->bTemp    = sal_False;
989 		pRes->bCreated =
990         pRes->bDirty   = sal_True;
991 	}
992 	else
993 	{
994 		pRes = new StgDirEntry( aEntry );
995 	    if( StgAvlNode::Insert( (StgAvlNode**) &rStg.pDown, pRes ) )
996 	    {
997 			pRes->pUp    = &rStg;
998 	        pRes->ppRoot = &pRoot;
999 			pRes->bCreated =
1000 	        pRes->bDirty = sal_True;
1001 	    }
1002 	    else
1003 	    {
1004 	        rIo.SetError( SVSTREAM_CANNOT_MAKE );
1005 	        delete pRes; pRes = NULL;
1006 	    }
1007 	}
1008 	return pRes;
1009 }
1010 
1011 // Rename the given entry.
1012 
1013 sal_Bool StgDirStrm::Rename( StgDirEntry& rStg, const String& rOld, const String& rNew )
1014 {
1015     StgDirEntry* p = Find( rStg, rOld );
1016     if( p )
1017     {
1018 
1019 		if( !StgAvlNode::Remove( (StgAvlNode**) &rStg.pDown, p, sal_False ) )
1020 			return sal_False;
1021 		p->aEntry.SetName( rNew );
1022 		if( !StgAvlNode::Insert( (StgAvlNode**) &rStg.pDown, p ) )
1023 			return sal_False;
1024 		p->bRenamed = p->bDirty   = sal_True;
1025 		return sal_True;
1026     }
1027     else
1028     {
1029         rIo.SetError( SVSTREAM_FILE_NOT_FOUND );
1030         return sal_False;
1031     }
1032 }
1033 
1034 // Move the given entry to a different storage.
1035 
1036 sal_Bool StgDirStrm::Move( StgDirEntry& rStg1, StgDirEntry& rStg2, const String& rName )
1037 {
1038     StgDirEntry* p = Find( rStg1, rName );
1039     if( p )
1040 	{
1041 		if( !StgAvlNode::Move
1042 			( (StgAvlNode**) &rStg1.pDown, (StgAvlNode**) &rStg2.pDown, p ) )
1043 			return sal_False;
1044 		p->bDirty = sal_True;
1045 		return sal_True;
1046 	}
1047 	else
1048     {
1049         rIo.SetError( SVSTREAM_FILE_NOT_FOUND );
1050         return sal_False;
1051     }
1052 }
1053 
1054