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