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 <osl/file.hxx> 30 #include <tools/tempfile.hxx> 31 #include <tools/debug.hxx> 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 #define __HUGE 41 42 ///////////////////////////// class StgFAT /////////////////////////////// 43 44 // The FAT class performs FAT operations on an underlying storage stream. 45 // This stream is either the master FAT stream (m == sal_True ) or a normal 46 // storage stream, which then holds the FAT for small data allocations. 47 48 StgFAT::StgFAT( StgStrm& r, sal_Bool m ) : rStrm( r ) 49 { 50 bPhys = m; 51 nPageSize = rStrm.GetIo().GetPhysPageSize(); 52 nEntries = nPageSize >> 2; 53 nOffset = 0; 54 nMaxPage = 0; 55 nLimit = 0; 56 } 57 58 // Retrieve the physical page for a given byte offset. 59 60 StgPage* StgFAT::GetPhysPage( sal_Int32 nByteOff ) 61 { 62 StgPage* pPg = NULL; 63 // Position within the underlying stream 64 // use the Pos2Page() method of the stream 65 if( rStrm.Pos2Page( nByteOff ) ) 66 { 67 nOffset = rStrm.GetOffset(); 68 sal_Int32 nPhysPage = rStrm.GetPage(); 69 // get the physical page (must be present) 70 pPg = rStrm.GetIo().Get( nPhysPage, sal_True ); 71 } 72 return pPg; 73 } 74 75 // Get the follow page for a certain FAT page. 76 77 sal_Int32 StgFAT::GetNextPage( sal_Int32 nPg ) 78 { 79 if( nPg >= 0 ) 80 { 81 StgPage* pPg = GetPhysPage( nPg << 2 ); 82 nPg = pPg ? pPg->GetPage( nOffset >> 2 ) : STG_EOF; 83 } 84 return nPg; 85 } 86 87 // Find the best fit block for the given size. Return 88 // the starting block and its size or STG_EOF and 0. 89 // nLastPage is a stopper which tells the current 90 // underlying stream size. It is treated as a recommendation 91 // to abort the search to inhibit excessive file growth. 92 93 sal_Int32 StgFAT::FindBlock( sal_Int32& nPgs ) 94 { 95 sal_Int32 nMinStart = STG_EOF, nMinLen = 0; 96 sal_Int32 nMaxStart = STG_EOF, nMaxLen = 0x7FFFFFFFL; 97 sal_Int32 nTmpStart = STG_EOF, nTmpLen = 0; 98 sal_Int32 nPages = rStrm.GetSize() >> 2; 99 sal_Bool bFound = sal_False; 100 StgPage* pPg = NULL; 101 short nEntry = 0; 102 for( sal_Int32 i = 0; i < nPages; i++, nEntry++ ) 103 { 104 if( !( nEntry % nEntries ) ) 105 { 106 // load the next page for that stream 107 nEntry = 0; 108 pPg = GetPhysPage( i << 2 ); 109 if( !pPg ) 110 return STG_EOF; 111 } 112 sal_Int32 nCur = pPg->GetPage( nEntry ); 113 if( nCur == STG_FREE ) 114 { 115 // count the size of this area 116 if( nTmpLen ) 117 nTmpLen++; 118 else 119 nTmpStart = i, 120 nTmpLen = 1; 121 if( nTmpLen == nPgs 122 // If we already did find a block, stop when reaching the limit 123 || ( bFound && ( nEntry >= nLimit ) ) ) 124 break; 125 } 126 else if( nTmpLen ) 127 { 128 if( nTmpLen > nPgs && nTmpLen < nMaxLen ) 129 // block > requested size 130 nMaxLen = nTmpLen, nMaxStart = nTmpStart, bFound = sal_True; 131 else if( nTmpLen >= nMinLen ) 132 { 133 // block < requested size 134 nMinLen = nTmpLen, nMinStart = nTmpStart; 135 bFound = sal_True; 136 if( nTmpLen == nPgs ) 137 break; 138 } 139 nTmpStart = STG_EOF; 140 nTmpLen = 0; 141 } 142 } 143 // Determine which block to use. 144 if( nTmpLen ) 145 { 146 if( nTmpLen > nPgs && nTmpLen < nMaxLen ) 147 // block > requested size 148 nMaxLen = nTmpLen, nMaxStart = nTmpStart; 149 else if( nTmpLen >= nMinLen ) 150 // block < requested size 151 nMinLen = nTmpLen, nMinStart = nTmpStart; 152 } 153 if( nMinStart != STG_EOF && nMaxStart != STG_EOF ) 154 { 155 // two areas found; return the best fit area 156 sal_Int32 nMinDiff = nPgs - nMinLen; 157 sal_Int32 nMaxDiff = nMaxLen - nPgs; 158 if( nMinDiff > nMaxDiff ) 159 nMinStart = STG_EOF; 160 } 161 if( nMinStart != STG_EOF ) 162 { 163 nPgs = nMinLen; return nMinStart; 164 } 165 else 166 { 167 return nMaxStart; 168 } 169 } 170 171 // Set up the consecutive chain for a given block. 172 173 sal_Bool StgFAT::MakeChain( sal_Int32 nStart, sal_Int32 nPgs ) 174 { 175 sal_Int32 nPos = nStart << 2; 176 StgPage* pPg = GetPhysPage( nPos ); 177 if( !pPg || !nPgs ) 178 return sal_False; 179 while( --nPgs ) 180 { 181 if( nOffset >= nPageSize ) 182 { 183 pPg = GetPhysPage( nPos ); 184 if( !pPg ) 185 return sal_False; 186 } 187 pPg->SetPage( nOffset >> 2, ++nStart ); 188 nOffset += 4; 189 nPos += 4; 190 } 191 if( nOffset >= nPageSize ) 192 { 193 pPg = GetPhysPage( nPos ); 194 if( !pPg ) 195 return sal_False; 196 } 197 pPg->SetPage( nOffset >> 2, STG_EOF ); 198 return sal_True; 199 } 200 201 // Allocate a block of data from the given page number on. 202 // It the page number is != STG_EOF, chain the block. 203 204 sal_Int32 StgFAT::AllocPages( sal_Int32 nBgn, sal_Int32 nPgs ) 205 { 206 sal_Int32 nOrig = nBgn; 207 sal_Int32 nLast = nBgn; 208 sal_Int32 nBegin = STG_EOF; 209 sal_Int32 nAlloc; 210 sal_Int32 nPages = rStrm.GetSize() >> 2; 211 short nPasses = 0; 212 // allow for two passes 213 while( nPasses < 2 ) 214 { 215 // try to satisfy the request from the pool of free pages 216 while( nPgs ) 217 { 218 nAlloc = nPgs; 219 nBegin = FindBlock( nAlloc ); 220 // no more blocks left in present alloc chain 221 if( nBegin == STG_EOF ) 222 break; 223 if( ( nBegin + nAlloc ) > nMaxPage ) 224 nMaxPage = nBegin + nAlloc; 225 if( !MakeChain( nBegin, nAlloc ) ) 226 return STG_EOF; 227 if( nOrig == STG_EOF ) 228 nOrig = nBegin; 229 else 230 { 231 // Patch the chain 232 StgPage* pPg = GetPhysPage( nLast << 2 ); 233 if( !pPg ) 234 return STG_EOF; 235 pPg->SetPage( nOffset >> 2, nBegin ); 236 } 237 nLast = nBegin + nAlloc - 1; 238 nPgs -= nAlloc; 239 } 240 if( nPgs && !nPasses ) 241 { 242 // we need new, fresh space, so allocate and retry 243 if( !rStrm.SetSize( ( nPages + nPgs ) << 2 ) ) 244 return STG_EOF; 245 if( !bPhys && !InitNew( nPages ) ) 246 return sal_False; 247 nPages = rStrm.GetSize() >> 2; 248 nPasses++; 249 } 250 else 251 break; 252 } 253 // now we should have a chain for the complete block 254 if( nBegin == STG_EOF || nPgs ) 255 { 256 rStrm.GetIo().SetError( SVSTREAM_FILEFORMAT_ERROR ); 257 return STG_EOF; // bad structure 258 } 259 return nOrig; 260 } 261 262 // Initialize newly allocated pages for a standard FAT stream 263 // It can be assumed that the stream size is always on 264 // a page boundary 265 266 sal_Bool StgFAT::InitNew( sal_Int32 nPage1 ) 267 { 268 sal_Int32 n = ( ( rStrm.GetSize() >> 2 ) - nPage1 ) / nEntries; 269 while( n-- ) 270 { 271 StgPage* pPg = NULL; 272 // Position within the underlying stream 273 // use the Pos2Page() method of the stream 274 rStrm.Pos2Page( nPage1 << 2 ); 275 // Initialize the page 276 pPg = rStrm.GetIo().Copy( rStrm.GetPage(), STG_FREE ); 277 if ( !pPg ) 278 return sal_False; 279 for( short i = 0; i < nEntries; i++ ) 280 pPg->SetPage( i, STG_FREE ); 281 nPage1++; 282 } 283 return sal_True; 284 } 285 286 // Release a chain 287 288 sal_Bool StgFAT::FreePages( sal_Int32 nStart, sal_Bool bAll ) 289 { 290 while( nStart >= 0 ) 291 { 292 StgPage* pPg = GetPhysPage( nStart << 2 ); 293 if( !pPg ) 294 return sal_False; 295 nStart = pPg->GetPage( nOffset >> 2 ); 296 // The first released page is either set to EOF or FREE 297 pPg->SetPage( nOffset >> 2, bAll ? STG_FREE : STG_EOF ); 298 bAll = sal_True; 299 } 300 return sal_True; 301 } 302 303 ///////////////////////////// class StgStrm //////////////////////////////// 304 305 // The base stream class provides basic functionality for seeking 306 // and accessing the data on a physical basis. It uses the built-in 307 // FAT class for the page allocations. 308 309 StgStrm::StgStrm( StgIo& r ) : rIo( r ) 310 { 311 pFat = NULL; 312 nStart = nPage = STG_EOF; 313 nOffset = 0; 314 pEntry = NULL; 315 nPos = nSize = 0; 316 nPageSize = rIo.GetPhysPageSize(); 317 } 318 319 StgStrm::~StgStrm() 320 { 321 delete pFat; 322 } 323 324 // Attach the stream to the given entry. 325 326 void StgStrm::SetEntry( StgDirEntry& r ) 327 { 328 r.aEntry.SetLeaf( STG_DATA, nStart ); 329 r.aEntry.SetSize( nSize ); 330 pEntry = &r; 331 r.SetDirty(); 332 } 333 334 // Compute page number and offset for the given byte position. 335 // If the position is behind the size, set the stream right 336 // behind the EOF. 337 338 sal_Bool StgStrm::Pos2Page( sal_Int32 nBytePos ) 339 { 340 sal_Int32 nRel, nBgn; 341 // Values < 0 seek to the end 342 if( nBytePos < 0 || nBytePos >= nSize ) 343 nBytePos = nSize; 344 // Adjust the position back to offset 0 345 nPos -= nOffset; 346 sal_Int32 nMask = ~( nPageSize - 1 ); 347 sal_Int32 nOld = nPos & nMask; 348 sal_Int32 nNew = nBytePos & nMask; 349 nOffset = (short) ( nBytePos & ~nMask ); 350 nPos = nBytePos; 351 if( nOld == nNew ) 352 return sal_True; 353 if( nNew > nOld ) 354 { 355 // the new position is behind the current, so an incremental 356 // positioning is OK. Set the page relative position 357 nRel = nNew - nOld; 358 nBgn = nPage; 359 } 360 else 361 { 362 // the new position is before the current, so we have to scan 363 // the entire chain. 364 nRel = nNew; 365 nBgn = nStart; 366 } 367 // now, traverse the FAT chain. 368 nRel /= nPageSize; 369 sal_Int32 nLast = STG_EOF; 370 while( nRel && nBgn >= 0 ) 371 { 372 nLast = nBgn; 373 nBgn = pFat->GetNextPage( nBgn ); 374 nRel--; 375 } 376 // special case: seek to 1st byte of new, unallocated page 377 // (in case the file size is a multiple of the page size) 378 if( nBytePos == nSize && nBgn == STG_EOF && !nRel && !nOffset ) 379 nBgn = nLast, nOffset = nPageSize; 380 if( nBgn < 0 && nBgn != STG_EOF ) 381 { 382 rIo.SetError( SVSTREAM_FILEFORMAT_ERROR ); 383 nBgn = STG_EOF; 384 nOffset = nPageSize; 385 } 386 nPage = nBgn; 387 return sal_Bool( nRel == 0 && nPage >= 0 ); 388 } 389 390 // Retrieve the physical page for a given byte offset. 391 392 StgPage* StgStrm::GetPhysPage( sal_Int32 nBytePos, sal_Bool bForce ) 393 { 394 if( !Pos2Page( nBytePos ) ) 395 return NULL; 396 return rIo.Get( nPage, bForce ); 397 } 398 399 // Copy an entire stream. Both streams are allocated in the FAT. 400 // The target stream is this stream. 401 402 sal_Bool StgStrm::Copy( sal_Int32 nFrom, sal_Int32 nBytes ) 403 { 404 sal_Int32 nTo = nStart; 405 sal_Int32 nPgs = ( nBytes + nPageSize - 1 ) / nPageSize; 406 while( nPgs-- ) 407 { 408 if( nTo < 0 ) 409 { 410 rIo.SetError( SVSTREAM_FILEFORMAT_ERROR ); 411 return sal_False; 412 } 413 rIo.Copy( nTo, nFrom ); 414 if( nFrom >= 0 ) 415 { 416 nFrom = pFat->GetNextPage( nFrom ); 417 if( nFrom < 0 ) 418 { 419 rIo.SetError( SVSTREAM_FILEFORMAT_ERROR ); 420 return sal_False; 421 } 422 } 423 nTo = pFat->GetNextPage( nTo ); 424 } 425 return sal_True; 426 } 427 428 sal_Bool StgStrm::SetSize( sal_Int32 nBytes ) 429 { 430 // round up to page size 431 sal_Int32 nOld = ( ( nSize + nPageSize - 1 ) / nPageSize ) * nPageSize; 432 sal_Int32 nNew = ( ( nBytes + nPageSize - 1 ) / nPageSize ) * nPageSize; 433 if( nNew > nOld ) 434 { 435 if( !Pos2Page( nSize ) ) 436 return sal_False; 437 sal_Int32 nBgn = pFat->AllocPages( nPage, ( nNew - nOld ) / nPageSize ); 438 if( nBgn == STG_EOF ) 439 return sal_False; 440 if( nStart == STG_EOF ) 441 nStart = nPage = nBgn; 442 } 443 else if( nNew < nOld ) 444 { 445 sal_Bool bAll = sal_Bool( nBytes == 0 ); 446 if( !Pos2Page( nBytes ) || !pFat->FreePages( nPage, bAll ) ) 447 return sal_False; 448 if( bAll ) 449 nStart = nPage = STG_EOF; 450 } 451 if( pEntry ) 452 { 453 // change the dir entry? 454 if( !nSize || !nBytes ) 455 pEntry->aEntry.SetLeaf( STG_DATA, nStart ); 456 pEntry->aEntry.SetSize( nBytes ); 457 pEntry->SetDirty(); 458 } 459 nSize = nBytes; 460 pFat->SetLimit( GetPages() ); 461 return sal_True; 462 } 463 464 // Return the # of allocated pages 465 466 sal_Int32 StgStrm::GetPages() 467 { 468 return ( nSize + nPageSize - 1 ) / nPageSize; 469 } 470 471 //////////////////////////// class StgFATStrm ////////////////////////////// 472 473 // The FAT stream class provides physical access to the master FAT. 474 // Since this access is implemented as a StgStrm, we can use the 475 // FAT allocator. 476 477 StgFATStrm::StgFATStrm( StgIo& r ) : StgStrm( r ) 478 { 479 pFat = new StgFAT( *this, sal_True ); 480 nSize = rIo.aHdr.GetFATSize() * nPageSize; 481 } 482 483 sal_Bool StgFATStrm::Pos2Page( sal_Int32 nBytePos ) 484 { 485 // Values < 0 seek to the end 486 if( nBytePos < 0 || nBytePos >= nSize ) 487 nBytePos = nSize ? nSize - 1 : 0; 488 nPage = nBytePos / nPageSize; 489 nOffset = (short) ( nBytePos % nPageSize ); 490 nPos = nBytePos; 491 nPage = GetPage( (short) nPage, sal_False ); 492 return sal_Bool( nPage >= 0 ); 493 } 494 495 // Retrieve the physical page for a given byte offset. 496 // Since Pos2Page() already has computed the physical offset, 497 // use the byte offset directly. 498 499 StgPage* StgFATStrm::GetPhysPage( sal_Int32 nBytePos, sal_Bool bForce ) 500 { 501 return rIo.Get( nBytePos / ( nPageSize >> 2 ), bForce ); 502 } 503 504 // Get the page number entry for the given page offset. 505 506 sal_Int32 StgFATStrm::GetPage( short nOff, sal_Bool bMake, sal_uInt16 *pnMasterAlloc ) 507 { 508 if( pnMasterAlloc ) *pnMasterAlloc = 0; 509 if( nOff < rIo.aHdr.GetFAT1Size() ) 510 return rIo.aHdr.GetFATPage( nOff ); 511 sal_Int32 nMaxPage = nSize >> 2; 512 nOff = nOff - rIo.aHdr.GetFAT1Size(); 513 // Anzahl der Masterpages, durch die wir iterieren muessen 514 sal_uInt16 nMasterCount = ( nPageSize >> 2 ) - 1; 515 sal_uInt16 nBlocks = nOff / nMasterCount; 516 // Offset in letzter Masterpage 517 nOff = nOff % nMasterCount; 518 519 StgPage* pOldPage = 0; 520 StgPage* pMaster = 0; 521 sal_Int32 nFAT = rIo.aHdr.GetFATChain(); 522 for( sal_uInt16 nCount = 0; nCount <= nBlocks; nCount++ ) 523 { 524 if( nFAT == STG_EOF || nFAT == STG_FREE ) 525 { 526 if( bMake ) 527 { 528 // create a new master page 529 nFAT = nMaxPage++; 530 pMaster = rIo.Copy( nFAT, STG_FREE ); 531 if ( pMaster ) 532 { 533 for( short k = 0; k < ( nPageSize >> 2 ); k++ ) 534 pMaster->SetPage( k, STG_FREE ); 535 // Verkettung herstellen 536 if( !pOldPage ) 537 rIo.aHdr.SetFATChain( nFAT ); 538 else 539 pOldPage->SetPage( nMasterCount, nFAT ); 540 if( nMaxPage >= rIo.GetPhysPages() ) 541 if( !rIo.SetSize( nMaxPage ) ) 542 return STG_EOF; 543 // mark the page as used 544 // Platz fuer Masterpage schaffen 545 if( !pnMasterAlloc ) // Selbst Platz schaffen 546 { 547 if( !Pos2Page( nFAT << 2 ) ) 548 return STG_EOF; 549 StgPage* pPg = rIo.Get( nPage, sal_True ); 550 if( !pPg ) 551 return STG_EOF; 552 pPg->SetPage( nOffset >> 2, STG_MASTER ); 553 } 554 else 555 (*pnMasterAlloc)++; 556 rIo.aHdr.SetMasters( nCount + 1 ); 557 pOldPage = pMaster; 558 } 559 } 560 } 561 else 562 { 563 pMaster = rIo.Get( nFAT, sal_True ); 564 if ( pMaster ) 565 { 566 nFAT = pMaster->GetPage( nMasterCount ); 567 pOldPage = pMaster; 568 } 569 } 570 } 571 if( pMaster ) 572 return pMaster->GetPage( nOff ); 573 rIo.SetError( SVSTREAM_GENERALERROR ); 574 return STG_EOF; 575 } 576 577 578 // Set the page number entry for the given page offset. 579 580 sal_Bool StgFATStrm::SetPage( short nOff, sal_Int32 nNewPage ) 581 { 582 sal_Bool bRes = sal_True; 583 if( nOff < rIo.aHdr.GetFAT1Size() ) 584 rIo.aHdr.SetFATPage( nOff, nNewPage ); 585 else 586 { 587 nOff = nOff - rIo.aHdr.GetFAT1Size(); 588 // Anzahl der Masterpages, durch die wir iterieren muessen 589 sal_uInt16 nMasterCount = ( nPageSize >> 2 ) - 1; 590 sal_uInt16 nBlocks = nOff / nMasterCount; 591 // Offset in letzter Masterpage 592 nOff = nOff % nMasterCount; 593 594 StgPage* pMaster = 0; 595 sal_Int32 nFAT = rIo.aHdr.GetFATChain(); 596 for( sal_uInt16 nCount = 0; nCount <= nBlocks; nCount++ ) 597 { 598 if( nFAT == STG_EOF || nFAT == STG_FREE ) 599 { 600 pMaster = 0; 601 break; 602 } 603 pMaster = rIo.Get( nFAT, sal_True ); 604 if ( pMaster ) 605 nFAT = pMaster->GetPage( nMasterCount ); 606 } 607 if( pMaster ) 608 pMaster->SetPage( nOff, nNewPage ); 609 else 610 { 611 rIo.SetError( SVSTREAM_GENERALERROR ); 612 bRes = sal_False; 613 } 614 } 615 616 // lock the page against access 617 if( bRes ) 618 { 619 Pos2Page( nNewPage << 2 ); 620 StgPage* pPg = rIo.Get( nPage, sal_True ); 621 if( pPg ) 622 pPg->SetPage( nOffset >> 2, STG_FAT ); 623 else 624 bRes = sal_False; 625 } 626 return bRes; 627 } 628 629 sal_Bool StgFATStrm::SetSize( sal_Int32 nBytes ) 630 { 631 // Set the number of entries to a multiple of the page size 632 short nOld = (short) ( ( nSize + ( nPageSize - 1 ) ) / nPageSize ); 633 short nNew = (short) ( 634 ( nBytes + ( nPageSize - 1 ) ) / nPageSize ) ; 635 if( nNew < nOld ) 636 { 637 // release master pages 638 for( short i = nNew; i < nOld; i++ ) 639 SetPage( i, STG_FREE ); 640 } 641 else 642 { 643 while( nOld < nNew ) 644 { 645 // allocate master pages 646 // find a free master page slot 647 sal_Int32 nPg = 0; 648 sal_uInt16 nMasterAlloc = 0; 649 nPg = GetPage( nOld, sal_True, &nMasterAlloc ); 650 if( nPg == STG_EOF ) 651 return sal_False; 652 // 4 Bytes have been used for Allocation of each MegaMasterPage 653 nBytes += nMasterAlloc << 2; 654 655 // find a free page using the FAT allocator 656 sal_Int32 n = 1; 657 sal_Int32 nNewPage = pFat->FindBlock( n ); 658 if( nNewPage == STG_EOF ) 659 { 660 // no free pages found; create a new page 661 // Since all pages are allocated, extend 662 // the file size for the next page! 663 nNewPage = nSize >> 2; 664 // if a MegaMasterPage was created avoid taking 665 // the same Page 666 nNewPage += nMasterAlloc; 667 // adjust the file size if necessary 668 if( nNewPage >= rIo.GetPhysPages() ) 669 if( !rIo.SetSize( nNewPage + 1 ) ) 670 return sal_False; 671 } 672 // Set up the page with empty entries 673 StgPage* pPg = rIo.Copy( nNewPage, STG_FREE ); 674 if ( !pPg ) 675 return sal_False; 676 for( short j = 0; j < ( nPageSize >> 2 ); j++ ) 677 pPg->SetPage( j, STG_FREE ); 678 679 // store the page number into the master FAT 680 // Set the size before so the correct FAT can be found 681 nSize = ( nOld + 1 ) * nPageSize; 682 SetPage( nOld, nNewPage ); 683 684 // MegaMasterPages were created, mark it them as used 685 686 sal_uInt32 nMax = rIo.aHdr.GetMasters( ); 687 sal_uInt32 nFAT = rIo.aHdr.GetFATChain(); 688 if( nMasterAlloc ) 689 for( sal_uInt16 nCount = 0; nCount < nMax; nCount++ ) 690 { 691 if( !Pos2Page( nFAT << 2 ) ) 692 return sal_False; 693 if( nMax - nCount <= nMasterAlloc ) 694 { 695 StgPage* piPg = rIo.Get( nPage, sal_True ); 696 if( !piPg ) 697 return sal_False; 698 piPg->SetPage( nOffset >> 2, STG_MASTER ); 699 } 700 StgPage* pPage = rIo.Get( nFAT, sal_True ); 701 if( !pPage ) return sal_False; 702 nFAT = pPage->GetPage( (nPageSize >> 2 ) - 1 ); 703 } 704 705 nOld++; 706 // We have used up 4 bytes for the STG_FAT entry 707 nBytes += 4; 708 nNew = (short) ( 709 ( nBytes + ( nPageSize - 1 ) ) / nPageSize ); 710 } 711 } 712 nSize = nNew * nPageSize; 713 rIo.aHdr.SetFATSize( nNew ); 714 return sal_True; 715 } 716 717 /////////////////////////// class StgDataStrm ////////////////////////////// 718 719 // This class is a normal physical stream which can be initialized 720 // either with an existing dir entry or an existing FAT chain. 721 // The stream has a size increment which normally is 1, but which can be 722 // set to any value is you want the size to be incremented by certain values. 723 724 StgDataStrm::StgDataStrm( StgIo& r, sal_Int32 nBgn, sal_Int32 nLen ) : StgStrm( r ) 725 { 726 Init( nBgn, nLen ); 727 } 728 729 StgDataStrm::StgDataStrm( StgIo& r, StgDirEntry* p ) : StgStrm( r ) 730 { 731 pEntry = p; 732 Init( p->aEntry.GetLeaf( STG_DATA ), 733 p->aEntry.GetSize() ); 734 } 735 736 void StgDataStrm::Init( sal_Int32 nBgn, sal_Int32 nLen ) 737 { 738 pFat = new StgFAT( *rIo.pFAT, sal_True ); 739 nStart = nPage = nBgn; 740 nSize = nLen; 741 nIncr = 1; 742 nOffset = 0; 743 if( nLen < 0 ) 744 { 745 // determine the actual size of the stream by scanning 746 // the FAT chain and counting the # of pages allocated 747 nSize = 0; 748 sal_Int32 nOldBgn = -1; 749 while( nBgn >= 0 && nBgn != nOldBgn ) 750 { 751 nOldBgn = nBgn; 752 nBgn = pFat->GetNextPage( nBgn ); 753 if( nBgn == nOldBgn ) 754 rIo.SetError( ERRCODE_IO_WRONGFORMAT ); 755 nSize += nPageSize; 756 } 757 } 758 } 759 760 // Set the size of a physical stream. 761 762 sal_Bool StgDataStrm::SetSize( sal_Int32 nBytes ) 763 { 764 nBytes = ( ( nBytes + nIncr - 1 ) / nIncr ) * nIncr; 765 sal_Int32 nOldSz = nSize; 766 if( ( nOldSz != nBytes ) ) 767 { 768 if( !StgStrm::SetSize( nBytes ) ) 769 return sal_False; 770 sal_Int32 nMaxPage = pFat->GetMaxPage(); 771 if( nMaxPage > rIo.GetPhysPages() ) 772 if( !rIo.SetSize( nMaxPage ) ) 773 return sal_False; 774 // If we only allocated one page or less, create this 775 // page in the cache for faster throughput. The current 776 // position is the former EOF point. 777 if( ( nSize - 1 ) / nPageSize - ( nOldSz - 1 ) / nPageSize == 1 ) 778 { 779 Pos2Page( nBytes ); 780 if( nPage >= 0 ) 781 rIo.Copy( nPage, STG_FREE ); 782 } 783 } 784 return sal_True; 785 } 786 787 // Get the address of the data byte at a specified offset. 788 // If bForce = sal_True, a read of non-existent data causes 789 // a read fault. 790 791 void* StgDataStrm::GetPtr( sal_Int32 Pos, sal_Bool bForce, sal_Bool bDirty ) 792 { 793 if( Pos2Page( Pos ) ) 794 { 795 StgPage* pPg = rIo.Get( nPage, bForce ); 796 if( pPg ) 797 { 798 pPg->SetOwner( pEntry ); 799 if( bDirty ) 800 pPg->SetDirty(); 801 return ((sal_uInt8 *)pPg->GetData()) + nOffset; 802 } 803 } 804 return NULL; 805 } 806 807 // This could easily be adapted to a better algorithm by determining 808 // the amount of consecutable blocks before doing a read. The result 809 // is the number of bytes read. No error is generated on EOF. 810 811 sal_Int32 StgDataStrm::Read( void* pBuf, sal_Int32 n ) 812 { 813 if ( n < 0 ) 814 return 0; 815 816 if( ( nPos + n ) > nSize ) 817 n = nSize - nPos; 818 sal_Int32 nDone = 0; 819 while( n ) 820 { 821 short nBytes = nPageSize - nOffset; 822 short nRes; 823 StgPage* pPg; 824 if( (sal_Int32) nBytes > n ) 825 nBytes = (short) n; 826 if( nBytes ) 827 { 828 void *p = (sal_uInt8 *) pBuf + nDone; 829 if( nBytes == nPageSize ) 830 { 831 pPg = rIo.Find( nPage ); 832 if( pPg ) 833 { 834 // data is present, so use the cached data 835 pPg->SetOwner( pEntry ); 836 memcpy( p, pPg->GetData(), nBytes ); 837 nRes = nBytes; 838 } 839 else 840 // do a direct (unbuffered) read 841 nRes = (short) rIo.Read( nPage, p, 1 ) * nPageSize; 842 } 843 else 844 { 845 // partial block read thru the cache. 846 pPg = rIo.Get( nPage, sal_False ); 847 if( !pPg ) 848 break; 849 pPg->SetOwner( pEntry ); 850 memcpy( p, (sal_uInt8*)pPg->GetData() + nOffset, nBytes ); 851 nRes = nBytes; 852 } 853 nDone += nRes; 854 nPos += nRes; 855 n -= nRes; 856 nOffset = nOffset + nRes; 857 if( nRes != nBytes ) 858 break; // read error or EOF 859 } 860 // Switch to next page if necessary 861 if( nOffset >= nPageSize && !Pos2Page( nPos ) ) 862 break; 863 } 864 return nDone; 865 } 866 867 sal_Int32 StgDataStrm::Write( const void* pBuf, sal_Int32 n ) 868 { 869 sal_Int32 nDone = 0; 870 if( ( nPos + n ) > nSize ) 871 { 872 sal_Int32 nOld = nPos; 873 if( !SetSize( nPos + n ) ) 874 return sal_False; 875 Pos2Page( nOld ); 876 } 877 while( n ) 878 { 879 short nBytes = nPageSize - nOffset; 880 short nRes; 881 StgPage* pPg; 882 if( (sal_Int32) nBytes > n ) 883 nBytes = (short) n; 884 if( nBytes ) 885 { 886 const void *p = (const sal_uInt8 *) pBuf + nDone; 887 if( nBytes == nPageSize ) 888 { 889 pPg = rIo.Find( nPage ); 890 if( pPg ) 891 { 892 // data is present, so use the cached data 893 pPg->SetOwner( pEntry ); 894 memcpy( pPg->GetData(), p, nBytes ); 895 pPg->SetDirty(); 896 nRes = nBytes; 897 } 898 else 899 // do a direct (unbuffered) write 900 nRes = (short) rIo.Write( nPage, (void*) p, 1 ) * nPageSize; 901 } 902 else 903 { 904 // partial block read thru the cache. 905 pPg = rIo.Get( nPage, sal_False ); 906 if( !pPg ) 907 break; 908 pPg->SetOwner( pEntry ); 909 memcpy( (sal_uInt8*)pPg->GetData() + nOffset, p, nBytes ); 910 pPg->SetDirty(); 911 nRes = nBytes; 912 } 913 nDone += nRes; 914 nPos += nRes; 915 n -= nRes; 916 nOffset = nOffset + nRes; 917 if( nRes != nBytes ) 918 break; // read error 919 } 920 // Switch to next page if necessary 921 if( nOffset >= nPageSize && !Pos2Page( nPos ) ) 922 break; 923 } 924 return nDone; 925 } 926 927 //////////////////////////// class StgSmallStream /////////////////////////// 928 929 // The small stream class provides access to streams with a size < 4096 bytes. 930 // This stream is a StgStream containing small pages. The FAT for this stream 931 // is also a StgStream. The start of the FAT is in the header at DataRootPage, 932 // the stream itself is pointed to by the root entry (it holds start & size). 933 934 StgSmallStrm::StgSmallStrm( StgIo& r, sal_Int32 nBgn, sal_Int32 nLen ) : StgStrm( r ) 935 { 936 Init( nBgn, nLen ); 937 } 938 939 StgSmallStrm::StgSmallStrm( StgIo& r, StgDirEntry* p ) : StgStrm( r ) 940 { 941 pEntry = p; 942 Init( p->aEntry.GetLeaf( STG_DATA ), 943 p->aEntry.GetSize() ); 944 } 945 946 void StgSmallStrm::Init( sal_Int32 nBgn, sal_Int32 nLen ) 947 { 948 pFat = new StgFAT( *rIo.pDataFAT, sal_False ); 949 pData = rIo.pDataStrm; 950 nPageSize = rIo.GetDataPageSize(); 951 nStart = 952 nPage = nBgn; 953 nSize = nLen; 954 } 955 956 // This could easily be adapted to a better algorithm by determining 957 // the amount of consecutable blocks before doing a read. The result 958 // is the number of bytes read. No error is generated on EOF. 959 960 sal_Int32 StgSmallStrm::Read( void* pBuf, sal_Int32 n ) 961 { 962 // We can safely assume that reads are not huge, since the 963 // small stream is likely to be < 64 KBytes. 964 if( ( nPos + n ) > nSize ) 965 n = nSize - nPos; 966 short nDone = 0; 967 while( n ) 968 { 969 short nBytes = nPageSize - nOffset; 970 if( (sal_Int32) nBytes > n ) 971 nBytes = (short) n; 972 if( nBytes ) 973 { 974 if( !pData->Pos2Page( nPage * nPageSize + nOffset ) ) 975 break; 976 // all reading thru the stream 977 short nRes = (short) pData->Read( (sal_uInt8*)pBuf + nDone, nBytes ); 978 nDone = nDone + nRes; 979 nPos += nRes; 980 n -= nRes; 981 nOffset = nOffset + nRes; 982 // read problem? 983 if( nRes != nBytes ) 984 break; 985 } 986 // Switch to next page if necessary 987 if( nOffset >= nPageSize && !Pos2Page( nPos ) ) 988 break; 989 } 990 return nDone; 991 } 992 993 sal_Int32 StgSmallStrm::Write( const void* pBuf, sal_Int32 n ) 994 { 995 // you can safely assume that reads are not huge, since the 996 // small stream is likely to be < 64 KBytes. 997 short nDone = 0; 998 if( ( nPos + n ) > nSize ) 999 { 1000 sal_Int32 nOld = nPos; 1001 if( !SetSize( nPos + n ) ) 1002 return sal_False; 1003 Pos2Page( nOld ); 1004 } 1005 while( n ) 1006 { 1007 short nBytes = nPageSize - nOffset; 1008 if( (sal_Int32) nBytes > n ) 1009 nBytes = (short) n; 1010 if( nBytes ) 1011 { 1012 // all writing goes thru the stream 1013 sal_Int32 nDataPos = nPage * nPageSize + nOffset; 1014 if( pData->GetSize() < ( nDataPos + nBytes ) ) 1015 if( !pData->SetSize( nDataPos + nBytes ) ) 1016 break; 1017 if( !pData->Pos2Page( nDataPos ) ) 1018 break; 1019 short nRes = (short) pData->Write( (sal_uInt8*)pBuf + nDone, nBytes ); 1020 nDone = nDone + nRes; 1021 nPos += nRes; 1022 n -= nRes; 1023 nOffset = nOffset + nRes; 1024 // write problem? 1025 if( nRes != nBytes ) 1026 break; 1027 } 1028 // Switch to next page if necessary 1029 if( nOffset >= nPageSize && !Pos2Page( nPos ) ) 1030 break; 1031 } 1032 return nDone; 1033 } 1034 1035 /////////////////////////// class StgTmpStrm ///////////////////////////// 1036 1037 // The temporary stream uses a memory stream if < 32K, otherwise a 1038 // temporary file. 1039 1040 #define THRESHOLD 32768L 1041 1042 StgTmpStrm::StgTmpStrm( sal_uLong nInitSize ) 1043 : SvMemoryStream( nInitSize > THRESHOLD 1044 ? 16 1045 : ( nInitSize ? nInitSize : 16 ), 4096 ) 1046 { 1047 pStrm = NULL; 1048 // this calls FlushData, so all members should be set by this time 1049 SetBufferSize( 0 ); 1050 if( nInitSize > THRESHOLD ) 1051 SetSize( nInitSize ); 1052 } 1053 1054 sal_Bool StgTmpStrm::Copy( StgTmpStrm& rSrc ) 1055 { 1056 sal_uLong n = rSrc.GetSize(); 1057 sal_uLong nCur = rSrc.Tell(); 1058 SetSize( n ); 1059 if( GetError() == SVSTREAM_OK ) 1060 { 1061 sal_uInt8* p = new sal_uInt8[ 4096 ]; 1062 rSrc.Seek( 0L ); 1063 Seek( 0L ); 1064 while( n ) 1065 { 1066 sal_uLong nn = n; 1067 if( nn > 4096 ) 1068 nn = 4096; 1069 if( rSrc.Read( p, nn ) != nn ) 1070 break; 1071 if( Write( p, nn ) != nn ) 1072 break; 1073 n -= nn; 1074 } 1075 delete [] p; 1076 rSrc.Seek( nCur ); 1077 Seek( nCur ); 1078 return sal_Bool( n == 0 ); 1079 } 1080 else 1081 return sal_False; 1082 } 1083 1084 StgTmpStrm::~StgTmpStrm() 1085 { 1086 if( pStrm ) 1087 { 1088 pStrm->Close(); 1089 osl::File::remove( aName ); 1090 delete pStrm; 1091 } 1092 } 1093 1094 sal_uLong StgTmpStrm::GetSize() const 1095 { 1096 sal_uLong n; 1097 if( pStrm ) 1098 { 1099 sal_uLong old = pStrm->Tell(); 1100 n = pStrm->Seek( STREAM_SEEK_TO_END ); 1101 pStrm->Seek( old ); 1102 } 1103 else 1104 n = nEndOfData; 1105 return n; 1106 } 1107 1108 void StgTmpStrm::SetSize( sal_uLong n ) 1109 { 1110 if( pStrm ) 1111 pStrm->SetStreamSize( n ); 1112 else 1113 { 1114 if( n > THRESHOLD ) 1115 { 1116 aName = TempFile::CreateTempName(); 1117 SvFileStream* s = new SvFileStream( aName, STREAM_READWRITE ); 1118 sal_uLong nCur = Tell(); 1119 sal_uLong i = nEndOfData; 1120 if( i ) 1121 { 1122 sal_uInt8* p = new sal_uInt8[ 4096 ]; 1123 Seek( 0L ); 1124 while( i ) 1125 { 1126 sal_uLong nb = ( i > 4096 ) ? 4096 : i; 1127 if( Read( p, nb ) == nb 1128 && s->Write( p, nb ) == nb ) 1129 i -= nb; 1130 else 1131 break; 1132 } 1133 delete [] p; 1134 } 1135 if( !i && n > nEndOfData ) 1136 { 1137 // We have to write one byte at the end of the file 1138 // if the file is bigger than the memstream to see 1139 // if it fits on disk 1140 s->Seek( n - 1 ); 1141 s->Write( &i, 1 ); 1142 s->Flush(); 1143 if( s->GetError() != SVSTREAM_OK ) 1144 i = 1; 1145 } 1146 Seek( nCur ); 1147 s->Seek( nCur ); 1148 if( i ) 1149 { 1150 SetError( s->GetError() ); 1151 delete s; 1152 return; 1153 } 1154 pStrm = s; 1155 // Shrink the memory to 16 bytes, which seems to be the minimum 1156 ReAllocateMemory( - ( (long) nEndOfData - 16 ) ); 1157 } 1158 else 1159 { 1160 if( n > nEndOfData ) 1161 { 1162 sal_uLong nCur = Tell(); 1163 Seek( nEndOfData - 1 ); 1164 *this << (sal_uInt8) 0; 1165 Seek( nCur ); 1166 } 1167 else 1168 nEndOfData = n; 1169 } 1170 } 1171 } 1172 1173 sal_uLong StgTmpStrm::GetData( void* pData, sal_uLong n ) 1174 { 1175 if( pStrm ) 1176 { 1177 n = pStrm->Read( pData, n ); 1178 SetError( pStrm->GetError() ); 1179 return n; 1180 } 1181 else 1182 return SvMemoryStream::GetData( (sal_Char *)pData, n ); 1183 } 1184 1185 sal_uLong StgTmpStrm::PutData( const void* pData, sal_uLong n ) 1186 { 1187 sal_uInt32 nCur = Tell(); 1188 sal_uInt32 nNew = nCur + n; 1189 if( nNew > THRESHOLD && !pStrm ) 1190 { 1191 SetSize( nNew ); 1192 if( GetError() != SVSTREAM_OK ) 1193 return 0; 1194 } 1195 if( pStrm ) 1196 { 1197 nNew = pStrm->Write( pData, n ); 1198 SetError( pStrm->GetError() ); 1199 } 1200 else 1201 nNew = SvMemoryStream::PutData( (sal_Char*)pData, n ); 1202 return nNew; 1203 } 1204 1205 sal_uLong StgTmpStrm::SeekPos( sal_uLong n ) 1206 { 1207 if( n == STREAM_SEEK_TO_END ) 1208 n = GetSize(); 1209 if( n && n > THRESHOLD && !pStrm ) 1210 { 1211 SetSize( n ); 1212 if( GetError() != SVSTREAM_OK ) 1213 return Tell(); 1214 else 1215 return n; 1216 } 1217 else if( pStrm ) 1218 { 1219 n = pStrm->Seek( n ); 1220 SetError( pStrm->GetError() ); 1221 return n; 1222 } 1223 else 1224 return SvMemoryStream::SeekPos( n ); 1225 } 1226 1227 void StgTmpStrm::FlushData() 1228 { 1229 if( pStrm ) 1230 { 1231 pStrm->Flush(); 1232 SetError( pStrm->GetError() ); 1233 } 1234 else 1235 SvMemoryStream::FlushData(); 1236 } 1237 1238