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