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