Bug Summary

File:home/maarten/src/libreoffice/core/sot/source/sdstor/stgstrms.cxx
Warning:line 721, column 28
The result of the left shift is undefined because the left operand is negative

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name stgstrms.cxx -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/11.0.0 -D BOOST_ERROR_CODE_HEADER_ONLY -D BOOST_SYSTEM_NO_DEPRECATED -D CPPU_ENV=gcc3 -D LINUX -D OSL_DEBUG_LEVEL=1 -D SAL_LOG_INFO -D SAL_LOG_WARN -D UNIX -D UNX -D X86_64 -D _PTHREADS -D _REENTRANT -D SOT_DLLIMPLEMENTATION -D EXCEPTIONS_ON -D LIBO_INTERNAL_ONLY -I /home/maarten/src/libreoffice/core/sot/inc -I /home/maarten/src/libreoffice/core/external/boost/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/boost -I /home/maarten/src/libreoffice/core/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include/linux -I /home/maarten/src/libreoffice/core/config_host -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/udkapi/normal -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/offapi/normal -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10 -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/x86_64-redhat-linux -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/backward -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/11.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -O0 -Wno-missing-braces -std=c++17 -fdeprecated-macro -fdebug-compilation-dir /home/maarten/src/libreoffice/core -ferror-limit 19 -fvisibility hidden -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcxx-exceptions -fexceptions -debug-info-kind=constructor -analyzer-output=html -faddrsig -o /home/maarten/tmp/wis/scan-build-libreoffice/output/report/2020-10-07-141433-9725-1 -x c++ /home/maarten/src/libreoffice/core/sot/source/sdstor/stgstrms.cxx
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 <algorithm>
21
22#include <string.h>
23#include <sal/log.hxx>
24#include <o3tl/safeint.hxx>
25#include <osl/file.hxx>
26#include <unotools/tempfile.hxx>
27
28#include "stgelem.hxx"
29#include "stgcache.hxx"
30#include "stgstrms.hxx"
31#include "stgdir.hxx"
32#include "stgio.hxx"
33#include <memory>
34
35///////////////////////////// class StgFAT
36
37// The FAT class performs FAT operations on an underlying storage stream.
38// This stream is either the master FAT stream (m == true ) or a normal
39// storage stream, which then holds the FAT for small data allocations.
40
41StgFAT::StgFAT( StgStrm& r, bool m ) : m_rStrm( r )
42{
43 m_bPhys = m;
44 m_nPageSize = m_rStrm.GetIo().GetPhysPageSize();
45 m_nEntries = m_nPageSize >> 2;
46 m_nOffset = 0;
47 m_nMaxPage = 0;
48 m_nLimit = 0;
49}
50
51// Retrieve the physical page for a given byte offset.
52
53rtl::Reference< StgPage > StgFAT::GetPhysPage( sal_Int32 nByteOff )
54{
55 rtl::Reference< StgPage > pPg;
56 // Position within the underlying stream
57 // use the Pos2Page() method of the stream
58 if( m_rStrm.Pos2Page( nByteOff ) )
59 {
60 m_nOffset = m_rStrm.GetOffset();
61 sal_Int32 nPhysPage = m_rStrm.GetPage();
62 // get the physical page (must be present)
63 pPg = m_rStrm.GetIo().Get( nPhysPage, true );
64 }
65 return pPg;
66}
67
68// Get the follow page for a certain FAT page.
69
70sal_Int32 StgFAT::GetNextPage( sal_Int32 nPg )
71{
72 if (nPg >= 0)
73 {
74 if (nPg > (SAL_MAX_INT32((sal_Int32) 0x7FFFFFFF) >> 2))
75 return STG_EOF-2L;
76 rtl::Reference< StgPage > pPg = GetPhysPage( nPg << 2 );
77 nPg = pPg.is() ? StgCache::GetFromPage( pPg, m_nOffset >> 2 ) : STG_EOF-2L;
78 }
79 return nPg;
80}
81
82// Find the best fit block for the given size. Return
83// the starting block and its size or STG_EOF and 0.
84// nLastPage is a stopper which tells the current
85// underlying stream size. It is treated as a recommendation
86// to abort the search to inhibit excessive file growth.
87
88sal_Int32 StgFAT::FindBlock( sal_Int32& nPgs )
89{
90 sal_Int32 nMinStart = STG_EOF-2L, nMinLen = 0;
91 sal_Int32 nMaxStart = STG_EOF-2L, nMaxLen = 0x7FFFFFFFL;
92 sal_Int32 nTmpStart = STG_EOF-2L, nTmpLen = 0;
93 sal_Int32 nPages = m_rStrm.GetSize() >> 2;
94 bool bFound = false;
95 rtl::Reference< StgPage > pPg;
96 short nEntry = 0;
97 for( sal_Int32 i = 0; i < nPages; i++, nEntry++ )
98 {
99 if( !( nEntry % m_nEntries ) )
100 {
101 // load the next page for that stream
102 nEntry = 0;
103 pPg = GetPhysPage( i << 2 );
104 if( !pPg.is() )
105 return STG_EOF-2L;
106 }
107 sal_Int32 nCur = StgCache::GetFromPage( pPg, nEntry );
108 if( nCur == STG_FREE-1L )
109 {
110 // count the size of this area
111 if( nTmpLen )
112 nTmpLen++;
113 else
114 {
115 nTmpStart = i;
116 nTmpLen = 1;
117 }
118 if( nTmpLen == nPgs
119 // If we already did find a block, stop when reaching the limit
120 || ( bFound && ( nEntry >= m_nLimit ) ) )
121 break;
122 }
123 else if( nTmpLen )
124 {
125 if( nTmpLen > nPgs && nTmpLen < nMaxLen )
126 {
127 // block > requested size
128 nMaxLen = nTmpLen;
129 nMaxStart = nTmpStart;
130 bFound = true;
131 }
132 else if( nTmpLen >= nMinLen )
133 {
134 // block < requested size
135 nMinLen = nTmpLen;
136 nMinStart = nTmpStart;
137 bFound = true;
138 if( nTmpLen == nPgs )
139 break;
140 }
141 nTmpStart = STG_EOF-2L;
142 nTmpLen = 0;
143 }
144 }
145 // Determine which block to use.
146 if( nTmpLen )
147 {
148 if( nTmpLen > nPgs && nTmpLen < nMaxLen )
149 {
150 // block > requested size
151 nMaxLen = nTmpLen;
152 nMaxStart = nTmpStart;
153 }
154 else if( nTmpLen >= nMinLen )
155 {
156 // block < requested size
157 nMinLen = nTmpLen;
158 nMinStart = nTmpStart;
159 }
160 }
161 if( nMinStart != STG_EOF-2L && nMaxStart != STG_EOF-2L )
162 {
163 // two areas found; return the best fit area
164 sal_Int32 nMinDiff = nPgs - nMinLen;
165 sal_Int32 nMaxDiff = nMaxLen - nPgs;
166 if( nMinDiff > nMaxDiff )
167 nMinStart = STG_EOF-2L;
168 }
169 if( nMinStart != STG_EOF-2L )
170 {
171 nPgs = nMinLen; return nMinStart;
172 }
173 else
174 {
175 return nMaxStart;
176 }
177}
178
179// Set up the consecutive chain for a given block.
180
181bool StgFAT::MakeChain( sal_Int32 nStart, sal_Int32 nPgs )
182{
183 sal_Int32 nPos = nStart << 2;
184 rtl::Reference< StgPage > pPg = GetPhysPage( nPos );
185 if( !pPg.is() || !nPgs )
186 return false;
187 while( --nPgs )
188 {
189 if( m_nOffset >= m_nPageSize )
190 {
191 pPg = GetPhysPage( nPos );
192 if( !pPg.is() )
193 return false;
194 }
195 m_rStrm.GetIo().SetToPage( pPg, m_nOffset >> 2, ++nStart );
196 m_nOffset += 4;
197 nPos += 4;
198 }
199 if( m_nOffset >= m_nPageSize )
200 {
201 pPg = GetPhysPage( nPos );
202 if( !pPg.is() )
203 return false;
204 }
205 m_rStrm.GetIo().SetToPage( pPg, m_nOffset >> 2, STG_EOF-2L );
206 return true;
207}
208
209// Allocate a block of data from the given page number on.
210// It the page number is != STG_EOF, chain the block.
211
212sal_Int32 StgFAT::AllocPages( sal_Int32 nBgn, sal_Int32 nPgs )
213{
214 sal_Int32 nOrig = nBgn;
215 sal_Int32 nLast = nBgn;
216 sal_Int32 nBegin = STG_EOF-2L;
217 sal_Int32 nAlloc;
218 sal_Int32 nPages = m_rStrm.GetSize() >> 2;
219 short nPasses = 0;
220 // allow for two passes
221 while( nPasses < 2 )
222 {
223 // try to satisfy the request from the pool of free pages
224 while( nPgs )
225 {
226 nAlloc = nPgs;
227 nBegin = FindBlock( nAlloc );
228 // no more blocks left in present alloc chain
229 if( nBegin == STG_EOF-2L )
230 break;
231 if( ( nBegin + nAlloc ) > m_nMaxPage )
232 m_nMaxPage = nBegin + nAlloc;
233 if( !MakeChain( nBegin, nAlloc ) )
234 return STG_EOF-2L;
235 if( nOrig == STG_EOF-2L )
236 nOrig = nBegin;
237 else
238 {
239 // Patch the chain
240 rtl::Reference< StgPage > pPg = GetPhysPage( nLast << 2 );
241 if( !pPg.is() )
242 return STG_EOF-2L;
243 m_rStrm.GetIo().SetToPage( pPg, m_nOffset >> 2, nBegin );
244 }
245 nLast = nBegin + nAlloc - 1;
246 nPgs -= nAlloc;
247 }
248 if( nPgs && !nPasses )
249 {
250 // we need new, fresh space, so allocate and retry
251 if( !m_rStrm.SetSize( ( nPages + nPgs ) << 2 ) )
252 return STG_EOF-2L;
253 if( !m_bPhys && !InitNew( nPages ) )
254 return 0;
255 // FIXME: this was originally "FALSE", whether or not that
256 // makes sense (or should be STG_EOF instead, say?)
257 nPages = m_rStrm.GetSize() >> 2;
258 nPasses++;
259 }
260 else
261 break;
262 }
263 // now we should have a chain for the complete block
264 if( nBegin == STG_EOF-2L || nPgs )
265 {
266 m_rStrm.GetIo().SetError( SVSTREAM_FILEFORMAT_ERRORErrCode( ErrCodeArea::Io, ErrCodeClass::Format, 21 ) );
267 return STG_EOF-2L; // bad structure
268 }
269 return nOrig;
270}
271
272// Initialize newly allocated pages for a standard FAT stream
273// It can be assumed that the stream size is always on
274// a page boundary
275
276bool StgFAT::InitNew( sal_Int32 nPage1 )
277{
278 sal_Int32 n = ( ( m_rStrm.GetSize() >> 2 ) - nPage1 ) / m_nEntries;
279 if ( n > 0 )
280 {
281 while( n-- )
282 {
283 rtl::Reference< StgPage > pPg;
284 // Position within the underlying stream
285 // use the Pos2Page() method of the stream
286 m_rStrm.Pos2Page( nPage1 << 2 );
287 // Initialize the page
288 pPg = m_rStrm.GetIo().Copy( m_rStrm.GetPage() );
289 if ( !pPg.is() )
290 return false;
291 for( short i = 0; i < m_nEntries; i++ )
292 m_rStrm.GetIo().SetToPage( pPg, i, STG_FREE-1L );
293 nPage1++;
294 }
295 }
296 return true;
297}
298
299// Release a chain
300
301bool StgFAT::FreePages( sal_Int32 nStart, bool bAll )
302{
303 while( nStart >= 0 )
304 {
305 rtl::Reference< StgPage > pPg = GetPhysPage( nStart << 2 );
306 if( !pPg.is() )
307 return false;
308 nStart = StgCache::GetFromPage( pPg, m_nOffset >> 2 );
309 // The first released page is either set to EOF or FREE
310 m_rStrm.GetIo().SetToPage( pPg, m_nOffset >> 2, bAll ? STG_FREE-1L : STG_EOF-2L );
311 bAll = true;
312 }
313 return true;
314}
315
316///////////////////////////// class StgStrm
317
318// The base stream class provides basic functionality for seeking
319// and accessing the data on a physical basis. It uses the built-in
320// FAT class for the page allocations.
321
322StgStrm::StgStrm( StgIo& r )
323 : m_nPos(0),
324 m_bBytePosValid(true),
325 m_rIo(r),
326 m_pEntry(nullptr),
327 m_nStart(STG_EOF-2L),
328 m_nSize(0),
329 m_nPage(STG_EOF-2L),
330 m_nOffset(0),
331 m_nPageSize(m_rIo.GetPhysPageSize())
332{
333}
334
335StgStrm::~StgStrm()
336{
337}
338
339// Attach the stream to the given entry.
340
341void StgStrm::SetEntry( StgDirEntry& r )
342{
343 r.m_aEntry.SetLeaf( STG_DATA, m_nStart );
344 r.m_aEntry.SetSize( m_nSize );
345 m_pEntry = &r;
346 r.SetDirty();
347}
348
349/*
350 * The page chain, is basically a singly linked list of slots each
351 * point to the next page. Instead of traversing the file structure
352 * for this each time build a simple flat in-memory vector list
353 * of pages.
354 */
355sal_Int32 StgStrm::scanBuildPageChainCache()
356{
357 if (m_nSize > 0)
358 {
359 m_aPagesCache.reserve(m_nSize/m_nPageSize);
360 m_aUsedPageNumbers.reserve(m_nSize/m_nPageSize);
361 }
362
363 bool bError = false;
364 sal_Int32 nBgn = m_nStart;
365 sal_Int32 nOptSize = 0;
366
367 // Track already scanned PageNumbers here and use them to
368 // see if an already counted page is re-visited
369 while( nBgn >= 0 && !bError )
370 {
371 m_aPagesCache.push_back(nBgn);
372 nBgn = m_pFat->GetNextPage( nBgn );
373
374 //returned second is false if it already exists
375 if (!m_aUsedPageNumbers.insert(nBgn).second)
376 {
377 SAL_WARN ("sot", "Error: page number " << nBgn << " already in chain for stream")do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "sot")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break; case SAL_DETAIL_LOG_ACTION_LOG
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "Error: page number " << nBgn << " already in chain for stream"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sot"
), ("/home/maarten/src/libreoffice/core/sot/source/sdstor/stgstrms.cxx"
":" "377" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "Error: page number " << nBgn <<
" already in chain for stream"), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "Error: page number "
<< nBgn << " already in chain for stream"; ::sal
::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sot"), ("/home/maarten/src/libreoffice/core/sot/source/sdstor/stgstrms.cxx"
":" "377" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "Error: page number " << nBgn << " already in chain for stream"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sot"
), ("/home/maarten/src/libreoffice/core/sot/source/sdstor/stgstrms.cxx"
":" "377" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "Error: page number " << nBgn <<
" already in chain for stream"), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "Error: page number "
<< nBgn << " already in chain for stream"; ::sal
::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sot"), ("/home/maarten/src/libreoffice/core/sot/source/sdstor/stgstrms.cxx"
":" "377" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
378 bError = true;
379 }
380
381 nOptSize += m_nPageSize;
382 }
383 if (bError)
384 {
385 SAL_WARN("sot", "returning wrong format error")do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "sot")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break; case SAL_DETAIL_LOG_ACTION_LOG
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "returning wrong format error") == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sot"), ("/home/maarten/src/libreoffice/core/sot/source/sdstor/stgstrms.cxx"
":" "385" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "returning wrong format error"), 0); }
else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "returning wrong format error"; ::sal::detail::log(
(::SAL_DETAIL_LOG_LEVEL_WARN), ("sot"), ("/home/maarten/src/libreoffice/core/sot/source/sdstor/stgstrms.cxx"
":" "385" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "returning wrong format error") == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sot"), ("/home/maarten/src/libreoffice/core/sot/source/sdstor/stgstrms.cxx"
":" "385" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "returning wrong format error"), 0); }
else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "returning wrong format error"; ::sal::detail::log(
(::SAL_DETAIL_LOG_LEVEL_WARN), ("sot"), ("/home/maarten/src/libreoffice/core/sot/source/sdstor/stgstrms.cxx"
":" "385" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
386 m_rIo.SetError( ERRCODE_IO_WRONGFORMATErrCode( ErrCodeArea::Io, ErrCodeClass::Format, 21 ) );
387 m_aPagesCache.clear();
388 m_aUsedPageNumbers.clear();
389 }
390 return nOptSize;
391}
392
393// Compute page number and offset for the given byte position.
394// If the position is behind the size, set the stream right
395// behind the EOF.
396bool StgStrm::Pos2Page( sal_Int32 nBytePos )
397{
398 if ( !m_pFat )
399 return false;
400
401 // Values < 0 seek to the end
402 if( nBytePos < 0 || nBytePos >= m_nSize )
403 nBytePos = m_nSize;
404 // Adjust the position back to offset 0
405 m_nPos -= m_nOffset;
406 sal_Int32 nMask = ~( m_nPageSize - 1 );
407 sal_Int32 nOld = m_nPos & nMask;
408 sal_Int32 nNew = nBytePos & nMask;
409 m_nOffset = static_cast<short>( nBytePos & ~nMask );
410 m_nPos = nBytePos;
411 if (nOld == nNew)
412 return m_bBytePosValid;
413
414 // See fdo#47644 for a .doc with a vast amount of pages where seeking around the
415 // document takes a colossal amount of time
416
417 // Please Note: we build the pagescache incrementally as we go if necessary,
418 // so that a corrupted FAT doesn't poison the stream state for earlier reads
419 size_t nIdx = nNew / m_nPageSize;
420 if( nIdx >= m_aPagesCache.size() )
421 {
422 // Extend the FAT cache ! ...
423 size_t nToAdd = nIdx + 1;
424
425 if (m_aPagesCache.empty())
426 {
427 m_aPagesCache.push_back( m_nStart );
428 assert(m_aUsedPageNumbers.empty())(static_cast <bool> (m_aUsedPageNumbers.empty()) ? void
(0) : __assert_fail ("m_aUsedPageNumbers.empty()", "/home/maarten/src/libreoffice/core/sot/source/sdstor/stgstrms.cxx"
, 428, __extension__ __PRETTY_FUNCTION__))
;
429 m_aUsedPageNumbers.insert(m_nStart);
430 }
431
432 nToAdd -= m_aPagesCache.size();
433
434 sal_Int32 nBgn = m_aPagesCache.back();
435
436 // Start adding pages while we can
437 while (nToAdd > 0 && nBgn >= 0)
438 {
439 sal_Int32 nOldBgn = nBgn;
440 nBgn = m_pFat->GetNextPage(nOldBgn);
441 if( nBgn >= 0 )
442 {
443 //returned second is false if it already exists
444 if (!m_aUsedPageNumbers.insert(nBgn).second)
445 {
446 SAL_WARN ("sot", "Error: page number " << nBgn << " already in chain for stream")do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "sot")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break; case SAL_DETAIL_LOG_ACTION_LOG
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "Error: page number " << nBgn << " already in chain for stream"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sot"
), ("/home/maarten/src/libreoffice/core/sot/source/sdstor/stgstrms.cxx"
":" "446" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "Error: page number " << nBgn <<
" already in chain for stream"), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "Error: page number "
<< nBgn << " already in chain for stream"; ::sal
::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sot"), ("/home/maarten/src/libreoffice/core/sot/source/sdstor/stgstrms.cxx"
":" "446" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "Error: page number " << nBgn << " already in chain for stream"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sot"
), ("/home/maarten/src/libreoffice/core/sot/source/sdstor/stgstrms.cxx"
":" "446" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "Error: page number " << nBgn <<
" already in chain for stream"), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "Error: page number "
<< nBgn << " already in chain for stream"; ::sal
::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sot"), ("/home/maarten/src/libreoffice/core/sot/source/sdstor/stgstrms.cxx"
":" "446" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
447 break;
448 }
449
450 //very much the normal case
451 m_aPagesCache.push_back(nBgn);
452 --nToAdd;
453 }
454 }
455 }
456
457 if ( nIdx > m_aPagesCache.size() )
458 {
459 SAL_WARN("sot", "seek to index " << nIdx <<do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "sot")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break; case SAL_DETAIL_LOG_ACTION_LOG
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "seek to index " << nIdx << " beyond page cache size "
<< m_aPagesCache.size()) == 1) { ::sal_detail_log( (::
SAL_DETAIL_LOG_LEVEL_WARN), ("sot"), ("/home/maarten/src/libreoffice/core/sot/source/sdstor/stgstrms.cxx"
":" "460" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "seek to index " << nIdx <<
" beyond page cache size " << m_aPagesCache.size()), 0
); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "seek to index " << nIdx << " beyond page cache size "
<< m_aPagesCache.size(); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("sot"), ("/home/maarten/src/libreoffice/core/sot/source/sdstor/stgstrms.cxx"
":" "460" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "seek to index " << nIdx << " beyond page cache size "
<< m_aPagesCache.size()) == 1) { ::sal_detail_log( (::
SAL_DETAIL_LOG_LEVEL_WARN), ("sot"), ("/home/maarten/src/libreoffice/core/sot/source/sdstor/stgstrms.cxx"
":" "460" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "seek to index " << nIdx <<
" beyond page cache size " << m_aPagesCache.size()), 0
); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "seek to index " << nIdx << " beyond page cache size "
<< m_aPagesCache.size(); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("sot"), ("/home/maarten/src/libreoffice/core/sot/source/sdstor/stgstrms.cxx"
":" "460" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
460 " beyond page cache size " << m_aPagesCache.size())do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "sot")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break; case SAL_DETAIL_LOG_ACTION_LOG
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "seek to index " << nIdx << " beyond page cache size "
<< m_aPagesCache.size()) == 1) { ::sal_detail_log( (::
SAL_DETAIL_LOG_LEVEL_WARN), ("sot"), ("/home/maarten/src/libreoffice/core/sot/source/sdstor/stgstrms.cxx"
":" "460" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "seek to index " << nIdx <<
" beyond page cache size " << m_aPagesCache.size()), 0
); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "seek to index " << nIdx << " beyond page cache size "
<< m_aPagesCache.size(); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("sot"), ("/home/maarten/src/libreoffice/core/sot/source/sdstor/stgstrms.cxx"
":" "460" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "seek to index " << nIdx << " beyond page cache size "
<< m_aPagesCache.size()) == 1) { ::sal_detail_log( (::
SAL_DETAIL_LOG_LEVEL_WARN), ("sot"), ("/home/maarten/src/libreoffice/core/sot/source/sdstor/stgstrms.cxx"
":" "460" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "seek to index " << nIdx <<
" beyond page cache size " << m_aPagesCache.size()), 0
); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "seek to index " << nIdx << " beyond page cache size "
<< m_aPagesCache.size(); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("sot"), ("/home/maarten/src/libreoffice/core/sot/source/sdstor/stgstrms.cxx"
":" "460" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
461 // fdo#84229 - handle seek to end and back as eg. XclImpStream expects
462 m_nPage = STG_EOF-2L;
463 m_nOffset = 0;
464 // Intriguingly in the past we didn't reset nPos to match the real
465 // length of the stream thus:
466 // nIdx = m_aPagesCache.size();
467 // nPos = nPageSize * nIdx;
468 // so retain this behavior for now.
469 m_bBytePosValid = false;
470 return false;
471 }
472
473 // special case: seek to 1st byte of new, unallocated page
474 // (in case the file size is a multiple of the page size)
475 if( nBytePos == m_nSize && !m_nOffset && nIdx > 0 && nIdx == m_aPagesCache.size() )
476 {
477 nIdx--;
478 m_nOffset = m_nPageSize;
479 }
480 else if ( nIdx == m_aPagesCache.size() )
481 {
482 m_nPage = STG_EOF-2L;
483 m_bBytePosValid = false;
484 return false;
485 }
486
487 m_nPage = m_aPagesCache[ nIdx ];
488
489 m_bBytePosValid = m_nPage >= 0;
490 return m_bBytePosValid;
491}
492
493// Copy an entire stream. Both streams are allocated in the FAT.
494// The target stream is this stream.
495
496bool StgStrm::Copy( sal_Int32 nFrom, sal_Int32 nBytes )
497{
498 if ( !m_pFat )
499 return false;
500
501 m_aPagesCache.clear();
502 m_aUsedPageNumbers.clear();
503
504 sal_Int32 nTo = m_nStart;
505 sal_Int32 nPgs = ( nBytes + m_nPageSize - 1 ) / m_nPageSize;
506 while( nPgs-- )
507 {
508 if( nTo < 0 )
509 {
510 m_rIo.SetError( SVSTREAM_FILEFORMAT_ERRORErrCode( ErrCodeArea::Io, ErrCodeClass::Format, 21 ) );
511 return false;
512 }
513 m_rIo.Copy( nTo, nFrom );
514 if( nFrom >= 0 )
515 {
516 nFrom = m_pFat->GetNextPage( nFrom );
517 if( nFrom < 0 )
518 {
519 m_rIo.SetError( SVSTREAM_FILEFORMAT_ERRORErrCode( ErrCodeArea::Io, ErrCodeClass::Format, 21 ) );
520 return false;
521 }
522 }
523 nTo = m_pFat->GetNextPage( nTo );
524 }
525 return true;
526}
527
528bool StgStrm::SetSize( sal_Int32 nBytes )
529{
530 if ( nBytes < 0 || !m_pFat )
531 return false;
532
533 m_aPagesCache.clear();
534 m_aUsedPageNumbers.clear();
535
536 // round up to page size
537 sal_Int32 nOld = ( ( m_nSize + m_nPageSize - 1 ) / m_nPageSize ) * m_nPageSize;
538 sal_Int32 nNew = ( ( nBytes + m_nPageSize - 1 ) / m_nPageSize ) * m_nPageSize;
539 if( nNew > nOld )
540 {
541 if( !Pos2Page( m_nSize ) )
542 return false;
543 sal_Int32 nBgn = m_pFat->AllocPages( m_nPage, ( nNew - nOld ) / m_nPageSize );
544 if( nBgn == STG_EOF-2L )
545 return false;
546 if( m_nStart == STG_EOF-2L )
547 m_nStart = m_nPage = nBgn;
548 }
549 else if( nNew < nOld )
550 {
551 bool bAll = ( nBytes == 0 );
552 if( !Pos2Page( nBytes ) || !m_pFat->FreePages( m_nPage, bAll ) )
553 return false;
554 if( bAll )
555 m_nStart = m_nPage = STG_EOF-2L;
556 }
557 if( m_pEntry )
558 {
559 // change the dir entry?
560 if( !m_nSize || !nBytes )
561 m_pEntry->m_aEntry.SetLeaf( STG_DATA, m_nStart );
562 m_pEntry->m_aEntry.SetSize( nBytes );
563 m_pEntry->SetDirty();
564 }
565 m_nSize = nBytes;
566 m_pFat->SetLimit( GetPages() );
567 return true;
568}
569
570// Return the # of allocated pages
571
572
573//////////////////////////// class StgFATStrm
574
575// The FAT stream class provides physical access to the master FAT.
576// Since this access is implemented as a StgStrm, we can use the
577// FAT allocator.
578
579StgFATStrm::StgFATStrm(StgIo& r, sal_Int32 nFatStrmSize) : StgStrm( r )
580{
581 m_pFat.reset( new StgFAT( *this, true ) );
582 m_nSize = nFatStrmSize;
583}
584
585bool StgFATStrm::Pos2Page( sal_Int32 nBytePos )
586{
587 // Values < 0 seek to the end
588 if( nBytePos < 0 || nBytePos >= m_nSize )
589 nBytePos = m_nSize ? m_nSize - 1 : 0;
590 m_nPage = nBytePos / m_nPageSize;
591 m_nOffset = static_cast<short>( nBytePos % m_nPageSize );
592 m_nPage = GetPage(m_nPage, false);
593 bool bValid = m_nPage >= 0;
594 SetPos(nBytePos, bValid);
595 return bValid;
596}
597
598// Get the page number entry for the given page offset.
599
600sal_Int32 StgFATStrm::GetPage(sal_Int32 nOff, bool bMake, sal_uInt16 *pnMasterAlloc)
601{
602 OSL_ENSURE( nOff >= 0, "The offset may not be negative!" )do { if (true && (!(nOff >= 0))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sot/source/sdstor/stgstrms.cxx"
":" "602" ": "), "%s", "The offset may not be negative!"); }
} while (false)
;
603 if( pnMasterAlloc ) *pnMasterAlloc = 0;
604 if( nOff < StgHeader::GetFAT1Size() )
605 return m_rIo.m_aHdr.GetFATPage( nOff );
606 sal_Int32 nMaxPage = m_nSize >> 2;
607 nOff = nOff - StgHeader::GetFAT1Size();
608 // number of master pages that we need to iterate through
609 sal_uInt16 nMasterCount = ( m_nPageSize >> 2 ) - 1;
610 sal_uInt16 nBlocks = nOff / nMasterCount;
611 // offset in the last master page
612 nOff = nOff % nMasterCount;
613
614 rtl::Reference< StgPage > pOldPage;
615 rtl::Reference< StgPage > pMaster;
616 sal_Int32 nFAT = m_rIo.m_aHdr.GetFATChain();
617 for( sal_uInt16 nCount = 0; nCount <= nBlocks; nCount++ )
618 {
619 if( nFAT == STG_EOF-2L || nFAT == STG_FREE-1L )
620 {
621 if( bMake )
622 {
623 m_aPagesCache.clear();
624 m_aUsedPageNumbers.clear();
625
626 // create a new master page
627 nFAT = nMaxPage++;
628 pMaster = m_rIo.Copy( nFAT );
629 if ( pMaster.is() )
630 {
631 for( short k = 0; k < static_cast<short>( m_nPageSize >> 2 ); k++ )
632 m_rIo.SetToPage( pMaster, k, STG_FREE-1L );
633 // chaining
634 if( !pOldPage.is() )
635 m_rIo.m_aHdr.SetFATChain( nFAT );
636 else
637 m_rIo.SetToPage( pOldPage, nMasterCount, nFAT );
638 if( nMaxPage >= m_rIo.GetPhysPages() )
639 if( !m_rIo.SetSize( nMaxPage ) )
640 return STG_EOF-2L;
641 // mark the page as used
642 // make space for Masterpage
643 if( !pnMasterAlloc ) // create space oneself
644 {
645 if( !Pos2Page( nFAT << 2 ) )
646 return STG_EOF-2L;
647 rtl::Reference< StgPage > pPg = m_rIo.Get( m_nPage, true );
648 if( !pPg.is() )
649 return STG_EOF-2L;
650 m_rIo.SetToPage( pPg, m_nOffset >> 2, STG_MASTER-4L );
651 }
652 else
653 (*pnMasterAlloc)++;
654 m_rIo.m_aHdr.SetMasters( nCount + 1 );
655 pOldPage = pMaster;
656 }
657 }
658 }
659 else
660 {
661 pMaster = m_rIo.Get( nFAT, true );
662 if ( pMaster.is() )
663 {
664 nFAT = StgCache::GetFromPage( pMaster, nMasterCount );
665 pOldPage = pMaster;
666 }
667 }
668 }
669 if( pMaster.is() )
670 return StgCache::GetFromPage( pMaster, nOff );
671 m_rIo.SetError( SVSTREAM_GENERALERRORErrCode( ErrCodeArea::Io, ErrCodeClass::General, 13 ) );
672 return STG_EOF-2L;
673}
674
675
676// Set the page number entry for the given page offset.
677
678bool StgFATStrm::SetPage( short nOff, sal_Int32 nNewPage )
679{
680 OSL_ENSURE( nOff >= 0, "The offset may not be negative!" )do { if (true && (!(nOff >= 0))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sot/source/sdstor/stgstrms.cxx"
":" "680" ": "), "%s", "The offset may not be negative!"); }
} while (false)
;
8
Assuming 'nOff' is >= 0
9
Taking false branch
10
Loop condition is false. Exiting loop
681 m_aPagesCache.clear();
682 m_aUsedPageNumbers.clear();
683
684 bool bRes = true;
685 if( nOff < StgHeader::GetFAT1Size() )
11
Assuming the condition is true
12
Taking true branch
686 m_rIo.m_aHdr.SetFATPage( nOff, nNewPage );
687 else
688 {
689 nOff = nOff - StgHeader::GetFAT1Size();
690 // number of master pages that we need to iterate through
691 sal_uInt16 nMasterCount = ( m_nPageSize >> 2 ) - 1;
692 sal_uInt16 nBlocks = nOff / nMasterCount;
693 // offset in the last master page
694 nOff = nOff % nMasterCount;
695
696 rtl::Reference< StgPage > pMaster;
697 sal_Int32 nFAT = m_rIo.m_aHdr.GetFATChain();
698 for( sal_uInt16 nCount = 0; nCount <= nBlocks; nCount++ )
699 {
700 if( nFAT == STG_EOF-2L || nFAT == STG_FREE-1L )
701 {
702 pMaster = nullptr;
703 break;
704 }
705 pMaster = m_rIo.Get( nFAT, true );
706 if ( pMaster.is() )
707 nFAT = StgCache::GetFromPage( pMaster, nMasterCount );
708 }
709 if( pMaster.is() )
710 m_rIo.SetToPage( pMaster, nOff, nNewPage );
711 else
712 {
713 m_rIo.SetError( SVSTREAM_GENERALERRORErrCode( ErrCodeArea::Io, ErrCodeClass::General, 13 ) );
714 bRes = false;
715 }
716 }
717
718 // lock the page against access
719 if( bRes
12.1
'bRes' is true
)
13
Taking true branch
720 {
721 Pos2Page( nNewPage << 2 );
14
The result of the left shift is undefined because the left operand is negative
722 rtl::Reference< StgPage > pPg = m_rIo.Get( m_nPage, true );
723 if( pPg.is() )
724 m_rIo.SetToPage( pPg, m_nOffset >> 2, STG_FAT-3L );
725 else
726 bRes = false;
727 }
728 return bRes;
729}
730
731bool StgFATStrm::SetSize( sal_Int32 nBytes )
732{
733 if ( nBytes < 0 )
1
Assuming 'nBytes' is >= 0
2
Taking false branch
734 return false;
735
736 m_aPagesCache.clear();
737 m_aUsedPageNumbers.clear();
738
739 // Set the number of entries to a multiple of the page size
740 short nOld = static_cast<short>( ( m_nSize + ( m_nPageSize - 1 ) ) / m_nPageSize );
741 short nNew = static_cast<short>(
742 ( nBytes + ( m_nPageSize - 1 ) ) / m_nPageSize ) ;
743 if( nNew < nOld )
3
Assuming 'nNew' is < 'nOld'
4
Taking true branch
744 {
745 // release master pages
746 for( short i = nNew; i
4.1
'i' is < 'nOld'
< nOld; i++ )
5
Loop condition is true. Entering loop body
747 SetPage( i, STG_FREE-1L );
6
Passing the value -1 via 2nd parameter 'nNewPage'
7
Calling 'StgFATStrm::SetPage'
748 }
749 else
750 {
751 while( nOld < nNew )
752 {
753 // allocate master pages
754 // find a free master page slot
755 sal_Int32 nPg = 0;
756 sal_uInt16 nMasterAlloc = 0;
757 nPg = GetPage( nOld, true, &nMasterAlloc );
758 if( nPg == STG_EOF-2L )
759 return false;
760 // 4 Bytes have been used for Allocation of each MegaMasterPage
761 nBytes += nMasterAlloc << 2;
762
763 // find a free page using the FAT allocator
764 sal_Int32 n = 1;
765 OSL_ENSURE( m_pFat, "The pointer is always initializer here!" )do { if (true && (!(m_pFat))) { sal_detail_logFormat(
(SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sot/source/sdstor/stgstrms.cxx"
":" "765" ": "), "%s", "The pointer is always initializer here!"
); } } while (false)
;
766 sal_Int32 nNewPage = m_pFat->FindBlock( n );
767 if( nNewPage == STG_EOF-2L )
768 {
769 // no free pages found; create a new page
770 // Since all pages are allocated, extend
771 // the file size for the next page!
772 nNewPage = m_nSize >> 2;
773 // if a MegaMasterPage was created avoid taking
774 // the same Page
775 nNewPage += nMasterAlloc;
776 // adjust the file size if necessary
777 if( nNewPage >= m_rIo.GetPhysPages() )
778 if( !m_rIo.SetSize( nNewPage + 1 ) )
779 return false;
780 }
781 // Set up the page with empty entries
782 rtl::Reference< StgPage > pPg = m_rIo.Copy( nNewPage );
783 if ( !pPg.is() )
784 return false;
785 for( short j = 0; j < static_cast<short>( m_nPageSize >> 2 ); j++ )
786 m_rIo.SetToPage( pPg, j, STG_FREE-1L );
787
788 // store the page number into the master FAT
789 // Set the size before so the correct FAT can be found
790 m_nSize = ( nOld + 1 ) * m_nPageSize;
791 SetPage( nOld, nNewPage );
792
793 // MegaMasterPages were created, mark it them as used
794
795 sal_uInt32 nMax = m_rIo.m_aHdr.GetMasters( );
796 sal_uInt32 nFAT = m_rIo.m_aHdr.GetFATChain();
797 if( nMasterAlloc )
798 for( sal_uInt32 nCount = 0; nCount < nMax; nCount++ )
799 {
800 if( !Pos2Page( nFAT << 2 ) )
801 return false;
802 if( nMax - nCount <= nMasterAlloc )
803 {
804 rtl::Reference< StgPage > piPg = m_rIo.Get( m_nPage, true );
805 if( !piPg.is() )
806 return false;
807 m_rIo.SetToPage( piPg, m_nOffset >> 2, STG_MASTER-4L );
808 }
809 rtl::Reference< StgPage > pPage = m_rIo.Get( nFAT, true );
810 if( !pPage.is() ) return false;
811 nFAT = StgCache::GetFromPage( pPage, (m_nPageSize >> 2 ) - 1 );
812 }
813
814 nOld++;
815 // We have used up 4 bytes for the STG_FAT entry
816 nBytes += 4;
817 nNew = static_cast<short>(
818 ( nBytes + ( m_nPageSize - 1 ) ) / m_nPageSize );
819 }
820 }
821 m_nSize = nNew * m_nPageSize;
822 m_rIo.m_aHdr.SetFATSize( nNew );
823 return true;
824}
825
826/////////////////////////// class StgDataStrm
827
828// This class is a normal physical stream which can be initialized
829// either with an existing dir entry or an existing FAT chain.
830// The stream has a size increment which normally is 1, but which can be
831// set to any value is you want the size to be incremented by certain values.
832
833StgDataStrm::StgDataStrm( StgIo& r, sal_Int32 nBgn, sal_Int32 nLen ) : StgStrm( r )
834{
835 Init( nBgn, nLen );
836}
837
838StgDataStrm::StgDataStrm( StgIo& r, StgDirEntry& p ) : StgStrm( r )
839{
840 m_pEntry = &p;
841 Init( p.m_aEntry.GetLeaf( STG_DATA ),
842 p.m_aEntry.GetSize() );
843}
844
845void StgDataStrm::Init( sal_Int32 nBgn, sal_Int32 nLen )
846{
847 if ( m_rIo.m_pFAT )
848 m_pFat.reset( new StgFAT( *m_rIo.m_pFAT, true ) );
849
850 OSL_ENSURE( m_pFat, "The pointer should not be empty!" )do { if (true && (!(m_pFat))) { sal_detail_logFormat(
(SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sot/source/sdstor/stgstrms.cxx"
":" "850" ": "), "%s", "The pointer should not be empty!"); }
} while (false)
;
851
852 m_nStart = m_nPage = nBgn;
853 m_nSize = nLen;
854 m_nIncr = 1;
855 m_nOffset = 0;
856 if( nLen < 0 && m_pFat )
857 {
858 // determine the actual size of the stream by scanning
859 // the FAT chain and counting the # of pages allocated
860 m_nSize = scanBuildPageChainCache();
861 }
862}
863
864// Set the size of a physical stream.
865
866bool StgDataStrm::SetSize( sal_Int32 nBytes )
867{
868 if ( !m_pFat )
869 return false;
870
871 nBytes = ( ( nBytes + m_nIncr - 1 ) / m_nIncr ) * m_nIncr;
872 sal_Int32 nOldSz = m_nSize;
873 if( nOldSz != nBytes )
874 {
875 if( !StgStrm::SetSize( nBytes ) )
876 return false;
877 sal_Int32 nMaxPage = m_pFat->GetMaxPage();
878 if( nMaxPage > m_rIo.GetPhysPages() )
879 if( !m_rIo.SetSize( nMaxPage ) )
880 return false;
881 // If we only allocated one page or less, create this
882 // page in the cache for faster throughput. The current
883 // position is the former EOF point.
884 if( ( m_nSize - 1 ) / m_nPageSize - ( nOldSz - 1 ) / m_nPageSize == 1 )
885 {
886 Pos2Page( nBytes );
887 if( m_nPage >= 0 )
888 m_rIo.Copy( m_nPage );
889 }
890 }
891 return true;
892}
893
894// Get the address of the data byte at a specified offset.
895// If bForce = true, a read of non-existent data causes
896// a read fault.
897
898void* StgDataStrm::GetPtr( sal_Int32 Pos, bool bDirty )
899{
900 if( Pos2Page( Pos ) )
901 {
902 rtl::Reference< StgPage > pPg = m_rIo.Get( m_nPage, true/*bForce*/ );
903 if (pPg.is() && m_nOffset < pPg->GetSize())
904 {
905 if( bDirty )
906 m_rIo.SetDirty( pPg );
907 return static_cast<sal_uInt8 *>(pPg->GetData()) + m_nOffset;
908 }
909 }
910 return nullptr;
911}
912
913// This could easily be adapted to a better algorithm by determining
914// the amount of consecutable blocks before doing a read. The result
915// is the number of bytes read. No error is generated on EOF.
916
917sal_Int32 StgDataStrm::Read( void* pBuf, sal_Int32 n )
918{
919 if ( n < 0 )
920 return 0;
921
922 const auto nAvailable = m_nSize - GetPos();
923 if (n > nAvailable)
924 n = nAvailable;
925 sal_Int32 nDone = 0;
926 while( n )
927 {
928 short nBytes = m_nPageSize - m_nOffset;
929 rtl::Reference< StgPage > pPg;
930 if( static_cast<sal_Int32>(nBytes) > n )
931 nBytes = static_cast<short>(n);
932 if( nBytes )
933 {
934 short nRes;
935 void *p = static_cast<sal_uInt8 *>(pBuf) + nDone;
936 if( nBytes == m_nPageSize )
937 {
938 pPg = m_rIo.Find( m_nPage );
939 if( pPg.is() )
940 {
941 // data is present, so use the cached data
942 memcpy( p, pPg->GetData(), nBytes );
943 nRes = nBytes;
944 }
945 else
946 // do a direct (unbuffered) read
947 nRes = static_cast<short>(m_rIo.Read( m_nPage, p )) * m_nPageSize;
948 }
949 else
950 {
951 // partial block read through the cache.
952 pPg = m_rIo.Get( m_nPage, false );
953 if( !pPg.is() )
954 break;
955 memcpy( p, static_cast<sal_uInt8*>(pPg->GetData()) + m_nOffset, nBytes );
956 nRes = nBytes;
957 }
958 nDone += nRes;
959 SetPos(GetPos() + nRes, true);
960 n -= nRes;
961 m_nOffset = m_nOffset + nRes;
962 if( nRes != nBytes )
963 break; // read error or EOF
964 }
965 // Switch to next page if necessary
966 if (m_nOffset >= m_nPageSize && !Pos2Page(GetPos()))
967 break;
968 }
969 return nDone;
970}
971
972sal_Int32 StgDataStrm::Write( const void* pBuf, sal_Int32 n )
973{
974 if ( n < 0 )
975 return 0;
976
977 sal_Int32 nDone = 0;
978 if( ( GetPos() + n ) > m_nSize )
979 {
980 sal_Int32 nOld = GetPos();
981 if( !SetSize( nOld + n ) )
982 return 0;
983 Pos2Page( nOld );
984 }
985 while( n )
986 {
987 short nBytes = m_nPageSize - m_nOffset;
988 rtl::Reference< StgPage > pPg;
989 if( static_cast<sal_Int32>(nBytes) > n )
990 nBytes = static_cast<short>(n);
991 if( nBytes )
992 {
993 short nRes;
994 const void *p = static_cast<const sal_uInt8 *>(pBuf) + nDone;
995 if( nBytes == m_nPageSize )
996 {
997 pPg = m_rIo.Find( m_nPage );
998 if( pPg.is() )
999 {
1000 // data is present, so use the cached data
1001 memcpy( pPg->GetData(), p, nBytes );
1002 m_rIo.SetDirty( pPg );
1003 nRes = nBytes;
1004 }
1005 else
1006 // do a direct (unbuffered) write
1007 nRes = static_cast<short>(m_rIo.Write( m_nPage, p )) * m_nPageSize;
1008 }
1009 else
1010 {
1011 // partial block read through the cache.
1012 pPg = m_rIo.Get( m_nPage, false );
1013 if( !pPg.is() )
1014 break;
1015 memcpy( static_cast<sal_uInt8*>(pPg->GetData()) + m_nOffset, p, nBytes );
1016 m_rIo.SetDirty( pPg );
1017 nRes = nBytes;
1018 }
1019 nDone += nRes;
1020 SetPos(GetPos() + nRes, true);
1021 n -= nRes;
1022 m_nOffset = m_nOffset + nRes;
1023 if( nRes != nBytes )
1024 break; // read error
1025 }
1026 // Switch to next page if necessary
1027 if( m_nOffset >= m_nPageSize && !Pos2Page(GetPos()) )
1028 break;
1029 }
1030 return nDone;
1031}
1032
1033//////////////////////////// class StgSmallStream
1034
1035// The small stream class provides access to streams with a size < 4096 bytes.
1036// This stream is a StgStream containing small pages. The FAT for this stream
1037// is also a StgStream. The start of the FAT is in the header at DataRootPage,
1038// the stream itself is pointed to by the root entry (it holds start & size).
1039
1040StgSmallStrm::StgSmallStrm( StgIo& r, sal_Int32 nBgn ) : StgStrm( r )
1041{
1042 Init( nBgn, 0 );
1043}
1044
1045StgSmallStrm::StgSmallStrm( StgIo& r, StgDirEntry& p ) : StgStrm( r )
1046{
1047 m_pEntry = &p;
1048 Init( p.m_aEntry.GetLeaf( STG_DATA ),
1049 p.m_aEntry.GetSize() );
1050}
1051
1052void StgSmallStrm::Init( sal_Int32 nBgn, sal_Int32 nLen )
1053{
1054 if ( m_rIo.m_pDataFAT )
1055 m_pFat.reset( new StgFAT( *m_rIo.m_pDataFAT, false ) );
1056 m_pData = m_rIo.m_pDataStrm;
1057 OSL_ENSURE( m_pFat && m_pData, "The pointers should not be empty!" )do { if (true && (!(m_pFat && m_pData))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sot/source/sdstor/stgstrms.cxx"
":" "1057" ": "), "%s", "The pointers should not be empty!")
; } } while (false)
;
1058
1059 m_nPageSize = m_rIo.GetDataPageSize();
1060 m_nStart =
1061 m_nPage = nBgn;
1062 m_nSize = nLen;
1063}
1064
1065// This could easily be adapted to a better algorithm by determining
1066// the amount of consecutable blocks before doing a read. The result
1067// is the number of bytes read. No error is generated on EOF.
1068
1069sal_Int32 StgSmallStrm::Read( void* pBuf, sal_Int32 n )
1070{
1071 // We can safely assume that reads are not huge, since the
1072 // small stream is likely to be < 64 KBytes.
1073 sal_Int32 nBytePos = GetPos();
1074 if( ( nBytePos + n ) > m_nSize )
1075 n = m_nSize - nBytePos;
1076 sal_Int32 nDone = 0;
1077 while( n )
1078 {
1079 short nBytes = m_nPageSize - m_nOffset;
1080 if( static_cast<sal_Int32>(nBytes) > n )
1081 nBytes = static_cast<short>(n);
1082 if( nBytes )
1083 {
1084 if (!m_pData)
1085 break;
1086 sal_Int32 nPos;
1087 if (o3tl::checked_multiply<sal_Int32>(m_nPage, m_nPageSize, nPos))
1088 break;
1089 if (!m_pData->Pos2Page(nPos + m_nOffset))
1090 break;
1091 // all reading through the stream
1092 short nRes = static_cast<short>(m_pData->Read( static_cast<sal_uInt8*>(pBuf) + nDone, nBytes ));
1093 nDone += nRes;
1094 SetPos(GetPos() + nRes, true);
1095 n -= nRes;
1096 m_nOffset = m_nOffset + nRes;
1097 // read problem?
1098 if( nRes != nBytes )
1099 break;
1100 }
1101 // Switch to next page if necessary
1102 if (m_nOffset >= m_nPageSize && !Pos2Page(GetPos()))
1103 break;
1104 }
1105 return nDone;
1106}
1107
1108sal_Int32 StgSmallStrm::Write( const void* pBuf, sal_Int32 n )
1109{
1110 // you can safely assume that reads are not huge, since the
1111 // small stream is likely to be < 64 KBytes.
1112 sal_Int32 nDone = 0;
1113 sal_Int32 nOldPos = GetPos();
1114 if( ( nOldPos + n ) > m_nSize )
1115 {
1116 if (!SetSize(nOldPos + n))
1117 return 0;
1118 Pos2Page(nOldPos);
1119 }
1120 while( n )
1121 {
1122 short nBytes = m_nPageSize - m_nOffset;
1123 if( static_cast<sal_Int32>(nBytes) > n )
1124 nBytes = static_cast<short>(n);
1125 if( nBytes )
1126 {
1127 // all writing goes through the stream
1128 sal_Int32 nDataPos = m_nPage * m_nPageSize + m_nOffset;
1129 if ( !m_pData
1130 || ( m_pData->GetSize() < ( nDataPos + nBytes )
1131 && !m_pData->SetSize( nDataPos + nBytes ) ) )
1132 break;
1133 if( !m_pData->Pos2Page( nDataPos ) )
1134 break;
1135 short nRes = static_cast<short>(m_pData->Write( static_cast<sal_uInt8 const *>(pBuf) + nDone, nBytes ));
1136 nDone += nRes;
1137 SetPos(GetPos() + nRes, true);
1138 n -= nRes;
1139 m_nOffset = m_nOffset + nRes;
1140 // write problem?
1141 if( nRes != nBytes )
1142 break;
1143 }
1144 // Switch to next page if necessary
1145 if( m_nOffset >= m_nPageSize && !Pos2Page(GetPos()) )
1146 break;
1147 }
1148 return nDone;
1149}
1150
1151/////////////////////////// class StgTmpStrm
1152
1153// The temporary stream uses a memory stream if < 32K, otherwise a
1154// temporary file.
1155
1156#define THRESHOLD32768L 32768L
1157
1158StgTmpStrm::StgTmpStrm( sal_uInt64 nInitSize )
1159 : SvMemoryStream( nInitSize > THRESHOLD32768L
1160 ? 16
1161 : ( nInitSize ? nInitSize : 16 ), 4096 )
1162{
1163 m_pStrm = nullptr;
1164 // this calls FlushData, so all members should be set by this time
1165 SetBufferSize( 0 );
1166 if( nInitSize > THRESHOLD32768L )
1167 SetSize( nInitSize );
1168}
1169
1170bool StgTmpStrm::Copy( StgTmpStrm& rSrc )
1171{
1172 sal_uInt64 n = rSrc.GetSize();
1173 const sal_uInt64 nCur = rSrc.Tell();
1174 SetSize( n );
1175 if( GetError() == ERRCODE_NONEErrCode(0) )
1176 {
1177 std::unique_ptr<sal_uInt8[]> p(new sal_uInt8[ 4096 ]);
1178 rSrc.Seek( 0 );
1179 Seek( 0 );
1180 while( n )
1181 {
1182 const sal_uInt64 nn = std::min<sal_uInt64>(n, 4096);
1183 if (rSrc.ReadBytes( p.get(), nn ) != nn)
1184 break;
1185 if (WriteBytes( p.get(), nn ) != nn)
1186 break;
1187 n -= nn;
1188 }
1189 p.reset();
1190 rSrc.Seek( nCur );
1191 Seek( nCur );
1192 return n == 0;
1193 }
1194 else
1195 return false;
1196}
1197
1198StgTmpStrm::~StgTmpStrm()
1199{
1200 if( m_pStrm )
1201 {
1202 m_pStrm->Close();
1203 osl::File::remove( m_aName );
1204 delete m_pStrm;
1205 }
1206}
1207
1208sal_uInt64 StgTmpStrm::GetSize() const
1209{
1210 sal_uInt64 n;
1211 if( m_pStrm )
1212 {
1213 n = m_pStrm->TellEnd();
1214 }
1215 else
1216 n = nEndOfData;
1217 return n;
1218}
1219
1220void StgTmpStrm::SetSize(sal_uInt64 n)
1221{
1222 if( m_pStrm )
1223 m_pStrm->SetStreamSize( n );
1224 else
1225 {
1226 if( n > THRESHOLD32768L )
1227 {
1228 m_aName = utl::TempFile(nullptr, false).GetURL();
1229 std::unique_ptr<SvFileStream> s(new SvFileStream( m_aName, StreamMode::READWRITE ));
1230 const sal_uInt64 nCur = Tell();
1231 sal_uInt64 i = nEndOfData;
1232 std::unique_ptr<sal_uInt8[]> p(new sal_uInt8[ 4096 ]);
1233 if( i )
1234 {
1235 Seek( 0 );
1236 while( i )
1237 {
1238 const sal_uInt64 nb = std::min<sal_uInt64>(i, 4096);
1239 if (ReadBytes(p.get(), nb) == nb
1240 && s->WriteBytes(p.get(), nb) == nb)
1241 i -= nb;
1242 else
1243 break;
1244 }
1245 }
1246 if( !i && n > nEndOfData )
1247 {
1248 // We have to write one byte at the end of the file
1249 // if the file is bigger than the memstream to see
1250 // if it fits on disk
1251 s->Seek(nEndOfData);
1252 memset(p.get(), 0x00, 4096);
1253 i = n - nEndOfData;
1254 while (i)
1255 {
1256 const sal_uInt64 nb = std::min<sal_uInt64>(i, 4096);
1257 if (s->WriteBytes(p.get(), nb) == nb)
1258 i -= nb;
1259 else
1260 break; // error
1261 }
1262 s->Flush();
1263 if( s->GetError() != ERRCODE_NONEErrCode(0) )
1264 i = 1;
1265 }
1266 Seek( nCur );
1267 s->Seek( nCur );
1268 if( i )
1269 {
1270 SetError( s->GetError() );
1271 return;
1272 }
1273 m_pStrm = s.release();
1274 // Shrink the memory to 16 bytes, which seems to be the minimum
1275 ReAllocateMemory( - ( static_cast<long>(nEndOfData) - 16 ) );
1276 }
1277 else
1278 {
1279 if( n > nEndOfData )
1280 {
1281 SvMemoryStream::SetSize(n);
1282 }
1283 else
1284 nEndOfData = n;
1285 }
1286 }
1287}
1288
1289std::size_t StgTmpStrm::GetData( void* pData, std::size_t n )
1290{
1291 if( m_pStrm )
1292 {
1293 n = m_pStrm->ReadBytes( pData, n );
1294 SetError( m_pStrm->GetError() );
1295 return n;
1296 }
1297 else
1298 return SvMemoryStream::GetData( pData, n );
1299}
1300
1301std::size_t StgTmpStrm::PutData( const void* pData, std::size_t n )
1302{
1303 sal_uInt32 nCur = Tell();
1304 sal_uInt32 nNew = nCur + n;
1305 if( nNew > THRESHOLD32768L && !m_pStrm )
1306 {
1307 SetSize( nNew );
1308 if( GetError() != ERRCODE_NONEErrCode(0) )
1309 return 0;
1310 }
1311 if( m_pStrm )
1312 {
1313 nNew = m_pStrm->WriteBytes( pData, n );
1314 SetError( m_pStrm->GetError() );
1315 }
1316 else
1317 nNew = SvMemoryStream::PutData( pData, n );
1318 return nNew;
1319}
1320
1321sal_uInt64 StgTmpStrm::SeekPos(sal_uInt64 n)
1322{
1323 // check if a truncated STREAM_SEEK_TO_END was passed
1324 assert(n != SAL_MAX_UINT32)(static_cast <bool> (n != ((sal_uInt32) 0xFFFFFFFF)) ? void
(0) : __assert_fail ("n != SAL_MAX_UINT32", "/home/maarten/src/libreoffice/core/sot/source/sdstor/stgstrms.cxx"
, 1324, __extension__ __PRETTY_FUNCTION__))
;
1325 if( n == STREAM_SEEK_TO_END((sal_uInt64) 0xFFFFFFFFFFFFFFFFul) )
1326 n = GetSize();
1327 if( n > THRESHOLD32768L && !m_pStrm )
1328 {
1329 SetSize( n );
1330 if( GetError() != ERRCODE_NONEErrCode(0) )
1331 return Tell();
1332 else
1333 return n;
1334 }
1335 else if( m_pStrm )
1336 {
1337 n = m_pStrm->Seek( n );
1338 SetError( m_pStrm->GetError() );
1339 return n;
1340 }
1341 else
1342 return SvMemoryStream::SeekPos( n );
1343}
1344
1345void StgTmpStrm::FlushData()
1346{
1347 if( m_pStrm )
1348 {
1349 m_pStrm->Flush();
1350 SetError( m_pStrm->GetError() );
1351 }
1352 else
1353 SvMemoryStream::FlushData();
1354}
1355
1356/* vim:set shiftwidth=4 softtabstop=4 expandtab: */