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 #if defined(_MSC_VER) && (_MSC_VER<1200) 32 #include <tools/presys.h> 33 #endif 34 #include <hash_map> 35 #if defined(_MSC_VER) && (_MSC_VER<1200) 36 #include <tools/postsys.h> 37 #endif 38 #include <vos/macros.hxx> 39 40 #include <string.h> 41 #include <osl/endian.h> 42 #include <tools/string.hxx> 43 44 #include "sot/stg.hxx" 45 #include "stgelem.hxx" 46 #include "stgcache.hxx" 47 #include "stgstrms.hxx" 48 #include "stgdir.hxx" 49 #include "stgio.hxx" 50 51 /*************************************************************************/ 52 //----------------------------------------------------------------------------- 53 typedef std::hash_map 54 < 55 sal_Int32, 56 StgPage *, 57 std::hash< sal_Int32 >, 58 NAMESPACE_STD(equal_to)< sal_Int32 > 59 > UsrStgPagePtr_Impl; 60 #ifdef _MSC_VER 61 #pragma warning( disable: 4786 ) 62 #endif 63 64 //#define CHECK_DIRTY 1 65 //#define READ_AFTER_WRITE 1 66 67 ////////////////////////////// class StgPage ///////////////////////////// 68 // This class implements buffer functionality. The cache will always return 69 // a page buffer, even if a read fails. It is up to the caller to determine 70 // the correctness of the I/O. 71 72 StgPage::StgPage( StgCache* p, short n ) 73 { 74 pCache = p; 75 nData = n; 76 bDirty = sal_False; 77 nPage = 0; 78 pData = new sal_uInt8[ nData ]; 79 pNext1 = 80 pNext2 = 81 pLast1 = 82 pLast2 = NULL; 83 pOwner = NULL; 84 } 85 86 StgPage::~StgPage() 87 { 88 delete [] pData; 89 } 90 91 void StgPage::SetPage( short nOff, sal_Int32 nVal ) 92 { 93 if( ( nOff < (short) ( nData / sizeof( sal_Int32 ) ) ) && nOff >= 0 ) 94 { 95 #ifdef OSL_BIGENDIAN 96 nVal = SWAPLONG(nVal); 97 #endif 98 ((sal_Int32*) pData )[ nOff ] = nVal; 99 bDirty = sal_True; 100 } 101 } 102 103 //////////////////////////////// class StgCache //////////////////////////// 104 105 // The disk cache holds the cached sectors. The sector type differ according 106 // to their purpose. 107 108 sal_Int32 lcl_GetPageCount( sal_uLong nFileSize, short nPageSize ) 109 { 110 // return (nFileSize >= 512) ? (nFileSize - 512) / nPageSize : 0; 111 // #i61980# reallife: last page may be incomplete, return number of *started* pages 112 return (nFileSize >= 512) ? (nFileSize - 512 + nPageSize - 1) / nPageSize : 0; 113 } 114 115 StgCache::StgCache() 116 { 117 nRef = 0; 118 pStrm = NULL; 119 pCur = pElem1 = NULL; 120 nPageSize = 512; 121 nError = SVSTREAM_OK; 122 bMyStream = sal_False; 123 bFile = sal_False; 124 pLRUCache = NULL; 125 pStorageStream = NULL; 126 } 127 128 StgCache::~StgCache() 129 { 130 Clear(); 131 SetStrm( NULL, sal_False ); 132 delete (UsrStgPagePtr_Impl*)pLRUCache; 133 } 134 135 void StgCache::SetPhysPageSize( short n ) 136 { 137 nPageSize = n; 138 sal_uLong nPos = pStrm->Tell(); 139 sal_uLong nFileSize = pStrm->Seek( STREAM_SEEK_TO_END ); 140 nPages = lcl_GetPageCount( nFileSize, nPageSize ); 141 pStrm->Seek( nPos ); 142 } 143 144 // Create a new cache element 145 // pCur points to this element 146 147 StgPage* StgCache::Create( sal_Int32 nPg ) 148 { 149 StgPage* pElem = new StgPage( this, nPageSize ); 150 pElem->nPage = nPg; 151 // For data security, clear the buffer contents 152 memset( pElem->pData, 0, pElem->nData ); 153 154 // insert to LRU 155 if( pCur ) 156 { 157 pElem->pNext1 = pCur; 158 pElem->pLast1 = pCur->pLast1; 159 pElem->pNext1->pLast1 = 160 pElem->pLast1->pNext1 = pElem; 161 } 162 else 163 pElem->pNext1 = pElem->pLast1 = pElem; 164 if( !pLRUCache ) 165 pLRUCache = new UsrStgPagePtr_Impl(); 166 (*(UsrStgPagePtr_Impl*)pLRUCache)[pElem->nPage] = pElem; 167 pCur = pElem; 168 169 // insert to Sorted 170 if( !pElem1 ) 171 pElem1 = pElem->pNext2 = pElem->pLast2 = pElem; 172 else 173 { 174 StgPage* p = pElem1; 175 do 176 { 177 if( pElem->nPage < p->nPage ) 178 break; 179 p = p->pNext2; 180 } while( p != pElem1 ); 181 pElem->pNext2 = p; 182 pElem->pLast2 = p->pLast2; 183 pElem->pNext2->pLast2 = 184 pElem->pLast2->pNext2 = pElem; 185 if( p->nPage < pElem1->nPage ) 186 pElem1 = pElem; 187 } 188 return pElem; 189 } 190 191 // Delete the given element 192 193 void StgCache::Erase( StgPage* pElem ) 194 { 195 //remove from LRU 196 pElem->pNext1->pLast1 = pElem->pLast1; 197 pElem->pLast1->pNext1 = pElem->pNext1; 198 if( pCur == pElem ) 199 pCur = ( pElem->pNext1 == pElem ) ? NULL : pElem->pNext1; 200 if( pLRUCache ) 201 ((UsrStgPagePtr_Impl*)pLRUCache)->erase( pElem->nPage ); 202 // remove from Sorted 203 pElem->pNext2->pLast2 = pElem->pLast2; 204 pElem->pLast2->pNext2 = pElem->pNext2; 205 if( pElem1 == pElem ) 206 pElem1 = ( pElem->pNext2 == pElem ) ? NULL : pElem->pNext2; 207 delete pElem; 208 } 209 210 // remove all cache elements without flushing them 211 212 void StgCache::Clear() 213 { 214 StgPage* pElem = pCur; 215 if( pCur ) do 216 { 217 StgPage* pDelete = pElem; 218 pElem = pElem->pNext1; 219 delete pDelete; 220 } 221 while( pCur != pElem ); 222 pCur = NULL; 223 pElem1 = NULL; 224 delete (UsrStgPagePtr_Impl*)pLRUCache; 225 pLRUCache = NULL; 226 } 227 228 // Look for a cached page 229 230 StgPage* StgCache::Find( sal_Int32 nPage ) 231 { 232 if( !pLRUCache ) 233 return NULL; 234 UsrStgPagePtr_Impl::iterator aIt = ((UsrStgPagePtr_Impl*)pLRUCache)->find( nPage ); 235 if( aIt != ((UsrStgPagePtr_Impl*)pLRUCache)->end() ) 236 { 237 // page found 238 StgPage* pFound = (*aIt).second; 239 240 if( pFound != pCur ) 241 { 242 // remove from LRU 243 pFound->pNext1->pLast1 = pFound->pLast1; 244 pFound->pLast1->pNext1 = pFound->pNext1; 245 // insert to LRU 246 pFound->pNext1 = pCur; 247 pFound->pLast1 = pCur->pLast1; 248 pFound->pNext1->pLast1 = 249 pFound->pLast1->pNext1 = pFound; 250 } 251 return pFound; 252 } 253 return NULL; 254 } 255 256 // Load a page into the cache 257 258 StgPage* StgCache::Get( sal_Int32 nPage, sal_Bool bForce ) 259 { 260 StgPage* p = Find( nPage ); 261 if( !p ) 262 { 263 p = Create( nPage ); 264 if( !Read( nPage, p->pData, 1 ) && bForce ) 265 { 266 Erase( p ); 267 p = NULL; 268 SetError( SVSTREAM_READ_ERROR ); 269 } 270 } 271 return p; 272 } 273 274 // Copy an existing page into a new page. Use this routine 275 // to duplicate an existing stream or to create new entries. 276 // The new page is initially marked dirty. No owner is copied. 277 278 StgPage* StgCache::Copy( sal_Int32 nNew, sal_Int32 nOld ) 279 { 280 StgPage* p = Find( nNew ); 281 if( !p ) 282 p = Create( nNew ); 283 if( nOld >= 0 ) 284 { 285 // old page: we must have this data! 286 StgPage* q = Get( nOld, sal_True ); 287 if( q ) 288 memcpy( p->pData, q->pData, p->nData ); 289 } 290 p->SetDirty(); 291 return p; 292 } 293 294 // Flush the cache whose owner is given. NULL flushes all. 295 296 sal_Bool StgCache::Commit( StgDirEntry* ) 297 { 298 StgPage* p = pElem1; 299 if( p ) do 300 { 301 if( p->bDirty ) 302 { 303 sal_Bool b = Write( p->nPage, p->pData, 1 ); 304 if( !b ) 305 return sal_False; 306 p->bDirty = sal_False; 307 } 308 p = p->pNext2; 309 } while( p != pElem1 ); 310 pStrm->Flush(); 311 SetError( pStrm->GetError() ); 312 #ifdef CHECK_DIRTY 313 p = pElem1; 314 if( p ) do 315 { 316 if( p->bDirty ) 317 { 318 ErrorBox( NULL, WB_OK, String("SO2: Dirty Block in Ordered List") ).Execute(); 319 sal_Bool b = Write( p->nPage, p->pData, 1 ); 320 if( !b ) 321 return sal_False; 322 p->bDirty = sal_False; 323 } 324 p = p->pNext2; 325 } while( p != pElem1 ); 326 p = pElem1; 327 if( p ) do 328 { 329 if( p->bDirty ) 330 { 331 ErrorBox( NULL, WB_OK, String("SO2: Dirty Block in LRU List") ).Execute(); 332 sal_Bool b = Write( p->nPage, p->pData, 1 ); 333 if( !b ) 334 return sal_False; 335 p->bDirty = sal_False; 336 } 337 p = p->pNext1; 338 } while( p != pElem1 ); 339 #endif 340 return sal_True; 341 } 342 343 void StgCache::Revert( StgDirEntry* ) 344 {} 345 346 // Set a stream 347 348 void StgCache::SetStrm( SvStream* p, sal_Bool bMy ) 349 { 350 if( pStorageStream ) 351 { 352 pStorageStream->ReleaseRef(); 353 pStorageStream = NULL; 354 } 355 356 if( bMyStream ) 357 delete pStrm; 358 pStrm = p; 359 bMyStream = bMy; 360 } 361 362 void StgCache::SetStrm( UCBStorageStream* pStgStream ) 363 { 364 if( pStorageStream ) 365 pStorageStream->ReleaseRef(); 366 pStorageStream = pStgStream; 367 368 if( bMyStream ) 369 delete pStrm; 370 371 pStrm = NULL; 372 373 if ( pStorageStream ) 374 { 375 pStorageStream->AddRef(); 376 pStrm = pStorageStream->GetModifySvStream(); 377 } 378 379 bMyStream = sal_False; 380 } 381 382 // Open/close the disk file 383 384 sal_Bool StgCache::Open( const String& rName, StreamMode nMode ) 385 { 386 // do not open in exclusive mode! 387 if( nMode & STREAM_SHARE_DENYALL ) 388 nMode = ( ( nMode & ~STREAM_SHARE_DENYALL ) | STREAM_SHARE_DENYWRITE ); 389 SvFileStream* pFileStrm = new SvFileStream( rName, nMode ); 390 // SvStream "Feature" Write Open auch erfolgreich, wenns nicht klappt 391 sal_Bool bAccessDenied = sal_False; 392 if( ( nMode & STREAM_WRITE ) && !pFileStrm->IsWritable() ) 393 { 394 pFileStrm->Close(); 395 bAccessDenied = sal_True; 396 } 397 SetStrm( pFileStrm, sal_True ); 398 if( pFileStrm->IsOpen() ) 399 { 400 sal_uLong nFileSize = pStrm->Seek( STREAM_SEEK_TO_END ); 401 nPages = lcl_GetPageCount( nFileSize, nPageSize ); 402 pStrm->Seek( 0L ); 403 } 404 else 405 nPages = 0; 406 bFile = sal_True; 407 SetError( bAccessDenied ? ERRCODE_IO_ACCESSDENIED : pStrm->GetError() ); 408 return Good(); 409 } 410 411 void StgCache::Close() 412 { 413 if( bFile ) 414 { 415 ((SvFileStream*) pStrm)->Close(); 416 SetError( pStrm->GetError() ); 417 } 418 } 419 420 // low level I/O 421 422 sal_Bool StgCache::Read( sal_Int32 nPage, void* pBuf, sal_Int32 nPg ) 423 { 424 if( Good() ) 425 { 426 /* #i73846# real life: a storage may refer to a page one-behind the 427 last valid page (see document attached to the issue). In that case 428 (if nPage==nPages), just do nothing here and let the caller work on 429 the empty zero-filled buffer. */ 430 if ( nPage > nPages ) 431 SetError( SVSTREAM_READ_ERROR ); 432 else if ( nPage < nPages ) 433 { 434 sal_uLong nPos = Page2Pos( nPage ); 435 sal_Int32 nPg2 = ( ( nPage + nPg ) > nPages ) ? nPages - nPage : nPg; 436 sal_uLong nBytes = nPg2 * nPageSize; 437 // fixed address and size for the header 438 if( nPage == -1 ) 439 { 440 nPos = 0L, nBytes = 512; 441 nPg2 = nPg; 442 } 443 if( pStrm->Tell() != nPos ) 444 { 445 if( pStrm->Seek( nPos ) != nPos ) { 446 #ifdef CHECK_DIRTY 447 ErrorBox( NULL, WB_OK, String("SO2: Seek failed") ).Execute(); 448 #endif 449 } 450 } 451 pStrm->Read( pBuf, nBytes ); 452 if ( nPg != nPg2 ) 453 SetError( SVSTREAM_READ_ERROR ); 454 else 455 SetError( pStrm->GetError() ); 456 } 457 } 458 return Good(); 459 } 460 461 sal_Bool StgCache::Write( sal_Int32 nPage, void* pBuf, sal_Int32 nPg ) 462 { 463 if( Good() ) 464 { 465 sal_uLong nPos = Page2Pos( nPage ); 466 sal_uLong nBytes = nPg * nPageSize; 467 // fixed address and size for the header 468 if( nPage == -1 ) 469 nPos = 0L, nBytes = 512; 470 if( pStrm->Tell() != nPos ) 471 { 472 if( pStrm->Seek( nPos ) != nPos ) { 473 #ifdef CHECK_DIRTY 474 ErrorBox( NULL, WB_OK, String("SO2: Seek failed") ).Execute(); 475 #endif 476 } 477 } 478 sal_uLong nRes = pStrm->Write( pBuf, nBytes ); 479 if( nRes != nBytes ) 480 SetError( SVSTREAM_WRITE_ERROR ); 481 else 482 SetError( pStrm->GetError() ); 483 #ifdef READ_AFTER_WRITE 484 sal_uInt8 cBuf[ 512 ]; 485 pStrm->Flush(); 486 pStrm->Seek( nPos ); 487 sal_Bool bRes = ( pStrm->Read( cBuf, 512 ) == 512 ); 488 if( bRes ) 489 bRes = !memcmp( cBuf, pBuf, 512 ); 490 if( !bRes ) 491 { 492 ErrorBox( NULL, WB_OK, String("SO2: Read after Write failed") ).Execute(); 493 pStrm->SetError( SVSTREAM_WRITE_ERROR ); 494 } 495 #endif 496 } 497 return Good(); 498 } 499 500 // set the file size in pages 501 502 sal_Bool StgCache::SetSize( sal_Int32 n ) 503 { 504 // Add the file header 505 sal_Int32 nSize = n * nPageSize + 512; 506 pStrm->SetStreamSize( nSize ); 507 SetError( pStrm->GetError() ); 508 if( !nError ) 509 nPages = n; 510 return Good(); 511 } 512 513 void StgCache::SetError( sal_uLong n ) 514 { 515 if( n && !nError ) 516 nError = n; 517 } 518 519 void StgCache::ResetError() 520 { 521 nError = SVSTREAM_OK; 522 pStrm->ResetError(); 523 } 524 525 void StgCache::MoveError( StorageBase& r ) 526 { 527 if( nError != SVSTREAM_OK ) 528 { 529 r.SetError( nError ); 530 ResetError(); 531 } 532 } 533 534 // Utility functions 535 536 sal_Int32 StgCache::Page2Pos( sal_Int32 nPage ) 537 { 538 if( nPage < 0 ) nPage = 0; 539 return( nPage * nPageSize ) + nPageSize; 540 } 541 542 sal_Int32 StgCache::Pos2Page( sal_Int32 nPos ) 543 { 544 return ( ( nPos + nPageSize - 1 ) / nPageSize ) * nPageSize - 1; 545 } 546 547