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