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 : #include <string.h>
21 : #include <osl/endian.h>
22 : #include <tools/string.hxx>
23 :
24 : #include "sot/stg.hxx"
25 : #include "stgelem.hxx"
26 : #include "stgcache.hxx"
27 : #include "stgstrms.hxx"
28 : #include "stgdir.hxx"
29 : #include "stgio.hxx"
30 :
31 : //#define CHECK_DIRTY 1
32 : //#define READ_AFTER_WRITE 1
33 :
34 : ////////////////////////////// class StgPage /////////////////////////////
35 : // This class implements buffer functionality. The cache will always return
36 : // a page buffer, even if a read fails. It is up to the caller to determine
37 : // the correctness of the I/O.
38 :
39 5785 : StgPage::StgPage( short nSize, sal_Int32 nPage )
40 : : mnRefCount( 0 )
41 : , mnPage( nPage )
42 5785 : , mpData( new sal_uInt8[ nSize ] )
43 11570 : , mnSize( nSize )
44 : {
45 : OSL_ENSURE( mnSize >= 512, "Unexpected page size is provided!" );
46 : // We will write this data to a permanant file later
47 : // best to clear if first.
48 5785 : memset( mpData, 0, mnSize );
49 5785 : }
50 :
51 17355 : StgPage::~StgPage()
52 : {
53 5785 : delete [] mpData;
54 11570 : }
55 :
56 5785 : rtl::Reference< StgPage > StgPage::Create( short nData, sal_Int32 nPage )
57 : {
58 5785 : return rtl::Reference< StgPage >( new StgPage( nData, nPage ) );
59 : }
60 :
61 11747 : void StgCache::SetToPage ( const rtl::Reference< StgPage > xPage, short nOff, sal_Int32 nVal )
62 : {
63 11747 : if( ( nOff < (short) ( xPage->GetSize() / sizeof( sal_Int32 ) ) ) && nOff >= 0 )
64 : {
65 : #ifdef OSL_BIGENDIAN
66 : nVal = OSL_SWAPDWORD(nVal);
67 : #endif
68 11747 : ((sal_Int32*) xPage->GetData() )[ nOff ] = nVal;
69 11747 : SetDirty( xPage );
70 : }
71 11747 : }
72 :
73 3434 : bool StgPage::IsPageGreater( const StgPage *pA, const StgPage *pB )
74 : {
75 3434 : return pA->mnPage < pB->mnPage;
76 : }
77 :
78 : //////////////////////////////// class StgCache ////////////////////////////
79 :
80 : // The disk cache holds the cached sectors. The sector type differ according
81 : // to their purpose.
82 :
83 102 : static sal_Int32 lcl_GetPageCount( sal_uLong nFileSize, short nPageSize )
84 : {
85 : // return (nFileSize >= 512) ? (nFileSize - 512) / nPageSize : 0;
86 : // #i61980# reallife: last page may be incomplete, return number of *started* pages
87 102 : return (nFileSize >= 512) ? (nFileSize - 512 + nPageSize - 1) / nPageSize : 0;
88 : }
89 :
90 102 : StgCache::StgCache()
91 : : nError( SVSTREAM_OK )
92 : , nPages( 0 )
93 : , nRef( 0 )
94 : , nReplaceIdx( 0 )
95 : , maLRUPages( 8 ) // entries in the LRU lookup
96 : , nPageSize( 512 )
97 : , pStorageStream( NULL )
98 : , pStrm( NULL )
99 : , bMyStream( sal_False )
100 102 : , bFile( sal_False )
101 : {
102 102 : }
103 :
104 204 : StgCache::~StgCache()
105 : {
106 102 : Clear();
107 102 : SetStrm( NULL, sal_False );
108 102 : }
109 :
110 102 : void StgCache::SetPhysPageSize( short n )
111 : {
112 : OSL_ENSURE( n >= 512, "Unexpecte page size is provided!" );
113 102 : if ( n >= 512 )
114 : {
115 102 : nPageSize = n;
116 102 : sal_uLong nPos = pStrm->Tell();
117 102 : sal_uLong nFileSize = pStrm->Seek( STREAM_SEEK_TO_END );
118 102 : nPages = lcl_GetPageCount( nFileSize, nPageSize );
119 102 : pStrm->Seek( nPos );
120 : }
121 102 : }
122 :
123 : // Create a new cache element
124 :
125 5785 : rtl::Reference< StgPage > StgCache::Create( sal_Int32 nPg )
126 : {
127 5785 : rtl::Reference< StgPage > xElem( StgPage::Create( nPageSize, nPg ) );
128 5785 : maLRUPages[ nReplaceIdx++ % maLRUPages.size() ] = xElem;
129 5785 : return xElem;
130 : }
131 :
132 : // Delete the given element
133 :
134 0 : void StgCache::Erase( const rtl::Reference< StgPage > &xElem )
135 : {
136 : OSL_ENSURE( xElem.is(), "The pointer should not be NULL!" );
137 0 : if ( xElem.is() ) {
138 0 : for ( LRUList::iterator it = maLRUPages.begin(); it != maLRUPages.end(); ++it ) {
139 0 : if ( it->is() && (*it)->GetPage() == xElem->GetPage() ) {
140 0 : it->clear();
141 0 : break;
142 : }
143 : }
144 : }
145 0 : }
146 :
147 : // remove all cache elements without flushing them
148 :
149 102 : void StgCache::Clear()
150 : {
151 102 : maDirtyPages.clear();
152 918 : for ( LRUList::iterator it = maLRUPages.begin(); it != maLRUPages.end(); ++it )
153 816 : it->clear();
154 102 : }
155 :
156 : // Look for a cached page
157 :
158 998408 : rtl::Reference< StgPage > StgCache::Find( sal_Int32 nPage )
159 : {
160 4698913 : for ( LRUList::iterator it = maLRUPages.begin(); it != maLRUPages.end(); ++it )
161 4652058 : if ( it->is() && (*it)->GetPage() == nPage )
162 951553 : return *it;
163 46855 : IndexToStgPage::iterator it2 = maDirtyPages.find( nPage );
164 46855 : if ( it2 != maDirtyPages.end() )
165 18297 : return it2->second;
166 28558 : return rtl::Reference< StgPage >();
167 : }
168 :
169 : // Load a page into the cache
170 :
171 974882 : rtl::Reference< StgPage > StgCache::Get( sal_Int32 nPage, sal_Bool bForce )
172 : {
173 974882 : rtl::Reference< StgPage > p = Find( nPage );
174 974882 : if( !p.is() )
175 : {
176 5243 : p = Create( nPage );
177 5243 : if( !Read( nPage, p->GetData(), 1 ) && bForce )
178 : {
179 0 : Erase( p );
180 0 : p.clear();
181 0 : SetError( SVSTREAM_READ_ERROR );
182 : }
183 : }
184 974882 : return p;
185 : }
186 :
187 : // Copy an existing page into a new page. Use this routine
188 : // to duplicate an existing stream or to create new entries.
189 : // The new page is initially marked dirty. No owner is copied.
190 :
191 560 : rtl::Reference< StgPage > StgCache::Copy( sal_Int32 nNew, sal_Int32 nOld )
192 : {
193 560 : rtl::Reference< StgPage > p = Find( nNew );
194 560 : if( !p.is() )
195 542 : p = Create( nNew );
196 560 : if( nOld >= 0 )
197 : {
198 : // old page: we must have this data!
199 0 : rtl::Reference< StgPage > q = Get( nOld, sal_True );
200 0 : if( q.is() )
201 : {
202 : OSL_ENSURE( p->GetSize() == q->GetSize(), "Unexpected page size!" );
203 0 : memcpy( p->GetData(), q->GetData(), p->GetSize() );
204 0 : }
205 : }
206 560 : SetDirty( p );
207 :
208 560 : return p;
209 : }
210 :
211 : // Historically this wrote pages in a sorted, ascending order;
212 : // continue that tradition.
213 35 : sal_Bool StgCache::Commit()
214 : {
215 35 : if ( Good() ) // otherwise Write does nothing
216 : {
217 35 : std::vector< StgPage * > aToWrite;
218 1896 : for ( IndexToStgPage::iterator aIt = maDirtyPages.begin();
219 1264 : aIt != maDirtyPages.end(); ++aIt )
220 597 : aToWrite.push_back( aIt->second.get() );
221 :
222 35 : std::sort( aToWrite.begin(), aToWrite.end(), StgPage::IsPageGreater );
223 1887 : for ( std::vector< StgPage * >::iterator aWr = aToWrite.begin();
224 1258 : aWr != aToWrite.end(); ++aWr)
225 : {
226 597 : const rtl::Reference< StgPage > &pPage = *aWr;
227 597 : if ( !Write( pPage->GetPage(), pPage->GetData(), 1 ) )
228 3 : return sal_False;
229 632 : }
230 : }
231 :
232 32 : maDirtyPages.clear();
233 :
234 32 : pStrm->Flush();
235 32 : SetError( pStrm->GetError() );
236 :
237 32 : return sal_True;
238 : }
239 :
240 : // Set a stream
241 :
242 204 : void StgCache::SetStrm( SvStream* p, sal_Bool bMy )
243 : {
244 204 : if( pStorageStream )
245 : {
246 0 : pStorageStream->ReleaseRef();
247 0 : pStorageStream = NULL;
248 : }
249 :
250 204 : if( bMyStream )
251 0 : delete pStrm;
252 204 : pStrm = p;
253 204 : bMyStream = bMy;
254 204 : }
255 :
256 0 : void StgCache::SetStrm( UCBStorageStream* pStgStream )
257 : {
258 0 : if( pStorageStream )
259 0 : pStorageStream->ReleaseRef();
260 0 : pStorageStream = pStgStream;
261 :
262 0 : if( bMyStream )
263 0 : delete pStrm;
264 :
265 0 : pStrm = NULL;
266 :
267 0 : if ( pStorageStream )
268 : {
269 0 : pStorageStream->AddRef();
270 0 : pStrm = pStorageStream->GetModifySvStream();
271 : }
272 :
273 0 : bMyStream = sal_False;
274 0 : }
275 :
276 15930 : void StgCache::SetDirty( const rtl::Reference< StgPage > &xPage )
277 : {
278 : assert( IsWritable() );
279 15930 : maDirtyPages[ xPage->GetPage() ] = xPage;
280 15930 : }
281 :
282 : // Open/close the disk file
283 :
284 0 : sal_Bool StgCache::Open( const String& rName, StreamMode nMode )
285 : {
286 : // do not open in exclusive mode!
287 0 : if( nMode & STREAM_SHARE_DENYALL )
288 0 : nMode = ( ( nMode & ~STREAM_SHARE_DENYALL ) | STREAM_SHARE_DENYWRITE );
289 0 : SvFileStream* pFileStrm = new SvFileStream( rName, nMode );
290 : // SvStream "Feature" Write Open auch erfolgreich, wenns nicht klappt
291 0 : sal_Bool bAccessDenied = sal_False;
292 0 : if( ( nMode & STREAM_WRITE ) && !pFileStrm->IsWritable() )
293 : {
294 0 : pFileStrm->Close();
295 0 : bAccessDenied = sal_True;
296 : }
297 0 : SetStrm( pFileStrm, sal_True );
298 0 : if( pFileStrm->IsOpen() )
299 : {
300 0 : sal_uLong nFileSize = pStrm->Seek( STREAM_SEEK_TO_END );
301 0 : nPages = lcl_GetPageCount( nFileSize, nPageSize );
302 0 : pStrm->Seek( 0L );
303 : }
304 : else
305 0 : nPages = 0;
306 0 : bFile = sal_True;
307 0 : SetError( bAccessDenied ? ERRCODE_IO_ACCESSDENIED : pStrm->GetError() );
308 0 : return Good();
309 : }
310 :
311 102 : void StgCache::Close()
312 : {
313 102 : if( bFile )
314 : {
315 0 : ((SvFileStream*) pStrm)->Close();
316 0 : SetError( pStrm->GetError() );
317 : }
318 102 : }
319 :
320 : // low level I/O
321 :
322 27460 : sal_Bool StgCache::Read( sal_Int32 nPage, void* pBuf, sal_Int32 nPg )
323 : {
324 27460 : if( Good() )
325 : {
326 : /* #i73846# real life: a storage may refer to a page one-behind the
327 : last valid page (see document attached to the issue). In that case
328 : (if nPage==nPages), just do nothing here and let the caller work on
329 : the empty zero-filled buffer. */
330 27453 : if ( nPage > nPages )
331 0 : SetError( SVSTREAM_READ_ERROR );
332 27453 : else if ( nPage < nPages )
333 : {
334 27453 : sal_uLong nPos = Page2Pos( nPage );
335 27453 : sal_Int32 nPg2 = ( ( nPage + nPg ) > nPages ) ? nPages - nPage : nPg;
336 27453 : sal_uLong nBytes = nPg2 * nPageSize;
337 : // fixed address and size for the header
338 27453 : if( nPage == -1 )
339 : {
340 0 : nPos = 0L, nBytes = 512;
341 0 : nPg2 = nPg;
342 : }
343 27453 : if( pStrm->Tell() != nPos )
344 : {
345 3750 : if( pStrm->Seek( nPos ) != nPos ) {
346 : #ifdef CHECK_DIRTY
347 : ErrorBox( NULL, WB_OK, String("SO2: Seek failed") ).Execute();
348 : #endif
349 : }
350 : }
351 27453 : pStrm->Read( pBuf, nBytes );
352 27453 : if ( nPg != nPg2 )
353 0 : SetError( SVSTREAM_READ_ERROR );
354 : else
355 27453 : SetError( pStrm->GetError() );
356 : }
357 : }
358 27460 : return Good();
359 : }
360 :
361 1153 : sal_Bool StgCache::Write( sal_Int32 nPage, void* pBuf, sal_Int32 nPg )
362 : {
363 1153 : if( Good() )
364 : {
365 1153 : sal_uLong nPos = Page2Pos( nPage );
366 1153 : sal_uLong nBytes = 0;
367 1153 : if ( SAL_MAX_INT32 / nPg > nPageSize )
368 1153 : nBytes = nPg * nPageSize;
369 :
370 : // fixed address and size for the header
371 : // nPageSize must be >= 512, otherwise the header can not be written here, we check it on import
372 1153 : if( nPage == -1 )
373 0 : nPos = 0L, nBytes = 512;
374 1153 : if( pStrm->Tell() != nPos )
375 : {
376 90 : if( pStrm->Seek( nPos ) != nPos ) {
377 : #ifdef CHECK_DIRTY
378 : ErrorBox( NULL, WB_OK, String("SO2: Seek failed") ).Execute();
379 : #endif
380 : }
381 : }
382 1153 : sal_uLong nRes = pStrm->Write( pBuf, nBytes );
383 1153 : if( nRes != nBytes )
384 0 : SetError( SVSTREAM_WRITE_ERROR );
385 : else
386 1153 : SetError( pStrm->GetError() );
387 : #ifdef READ_AFTER_WRITE
388 : sal_uInt8 cBuf[ 512 ];
389 : pStrm->Flush();
390 : pStrm->Seek( nPos );
391 : sal_Bool bRes = ( pStrm->Read( cBuf, 512 ) == 512 );
392 : if( bRes )
393 : bRes = !memcmp( cBuf, pBuf, 512 );
394 : if( !bRes )
395 : {
396 : ErrorBox( NULL, WB_OK, String("SO2: Read after Write failed") ).Execute();
397 : pStrm->SetError( SVSTREAM_WRITE_ERROR );
398 : }
399 : #endif
400 : }
401 1153 : return Good();
402 : }
403 :
404 : // set the file size in pages
405 :
406 488 : sal_Bool StgCache::SetSize( sal_Int32 n )
407 : {
408 : // Add the file header
409 488 : sal_Int32 nSize = n * nPageSize + 512;
410 488 : pStrm->SetStreamSize( nSize );
411 488 : SetError( pStrm->GetError() );
412 488 : if( !nError )
413 488 : nPages = n;
414 488 : return Good();
415 : }
416 :
417 29585 : void StgCache::SetError( sal_uLong n )
418 : {
419 29585 : if( n && !nError )
420 43 : nError = n;
421 29585 : }
422 :
423 244 : void StgCache::ResetError()
424 : {
425 244 : nError = SVSTREAM_OK;
426 244 : pStrm->ResetError();
427 244 : }
428 :
429 954158 : void StgCache::MoveError( StorageBase& r )
430 : {
431 954158 : if( nError != SVSTREAM_OK )
432 : {
433 43 : r.SetError( nError );
434 43 : ResetError();
435 : }
436 954158 : }
437 :
438 : // Utility functions
439 :
440 28606 : sal_Int32 StgCache::Page2Pos( sal_Int32 nPage )
441 : {
442 28606 : if( nPage < 0 ) nPage = 0;
443 28606 : return( nPage * nPageSize ) + nPageSize;
444 : }
445 :
446 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|