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