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