xref: /trunk/main/sot/source/sdstor/stgdir.cxx (revision 20a3ff3d)
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 
StgDirEntry(const void * pBuffer,sal_uInt32 nBufferLen,sal_Bool * pbOk)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 
StgDirEntry(const StgEntry & r)60 StgDirEntry::StgDirEntry( const StgEntry& r ) : StgAvlNode(), aEntry( r )
61 {
62 	InitMembers();
63 }
64 
65 // Helper for all ctors
66 
InitMembers()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 
~StgDirEntry()90 StgDirEntry::~StgDirEntry()
91 {
92 	Close();
93 	delete pCurStrm;
94 	delete pStgStrm;
95 	delete pDown;
96 }
97 
98 // Comparison function
99 
Compare(const StgAvlNode * p) const100 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 
Enum(sal_Int32 & n)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 
DelTemp(sal_Bool bForce)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 
Store(StgDirStrm & rStrm)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 
StoreStream(StgIo & rIo)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 
StoreStreams(StgIo & rIo)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 
RevertAll()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 
IsDirty()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 
OpenStream(StgIo & rIo,sal_Bool bForceBig)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 
Close()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 
GetSize()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 
SetSize(sal_Int32 nNewSize)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 
Seek(sal_Int32 nNew)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 
Read(void * p,sal_Int32 nLen)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 
Write(const void * p,sal_Int32 nLen)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 
Copy(StgDirEntry & rDest)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 
Copy(BaseStorageStream & rDest)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 
Commit()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 
Revert()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 
Strm2Tmp()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 
Tmp2Strm()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 
IsContained(StgDirEntry * pStg)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 
Invalidate(sal_Bool bDel)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 
StgDirStrm(StgIo & r)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, nSize/STGENTRY_SIZE, 0);
831         rIo.Revert( pEntry );
832         pEntry = NULL;
833     }
834 }
835 
~StgDirStrm()836 StgDirStrm::~StgDirStrm()
837 {
838     delete pRoot;
839 }
840 
841 // Recursively parse the directory tree during reading the TOC stream
842 
SetupEntry(const sal_Int32 n,StgDirEntry * pUpper,const sal_Int32 nEntryCount,const sal_Int32 nDepth)843 void StgDirStrm::SetupEntry (
844     const sal_Int32 n,
845     StgDirEntry* pUpper,
846     const sal_Int32 nEntryCount,
847     const sal_Int32 nDepth)
848 {
849     if (nDepth > nEntryCount)
850     {
851         // Tree grew higher than there are different nodes.  Looks like
852         // something is wrong with the file.  Return now to avoid
853         // infinite recursion.
854         return;
855     }
856     else if (n>=nEntryCount || (n<0 && n!=STG_FREE))
857     {
858         // n has an invalid value.  Don't access the corresponding
859         // stream content.
860         return;
861     }
862 
863     void* p = ( n == STG_FREE ) ? NULL : GetEntry( n );
864     if( p )
865     {
866         sal_Bool bOk(sal_False);
867         StgDirEntry* pCur = new StgDirEntry( p, STGENTRY_SIZE, &bOk );
868 
869         if( !bOk )
870         {
871             delete pCur;
872             rIo.SetError( SVSTREAM_GENERALERROR );
873             // an error occurred
874             return;
875         }
876 
877         // better it is
878         if( !pUpper )
879             pCur->aEntry.SetType( STG_ROOT );
880 
881         sal_Int32 nLeft = pCur->aEntry.GetLeaf( STG_LEFT );
882         sal_Int32 nRight = pCur->aEntry.GetLeaf( STG_RIGHT );
883         // substorage?
884         sal_Int32 nLeaf = STG_FREE;
885         if( pCur->aEntry.GetType() == STG_STORAGE || pCur->aEntry.GetType() == STG_ROOT )
886         {
887             nLeaf = pCur->aEntry.GetLeaf( STG_CHILD );
888             if (nLeaf != STG_FREE && nLeaf == n)
889             {
890                 delete pCur;
891                 rIo.SetError( SVSTREAM_GENERALERROR );
892                 return;
893             }
894         }
895 
896         if( nLeaf != 0 && nLeft != 0 && nRight != 0 )
897         {
898             if( StgAvlNode::Insert
899                 ( (StgAvlNode**) ( pUpper ? &pUpper->pDown : &pRoot ), pCur ) )
900             {
901                 pCur->pUp    = pUpper;
902                 pCur->ppRoot = &pRoot;
903             }
904             else
905             {
906                 rIo.SetError( SVSTREAM_CANNOT_MAKE );
907                 delete pCur; pCur = NULL;
908                 return;
909             }
910             SetupEntry( nLeft, pUpper, nEntryCount, nDepth+1);
911             SetupEntry( nRight, pUpper, nEntryCount, nDepth+1);
912             SetupEntry( nLeaf, pCur, nEntryCount, nDepth+1);
913         }
914         else
915             delete pCur;
916     }
917 }
918 
919 // Extend or shrink the directory stream.
920 
SetSize(sal_Int32 nBytes)921 sal_Bool StgDirStrm::SetSize( sal_Int32 nBytes )
922 {
923     // Always allocate full pages
924     if ( nBytes < 0 )
925         nBytes = 0;
926 
927     nBytes = ( ( nBytes + nPageSize - 1 ) / nPageSize ) * nPageSize;
928     return StgStrm::SetSize( nBytes );
929 }
930 
931 // Save the TOC stream into a new substream after saving all data streams
932 
Store()933 sal_Bool StgDirStrm::Store()
934 {
935     if( !pRoot || !pRoot->IsDirty() )
936         return sal_True;
937 	if( !pRoot->StoreStreams( rIo ) )
938 		return sal_False;
939 	// After writing all streams, the data FAT stream has changed,
940 	// so we have to commit the root again
941 	pRoot->Commit();
942 	// We want a completely new stream, so fake an empty stream
943     sal_Int32 nOldStart = nStart;       // save for later deletion
944     sal_Int32 nOldSize  = nSize;
945     nStart = nPage = STG_EOF;
946     nSize  = nPos = 0;
947     nOffset = 0;
948 	// Delete all temporary entries
949 	pRoot->DelTemp( sal_False );
950     // set the entry numbers
951     sal_Int32 n = 0;
952     pRoot->Enum( n );
953     if( !SetSize( n * STGENTRY_SIZE ) )
954     {
955         nStart = nOldStart; nSize = nOldSize;
956 		pRoot->RevertAll();
957         return sal_False;
958     }
959     // set up the cache elements for the new stream
960     if( !Copy( STG_FREE, nSize ) )
961 	{
962 		pRoot->RevertAll();
963         return sal_False;
964 	}
965     // Write the data to the new stream
966     if( !pRoot->Store( *this ) )
967 	{
968 		pRoot->RevertAll();
969         return sal_False;
970 	}
971 	// fill any remaining entries with empty data
972     sal_Int32 ne = nSize / STGENTRY_SIZE;
973 	StgEntry aEmpty;
974 	aEmpty.Init();
975     while( n < ne )
976     {
977         void* p = GetEntry( n++, sal_True );
978         if( !p )
979 		{
980 			pRoot->RevertAll();
981             return sal_False;
982 		}
983 		aEmpty.Store( p );
984     }
985     // Now we can release the old stream
986     pFat->FreePages( nOldStart, sal_True );
987     rIo.aHdr.SetTOCStart( nStart );
988 	return sal_True;
989 }
990 
991 // Get a dir entry.
992 
GetEntry(sal_Int32 n,sal_Bool bDirty)993 void* StgDirStrm::GetEntry( sal_Int32 n, sal_Bool bDirty )
994 {
995     if( n < 0 )
996         return NULL;
997 
998     n *= STGENTRY_SIZE;
999     if( n < 0 && n >= nSize )
1000         return NULL;
1001     return GetPtr( n, sal_True, bDirty );
1002 }
1003 
1004 // Find a dir entry.
1005 
Find(StgDirEntry & rStg,const String & rName)1006 StgDirEntry* StgDirStrm::Find( StgDirEntry& rStg, const String& rName )
1007 {
1008     if( rStg.pDown )
1009     {
1010         StgEntry aEntry;
1011         aEntry.Init();
1012         if( !aEntry.SetName( rName ) )
1013         {
1014             rIo.SetError( SVSTREAM_GENERALERROR );
1015             return NULL;
1016         }
1017         // Look in the directory attached to the entry
1018         StgDirEntry aTest( aEntry );
1019         return (StgDirEntry*) rStg.pDown->Find( &aTest );
1020     }
1021     else
1022         return NULL;
1023 }
1024 
1025 // Create a new entry.
1026 
Create(StgDirEntry & rStg,const String & rName,StgEntryType eType)1027 StgDirEntry* StgDirStrm::Create
1028 	( StgDirEntry& rStg, const String& rName, StgEntryType eType )
1029 {
1030 	StgEntry aEntry;
1031     aEntry.Init();
1032 	aEntry.SetType( eType );
1033     if( !aEntry.SetName( rName ) )
1034     {
1035         rIo.SetError( SVSTREAM_GENERALERROR );
1036         return NULL;
1037     }
1038     StgDirEntry* pRes = Find( rStg, rName );
1039 	if( pRes )
1040 	{
1041 		if( !pRes->bInvalid )
1042 		{
1043 			rIo.SetError( SVSTREAM_CANNOT_MAKE );
1044 			return NULL;
1045 		}
1046 		pRes->bInvalid =
1047 		pRes->bRemoved =
1048 		pRes->bTemp    = sal_False;
1049 		pRes->bCreated =
1050         pRes->bDirty   = sal_True;
1051 	}
1052 	else
1053 	{
1054 		pRes = new StgDirEntry( aEntry );
1055 	    if( StgAvlNode::Insert( (StgAvlNode**) &rStg.pDown, pRes ) )
1056 	    {
1057 			pRes->pUp    = &rStg;
1058 	        pRes->ppRoot = &pRoot;
1059 			pRes->bCreated =
1060 	        pRes->bDirty = sal_True;
1061 	    }
1062 	    else
1063 	    {
1064 	        rIo.SetError( SVSTREAM_CANNOT_MAKE );
1065 	        delete pRes; pRes = NULL;
1066 	    }
1067 	}
1068 	return pRes;
1069 }
1070 
1071 // Rename the given entry.
1072 
Rename(StgDirEntry & rStg,const String & rOld,const String & rNew)1073 sal_Bool StgDirStrm::Rename( StgDirEntry& rStg, const String& rOld, const String& rNew )
1074 {
1075     StgDirEntry* p = Find( rStg, rOld );
1076     if( p )
1077     {
1078 
1079 		if( !StgAvlNode::Remove( (StgAvlNode**) &rStg.pDown, p, sal_False ) )
1080 			return sal_False;
1081 		p->aEntry.SetName( rNew );
1082 		if( !StgAvlNode::Insert( (StgAvlNode**) &rStg.pDown, p ) )
1083 			return sal_False;
1084 		p->bRenamed = 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 // Move the given entry to a different storage.
1095 
Move(StgDirEntry & rStg1,StgDirEntry & rStg2,const String & rName)1096 sal_Bool StgDirStrm::Move( StgDirEntry& rStg1, StgDirEntry& rStg2, const String& rName )
1097 {
1098     StgDirEntry* p = Find( rStg1, rName );
1099     if( p )
1100 	{
1101 		if( !StgAvlNode::Move
1102 			( (StgAvlNode**) &rStg1.pDown, (StgAvlNode**) &rStg2.pDown, p ) )
1103 			return sal_False;
1104 		p->bDirty = sal_True;
1105 		return sal_True;
1106 	}
1107 	else
1108     {
1109         rIo.SetError( SVSTREAM_FILE_NOT_FOUND );
1110         return sal_False;
1111     }
1112 }
1113 
1114