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