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 5368 : StgDirEntry::StgDirEntry( const void* pBuffer, sal_uInt32 nBufferLen, bool * pbOk ) : StgAvlNode()
48 : {
49 5368 : *pbOk = aEntry.Load( pBuffer, nBufferLen );
50 :
51 5368 : InitMembers();
52 5368 : }
53 :
54 15534 : StgDirEntry::StgDirEntry( const StgEntry& r ) : StgAvlNode(), aEntry( r )
55 : {
56 15534 : InitMembers();
57 15534 : }
58 :
59 : // Helper for all ctors
60 :
61 20902 : void StgDirEntry::InitMembers()
62 : {
63 20902 : aSave = aEntry;
64 : pUp =
65 20902 : pDown = NULL;
66 20902 : ppRoot = NULL;
67 20902 : pStgStrm = NULL;
68 : pCurStrm =
69 20902 : pTmpStrm = NULL;
70 : nPos =
71 : nEntry =
72 20902 : nRefCnt = 0;
73 20902 : nMode = STREAM_READ;
74 20902 : bDirect = true;
75 : bInvalid =
76 : bCreated =
77 : bRenamed =
78 : bRemoved =
79 : bTemp =
80 : bDirty =
81 20902 : bZombie = false;
82 20902 : }
83 :
84 49344 : StgDirEntry::~StgDirEntry()
85 : {
86 20902 : Close();
87 20902 : delete pCurStrm;
88 20902 : delete pStgStrm;
89 20902 : delete pDown;
90 28442 : }
91 :
92 : // Comparison function
93 :
94 110120 : short StgDirEntry::Compare( const StgAvlNode* p ) const
95 : {
96 110120 : short nResult = -1;
97 110120 : if ( p )
98 : {
99 110120 : const StgDirEntry* pEntry = (const StgDirEntry*) p;
100 110120 : nResult = aEntry.Compare( pEntry->aEntry );
101 : }
102 110120 : 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 2338 : void StgDirEntry::Enum( sal_Int32& n )
112 : {
113 2338 : sal_Int32 nLeft = STG_FREE, nRight = STG_FREE, nDown = STG_FREE;
114 2338 : nEntry = n++;
115 2338 : if( pLeft )
116 : {
117 79 : ((StgDirEntry*) pLeft)->Enum( n ); nLeft = ((StgDirEntry*) pLeft)->nEntry;
118 : }
119 2338 : if( pRight )
120 : {
121 1674 : ((StgDirEntry*) pRight)->Enum( n ); nRight = ((StgDirEntry*) pRight)->nEntry;
122 : }
123 2338 : if( pDown )
124 : {
125 228 : pDown->Enum( n ); nDown = pDown->nEntry;
126 : }
127 2338 : aSave.SetLeaf( STG_LEFT, nLeft );
128 2338 : aSave.SetLeaf( STG_RIGHT, nRight );
129 2338 : aSave.SetLeaf( STG_CHILD, nDown );
130 2338 : }
131 :
132 : // Delete all temporary entries before writing the TOC stream.
133 : // Until now Deltem is never called with bForce True
134 :
135 2348 : void StgDirEntry::DelTemp( bool bForce )
136 : {
137 2348 : if( pLeft )
138 89 : ((StgDirEntry*) pLeft)->DelTemp( false );
139 2348 : if( pRight )
140 1674 : ((StgDirEntry*) pRight)->DelTemp( false );
141 2348 : if( pDown )
142 : {
143 : // If the storage is dead, of course all elements are dead, too
144 228 : if( bInvalid && aEntry.GetType() == STG_STORAGE )
145 0 : bForce = true;
146 228 : pDown->DelTemp( bForce );
147 : }
148 4696 : if( ( bForce || bInvalid )
149 2358 : && ( aEntry.GetType() != STG_ROOT ) /* && ( nRefCnt <= 1 ) */ )
150 : {
151 10 : Close();
152 10 : if( pUp )
153 : {
154 : // this deletes the element if refcnt == 0!
155 10 : bool bDel = nRefCnt == 0;
156 10 : StgAvlNode::Remove( (StgAvlNode**) &pUp->pDown, this, bDel );
157 10 : if( !bDel )
158 : {
159 0 : pLeft = pRight = pDown = 0;
160 0 : bInvalid = bZombie = true;
161 : }
162 : }
163 : }
164 2348 : }
165 :
166 : // Save the tree into the given dir stream
167 :
168 2338 : bool StgDirEntry::Store( StgDirStrm& rStrm )
169 : {
170 2338 : void* pEntry = rStrm.GetEntry( nEntry, true );
171 2338 : if( !pEntry )
172 0 : return false;
173 : // Do not store the current (maybe not commited) entry
174 2338 : aSave.Store( pEntry );
175 2338 : if( pLeft )
176 79 : if( !((StgDirEntry*) pLeft)->Store( rStrm ) )
177 0 : return false;
178 2338 : if( pRight )
179 1674 : if( !((StgDirEntry*) pRight)->Store( rStrm ) )
180 0 : return false;
181 2338 : if( pDown )
182 228 : if( !pDown->Store( rStrm ) )
183 0 : return false;
184 2338 : return true;
185 : }
186 :
187 2348 : bool StgDirEntry::StoreStream( StgIo& rIo )
188 : {
189 2348 : if( aEntry.GetType() == STG_STREAM || aEntry.GetType() == STG_ROOT )
190 : {
191 2282 : if( bInvalid )
192 : {
193 : // Delete the stream if needed
194 10 : if( !pStgStrm )
195 : {
196 0 : OpenStream( rIo );
197 0 : delete pStgStrm, pStgStrm = NULL;
198 : }
199 : else
200 10 : pStgStrm->SetSize( 0 );
201 : }
202 : // or write the data stream
203 2272 : else if( !Tmp2Strm() )
204 0 : return false;
205 : }
206 2348 : return true;
207 : }
208 :
209 : // Save all dirty streams
210 :
211 2348 : bool StgDirEntry::StoreStreams( StgIo& rIo )
212 : {
213 2348 : if( !StoreStream( rIo ) )
214 0 : return false;
215 2348 : if( pLeft )
216 89 : if( !((StgDirEntry*) pLeft)->StoreStreams( rIo ) )
217 0 : return false;
218 2348 : if( pRight )
219 1674 : if( !((StgDirEntry*) pRight)->StoreStreams( rIo ) )
220 0 : return false;
221 2348 : if( pDown )
222 228 : if( !pDown->StoreStreams( rIo ) )
223 0 : return false;
224 2348 : 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 357 : bool StgDirEntry::IsDirty()
243 : {
244 357 : if( bDirty || bInvalid )
245 357 : 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 5489 : void StgDirEntry::OpenStream( StgIo& rIo, bool bForceBig )
258 : {
259 5489 : sal_Int32 nThreshold = (sal_uInt16) rIo.aHdr.GetThreshold();
260 5489 : delete pStgStrm;
261 5489 : if( !bForceBig && aEntry.GetSize() < nThreshold )
262 4750 : pStgStrm = new StgSmallStrm( rIo, *this );
263 : else
264 739 : pStgStrm = new StgDataStrm( rIo, *this );
265 5489 : 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 5489 : nPos = 0;
272 5489 : }
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 27255 : void StgDirEntry::Close()
280 : {
281 27255 : delete pTmpStrm;
282 27255 : pTmpStrm = NULL;
283 : // nRefCnt = 0;
284 27255 : bInvalid = bTemp;
285 27255 : }
286 :
287 : // Get the current stream size
288 :
289 10464 : sal_Int32 StgDirEntry::GetSize()
290 : {
291 : sal_Int32 n;
292 10464 : if( pTmpStrm )
293 0 : n = pTmpStrm->GetSize();
294 10464 : else if( pCurStrm )
295 0 : n = pCurStrm->GetSize();
296 10464 : else n = aEntry.GetSize();
297 10464 : return n;
298 : }
299 :
300 : // Set the stream size. This means also creating a temp stream.
301 :
302 992 : bool StgDirEntry::SetSize( sal_Int32 nNewSize )
303 : {
304 992 : if (
305 1984 : !( nMode & STREAM_WRITE ) ||
306 1984 : (!bDirect && !pTmpStrm && !Strm2Tmp())
307 : )
308 : {
309 0 : return false;
310 : }
311 :
312 992 : if( nNewSize < nPos )
313 0 : nPos = nNewSize;
314 992 : if( pTmpStrm )
315 : {
316 992 : pTmpStrm->SetSize( nNewSize );
317 992 : pStgStrm->GetIo().SetError( pTmpStrm->GetError() );
318 992 : 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 1892330 : sal_Int32 StgDirEntry::Seek( sal_Int32 nNew )
390 : {
391 1892330 : if( pTmpStrm )
392 : {
393 14429 : if( nNew < 0 )
394 3972 : nNew = pTmpStrm->GetSize();
395 14429 : nNew = pTmpStrm->Seek( nNew );
396 : }
397 1877901 : else if( pCurStrm )
398 : {
399 41 : if( nNew < 0 )
400 2 : nNew = pCurStrm->GetSize();
401 41 : nNew = pCurStrm->Seek( nNew );
402 : }
403 : else
404 : {
405 : OSL_ENSURE( pStgStrm, "The pointer may not be NULL!" );
406 1877860 : if ( !pStgStrm )
407 0 : return nPos;
408 :
409 1877860 : sal_Int32 nSize = aEntry.GetSize();
410 :
411 1877860 : if( nNew < 0 )
412 2220 : nNew = nSize;
413 :
414 : // try to enlarge, the readonly streams should not allow this
415 1877860 : if( nNew > nSize )
416 : {
417 444 : 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 444 : return nPos;
422 : }
423 : else
424 0 : return Seek( nNew );
425 : }
426 1877416 : pStgStrm->Pos2Page( nNew );
427 1877416 : nNew = pStgStrm->GetPos();
428 : }
429 :
430 1891886 : return nPos = nNew;
431 : }
432 :
433 : // Read
434 :
435 1097247 : sal_Int32 StgDirEntry::Read( void* p, sal_Int32 nLen )
436 : {
437 1097247 : if( nLen <= 0 )
438 12 : return 0;
439 1097235 : if( pTmpStrm )
440 0 : nLen = pTmpStrm->Read( p, nLen );
441 1097235 : else if( pCurStrm )
442 0 : nLen = pCurStrm->Read( p, nLen );
443 : else
444 : {
445 : OSL_ENSURE( pStgStrm, "The pointer may not be NULL!" );
446 1097235 : if ( !pStgStrm )
447 0 : return 0;
448 :
449 1097235 : nLen = pStgStrm->Read( p, nLen );
450 : }
451 :
452 1097235 : nPos += nLen;
453 1097235 : return nLen;
454 : }
455 :
456 : // Write
457 :
458 7143 : sal_Int32 StgDirEntry::Write( const void* p, sal_Int32 nLen )
459 : {
460 7143 : if( nLen <= 0 || !( nMode & STREAM_WRITE ) )
461 0 : return 0;
462 :
463 : // Was this stream committed internally and reopened in direct mode?
464 7143 : 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 7143 : if( !bDirect && !pTmpStrm && !Strm2Tmp() )
468 0 : return 0;
469 :
470 : OSL_ENSURE( pStgStrm, "The pointer may not be NULL!" );
471 7143 : if ( !pStgStrm )
472 0 : return 0;
473 :
474 7143 : if( pTmpStrm )
475 : {
476 7143 : nLen = pTmpStrm->Write( p, nLen );
477 7143 : 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 7143 : nPos += nLen;
491 7143 : return nLen;
492 : }
493 :
494 938 : void StgDirEntry::Copy( BaseStorageStream& rDest )
495 : {
496 938 : sal_Int32 n = GetSize();
497 938 : if( rDest.SetSize( n ) && n )
498 : {
499 909 : sal_uLong Pos = rDest.Tell();
500 : sal_uInt8 aTempBytes[ 4096 ];
501 909 : void* p = static_cast<void*>( aTempBytes );
502 909 : Seek( 0L );
503 909 : rDest.Seek( 0L );
504 2996 : while( n )
505 : {
506 1178 : sal_Int32 nn = n;
507 1178 : if( nn > 4096 )
508 269 : nn = 4096;
509 1178 : if( Read( p, nn ) != nn )
510 0 : break;
511 1178 : if( sal::static_int_cast<sal_Int32>(rDest.Write( p, nn )) != nn )
512 0 : break;
513 1178 : n -= nn;
514 : }
515 909 : rDest.Seek( Pos ); // ?! Seems to be undocumented !
516 : }
517 938 : }
518 :
519 : // Commit this entry
520 :
521 8276 : bool StgDirEntry::Commit()
522 : {
523 : // OSL_ENSURE( nMode & STREAM_WRITE, "Trying to commit readonly stream!" );
524 :
525 8276 : aSave = aEntry;
526 8276 : bool bRes = true;
527 8276 : if( aEntry.GetType() == STG_STREAM )
528 : {
529 7560 : if( pTmpStrm )
530 1908 : delete pCurStrm, pCurStrm = pTmpStrm, pTmpStrm = NULL;
531 7560 : if( bRemoved )
532 : // Delete the stream if needed
533 10 : if( pStgStrm )
534 10 : pStgStrm->SetSize( 0 );
535 : }
536 716 : else if( aEntry.GetType() == STG_STORAGE && bDirect && bRes )
537 : {
538 26 : StgIterator aIter( *this );
539 154 : for( StgDirEntry* p = aIter.First(); p && bRes; p = aIter.Next() )
540 128 : bRes = p->Commit();
541 : }
542 8276 : 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 1908 : bool StgDirEntry::Strm2Tmp()
610 : {
611 1908 : if( !pTmpStrm )
612 : {
613 1908 : sal_uLong n = 0;
614 1908 : if( pCurStrm )
615 : {
616 : // It was already commited once
617 13 : pTmpStrm = new StgTmpStrm;
618 13 : if( pTmpStrm->GetError() == SVSTREAM_OK && pTmpStrm->Copy( *pCurStrm ) )
619 13 : return true;
620 0 : n = 1; // indicates error
621 : }
622 : else
623 : {
624 1895 : n = aEntry.GetSize();
625 1895 : pTmpStrm = new StgTmpStrm( n );
626 1895 : if( pTmpStrm->GetError() == SVSTREAM_OK )
627 : {
628 1895 : 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 1895 : 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 1895 : return true;
668 : }
669 :
670 : // Copy the temp stream to the stg stream during the final commit
671 :
672 2272 : bool StgDirEntry::Tmp2Strm()
673 : {
674 : // We did commit once, but have not written since then
675 2272 : if( !pTmpStrm )
676 2272 : pTmpStrm = pCurStrm, pCurStrm = NULL;
677 2272 : if( pTmpStrm )
678 : {
679 : OSL_ENSURE( pStgStrm, "The pointer may not be NULL!" );
680 1895 : if ( !pStgStrm )
681 0 : return false;
682 1895 : sal_uLong n = pTmpStrm->GetSize();
683 : StgStrm* pNewStrm;
684 1895 : StgIo& rIo = pStgStrm->GetIo();
685 1895 : sal_uLong nThreshold = (sal_uLong) rIo.aHdr.GetThreshold();
686 1895 : if( n < nThreshold )
687 1641 : pNewStrm = new StgSmallStrm( rIo, STG_EOF, 0 );
688 : else
689 254 : pNewStrm = new StgDataStrm( rIo, STG_EOF, 0 );
690 1895 : if( pNewStrm->SetSize( n ) )
691 : {
692 : sal_uInt8 p[ 4096 ];
693 1895 : pTmpStrm->Seek( 0L );
694 6209 : while( n )
695 : {
696 2419 : sal_uLong nn = n;
697 2419 : if( nn > 4096 )
698 553 : nn = 4096;
699 2419 : if( pTmpStrm->Read( p, nn ) != nn )
700 0 : break;
701 2419 : if( (sal_uLong) pNewStrm->Write( p, nn ) != nn )
702 0 : break;
703 2419 : n -= nn;
704 : }
705 1895 : 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 1895 : pStgStrm->SetSize( 0L );
715 1895 : delete pStgStrm;
716 1895 : pStgStrm = pNewStrm;
717 1895 : pNewStrm->SetEntry( *this );
718 1895 : pNewStrm->Pos2Page( nPos );
719 1895 : delete pTmpStrm;
720 1895 : delete pCurStrm;
721 1895 : pTmpStrm = pCurStrm = NULL;
722 1895 : aSave = aEntry;
723 : }
724 : }
725 : }
726 2272 : 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 10434 : void StgDirEntry::Invalidate( bool bDel )
755 : {
756 : // nRefCnt = 0;
757 10434 : if( bDel )
758 10 : bRemoved = bInvalid = true;
759 10434 : switch( aEntry.GetType() )
760 : {
761 : case STG_STORAGE:
762 : case STG_ROOT:
763 : {
764 1329 : StgIterator aIter( *this );
765 10899 : for( StgDirEntry* p = aIter.First(); p; p = aIter.Next() )
766 9570 : p->Invalidate( bDel );
767 1329 : break;
768 : }
769 : default:
770 9105 : break;
771 : }
772 10434 : }
773 :
774 : ///////////////////////////// class StgDirStrm
775 :
776 : // This specialized stream is the maintenance stream for the directory tree.
777 :
778 668 : StgDirStrm::StgDirStrm( StgIo& r )
779 : : StgDataStrm( r, r.aHdr.GetTOCStart(), -1 )
780 : , pRoot( NULL )
781 668 : , nEntries( 0 )
782 : {
783 668 : if( r.GetError() )
784 669 : return;
785 667 : nEntries = nPageSize / STGENTRY_SIZE;
786 667 : if( nStart == STG_EOF )
787 : {
788 171 : StgEntry aRoot;
789 171 : aRoot.Init();
790 171 : aRoot.SetName( OUString("Root Entry") );
791 171 : aRoot.SetType( STG_ROOT );
792 171 : pRoot = new StgDirEntry( aRoot );
793 171 : pRoot->SetDirty();
794 : }
795 : else
796 : {
797 : // temporarily use this instance as owner, so
798 : // the TOC pages can be removed.
799 496 : pEntry = (StgDirEntry*) this; // just for a bit pattern
800 496 : SetupEntry( 0, pRoot );
801 496 : pEntry = NULL;
802 : }
803 : }
804 :
805 2004 : StgDirStrm::~StgDirStrm()
806 : {
807 668 : delete pRoot;
808 1336 : }
809 :
810 : // Recursively parse the directory tree during reading the TOC stream
811 :
812 16582 : void StgDirStrm::SetupEntry( sal_Int32 n, StgDirEntry* pUpper )
813 : {
814 16582 : void* p = ( n == STG_FREE ) ? NULL : GetEntry( n );
815 16582 : if( p )
816 : {
817 5368 : bool bOk(false);
818 5368 : StgDirEntry* pCur = new StgDirEntry( p, STGENTRY_SIZE, &bOk );
819 :
820 5368 : if( !bOk )
821 : {
822 3 : delete pCur;
823 3 : rIo.SetError( SVSTREAM_GENERALERROR );
824 : // an error occurred
825 8 : return;
826 : }
827 :
828 : // better it is
829 5365 : if( !pUpper )
830 494 : pCur->aEntry.SetType( STG_ROOT );
831 :
832 5365 : sal_Int32 nLeft = pCur->aEntry.GetLeaf( STG_LEFT );
833 5365 : sal_Int32 nRight = pCur->aEntry.GetLeaf( STG_RIGHT );
834 : // substorage?
835 5365 : sal_Int32 nLeaf = STG_FREE;
836 5365 : if( pCur->aEntry.GetType() == STG_STORAGE || pCur->aEntry.GetType() == STG_ROOT )
837 : {
838 796 : nLeaf = pCur->aEntry.GetLeaf( STG_CHILD );
839 796 : if (nLeaf != STG_FREE && nLeaf == n)
840 : {
841 0 : delete pCur;
842 0 : rIo.SetError( SVSTREAM_GENERALERROR );
843 0 : return;
844 : }
845 : }
846 :
847 5365 : if( nLeaf != 0 && nLeft != 0 && nRight != 0 )
848 : {
849 : //fdo#41642 Do we need to check full chain upwards for loops ?
850 5364 : if (pUpper)
851 : {
852 4870 : 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 1 : delete pCur;
856 1 : return;
857 : }
858 :
859 4869 : StgDirEntry *pUpperUpper = pUpper->pUp;
860 4869 : 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 1 : delete pCur;
864 1 : return;
865 : }
866 : }
867 :
868 5362 : if( StgAvlNode::Insert
869 5362 : ( (StgAvlNode**) ( pUpper ? &pUpper->pDown : &pRoot ), pCur ) )
870 : {
871 5362 : pCur->pUp = pUpper;
872 5362 : 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 5362 : SetupEntry( nLeft, pUpper );
884 5362 : SetupEntry( nRight, pUpper );
885 5362 : SetupEntry( nLeaf, pCur );
886 : }
887 : else
888 : {
889 1 : delete pCur;
890 : }
891 : }
892 : }
893 :
894 : // Extend or shrink the directory stream.
895 :
896 357 : bool StgDirStrm::SetSize( sal_Int32 nBytes )
897 : {
898 : // Always allocate full pages
899 357 : if ( nBytes < 0 )
900 0 : nBytes = 0;
901 :
902 357 : nBytes = ( ( nBytes + nPageSize - 1 ) / nPageSize ) * nPageSize;
903 357 : return StgStrm::SetSize( nBytes );
904 : }
905 :
906 : // Save the TOC stream into a new substream after saving all data streams
907 :
908 357 : bool StgDirStrm::Store()
909 : {
910 357 : if( !pRoot || !pRoot->IsDirty() )
911 0 : return true;
912 357 : 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 357 : pRoot->Commit();
917 : // We want a completely new stream, so fake an empty stream
918 357 : sal_Int32 nOldStart = nStart; // save for later deletion
919 357 : sal_Int32 nOldSize = nSize;
920 357 : nStart = nPage = STG_EOF;
921 357 : nSize = nPos = 0;
922 357 : nOffset = 0;
923 : // Delete all temporary entries
924 357 : pRoot->DelTemp( false );
925 : // set the entry numbers
926 357 : sal_Int32 n = 0;
927 357 : pRoot->Enum( n );
928 357 : 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 357 : if( !Copy( STG_FREE, nSize ) )
936 : {
937 0 : pRoot->RevertAll();
938 0 : return false;
939 : }
940 : // Write the data to the new stream
941 357 : if( !pRoot->Store( *this ) )
942 : {
943 0 : pRoot->RevertAll();
944 0 : return false;
945 : }
946 : // fill any remaining entries with empty data
947 357 : sal_Int32 ne = nSize / STGENTRY_SIZE;
948 357 : StgEntry aEmpty;
949 357 : aEmpty.Init();
950 1532 : while( n < ne )
951 : {
952 818 : void* p = GetEntry( n++, true );
953 818 : if( !p )
954 : {
955 0 : pRoot->RevertAll();
956 0 : return false;
957 : }
958 818 : aEmpty.Store( p );
959 : }
960 : // Now we can release the old stream
961 357 : pFat->FreePages( nOldStart, true );
962 357 : rIo.aHdr.SetTOCStart( nStart );
963 357 : return true;
964 : }
965 :
966 : // Get a dir entry.
967 :
968 8525 : void* StgDirStrm::GetEntry( sal_Int32 n, bool bDirty )
969 : {
970 8525 : if( n < 0 )
971 1 : return NULL;
972 :
973 8524 : n *= STGENTRY_SIZE;
974 8524 : if( n >= nSize )
975 0 : return NULL;
976 8524 : return GetPtr( n, true, bDirty );
977 : }
978 :
979 : // Find a dir entry.
980 :
981 13992 : StgDirEntry* StgDirStrm::Find( StgDirEntry& rStg, const OUString& rName )
982 : {
983 13992 : if( rStg.pDown )
984 : {
985 13362 : StgEntry aEntry;
986 13362 : aEntry.Init();
987 13362 : 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 26724 : StgDirEntry aTest( aEntry );
994 26724 : return (StgDirEntry*) rStg.pDown->Find( &aTest );
995 : }
996 : else
997 630 : return NULL;
998 : }
999 :
1000 : // Create a new entry.
1001 :
1002 2001 : StgDirEntry* StgDirStrm::Create( StgDirEntry& rStg, const OUString& rName, StgEntryType eType )
1003 : {
1004 2001 : StgEntry aEntry;
1005 2001 : aEntry.Init();
1006 2001 : aEntry.SetType( eType );
1007 2001 : if( !aEntry.SetName( rName ) )
1008 : {
1009 0 : rIo.SetError( SVSTREAM_GENERALERROR );
1010 0 : return NULL;
1011 : }
1012 2001 : StgDirEntry* pRes = Find( rStg, rName );
1013 2001 : 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 2001 : pRes = new StgDirEntry( aEntry );
1029 2001 : if( StgAvlNode::Insert( (StgAvlNode**) &rStg.pDown, pRes ) )
1030 : {
1031 2001 : pRes->pUp = &rStg;
1032 2001 : pRes->ppRoot = &pRoot;
1033 : pRes->bCreated =
1034 2001 : pRes->bDirty = true;
1035 : }
1036 : else
1037 : {
1038 0 : rIo.SetError( SVSTREAM_CANNOT_MAKE );
1039 0 : delete pRes; pRes = NULL;
1040 : }
1041 : }
1042 2001 : 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: */
|