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