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