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