xref: /AOO41X/main/sot/source/sdstor/stgdir.cxx (revision 8809db7a87f97847b57a57f4cd2b0104b2b83182)
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