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