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