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