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