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