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