xref: /aoo41x/main/sot/source/sdstor/stgcache.cxx (revision cdf0e10c)
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