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 
StgFAT(StgStrm & r,bool m)41 StgFAT::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 
GetPhysPage(sal_Int32 nByteOff)53 rtl::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 
GetNextPage(sal_Int32 nPg)70 sal_Int32 StgFAT::GetNextPage( sal_Int32 nPg )
71 {
72     if (nPg >= 0)
73     {
74         if (nPg > (SAL_MAX_INT32 >> 2))
75             return STG_EOF;
76         rtl::Reference< StgPage > pPg = GetPhysPage( nPg << 2 );
77         nPg = pPg.is() ? StgCache::GetFromPage( pPg, m_nOffset >> 2 ) : STG_EOF;
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 
FindBlock(sal_Int32 & nPgs)88 sal_Int32 StgFAT::FindBlock( sal_Int32& nPgs )
89 {
90     sal_Int32 nMinStart = STG_EOF, nMinLen = 0;
91     sal_Int32 nMaxStart = STG_EOF, nMaxLen = 0x7FFFFFFFL;
92     sal_Int32 nTmpStart = STG_EOF, 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;
106         }
107         sal_Int32 nCur = StgCache::GetFromPage( pPg, nEntry );
108         if( nCur == STG_FREE )
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;
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 && nMaxStart != STG_EOF )
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;
168     }
169     if( nMinStart != STG_EOF )
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 
MakeChain(sal_Int32 nStart,sal_Int32 nPgs)181 bool 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 );
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 
AllocPages(sal_Int32 nBgn,sal_Int32 nPgs)212 sal_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;
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 )
230                 break;
231             if( ( nBegin + nAlloc ) > m_nMaxPage )
232                 m_nMaxPage = nBegin + nAlloc;
233             if( !MakeChain( nBegin, nAlloc ) )
234                 return STG_EOF;
235             if( nOrig == STG_EOF )
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;
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;
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 || nPgs )
265     {
266         m_rStrm.GetIo().SetError( SVSTREAM_FILEFORMAT_ERROR );
267         return STG_EOF; // 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 
InitNew(sal_Int32 nPage1)276 bool 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 );
293             nPage1++;
294         }
295     }
296     return true;
297 }
298 
299 // Release a chain
300 
FreePages(sal_Int32 nStart,bool bAll)301 bool 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 : STG_EOF );
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 
StgStrm(StgIo & r)322 StgStrm::StgStrm( StgIo& r )
323     : m_nPos(0),
324       m_bBytePosValid(true),
325       m_rIo(r),
326       m_pEntry(nullptr),
327       m_nStart(STG_EOF),
328       m_nSize(0),
329       m_nPage(STG_EOF),
330       m_nOffset(0),
331       m_nPageSize(m_rIo.GetPhysPageSize())
332 {
333 }
334 
~StgStrm()335 StgStrm::~StgStrm()
336 {
337 }
338 
339 // Attach the stream to the given entry.
340 
SetEntry(StgDirEntry & r)341 void 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  */
scanBuildPageChainCache()355 sal_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");
378             bError = true;
379         }
380 
381         nOptSize += m_nPageSize;
382     }
383     if (bError)
384     {
385         SAL_WARN("sot", "returning wrong format error");
386         m_rIo.SetError( ERRCODE_IO_WRONGFORMAT );
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.
Pos2Page(sal_Int32 nBytePos)396 bool 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());
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");
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 <<
460                  " beyond page cache size " << m_aPagesCache.size());
461         // fdo#84229 - handle seek to end and back as eg. XclImpStream expects
462         m_nPage = STG_EOF;
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;
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 
Copy(sal_Int32 nFrom,sal_Int32 nBytes)496 bool 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_ERROR );
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_ERROR );
520                 return false;
521             }
522         }
523         nTo = m_pFat->GetNextPage( nTo );
524     }
525     return true;
526 }
527 
SetSize(sal_Int32 nBytes)528 bool 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 )
545             return false;
546         if( m_nStart == STG_EOF )
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;
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 
StgFATStrm(StgIo & r,sal_Int32 nFatStrmSize)579 StgFATStrm::StgFATStrm(StgIo& r, sal_Int32 nFatStrmSize) : StgStrm( r )
580 {
581     m_pFat.reset( new StgFAT( *this, true ) );
582     m_nSize = nFatStrmSize;
583 }
584 
Pos2Page(sal_Int32 nBytePos)585 bool 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 
GetPage(sal_Int32 nOff,bool bMake,sal_uInt16 * pnMasterAlloc)600 sal_Int32 StgFATStrm::GetPage(sal_Int32 nOff, bool bMake, sal_uInt16 *pnMasterAlloc)
601 {
602     OSL_ENSURE( nOff >= 0, "The offset may not be negative!" );
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 || nFAT == STG_FREE )
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 );
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;
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;
647                         rtl::Reference< StgPage > pPg = m_rIo.Get( m_nPage, true );
648                         if( !pPg.is() )
649                             return STG_EOF;
650                         m_rIo.SetToPage( pPg, m_nOffset >> 2, STG_MASTER );
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_GENERALERROR );
672     return STG_EOF;
673 }
674 
675 
676 // Set the page number entry for the given page offset.
677 
SetPage(short nOff,sal_Int32 nNewPage)678 bool StgFATStrm::SetPage( short nOff, sal_Int32 nNewPage )
679 {
680     OSL_ENSURE( nOff >= 0, "The offset may not be negative!" );
681     m_aPagesCache.clear();
682     m_aUsedPageNumbers.clear();
683 
684     bool bRes = true;
685     if( nOff < StgHeader::GetFAT1Size() )
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 || nFAT == STG_FREE )
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_GENERALERROR );
714             bRes = false;
715         }
716     }
717 
718     // lock the page against access
719     if( bRes )
720     {
721         Pos2Page( nNewPage << 2 );
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 );
725         else
726             bRes = false;
727     }
728     return bRes;
729 }
730 
SetSize(sal_Int32 nBytes)731 bool StgFATStrm::SetSize( sal_Int32 nBytes )
732 {
733     if ( nBytes < 0 )
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 )
744     {
745         // release master pages
746         for( short i = nNew; i < nOld; i++ )
747             SetPage( i, STG_FREE );
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 )
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!" );
766             sal_Int32 nNewPage = m_pFat->FindBlock( n );
767             if( nNewPage == STG_EOF )
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 );
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 );
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 
StgDataStrm(StgIo & r,sal_Int32 nBgn,sal_Int32 nLen)833 StgDataStrm::StgDataStrm( StgIo& r, sal_Int32 nBgn, sal_Int32 nLen ) : StgStrm( r )
834 {
835     Init( nBgn, nLen );
836 }
837 
StgDataStrm(StgIo & r,StgDirEntry & p)838 StgDataStrm::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 
Init(sal_Int32 nBgn,sal_Int32 nLen)845 void 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!" );
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 
SetSize(sal_Int32 nBytes)866 bool 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 
GetPtr(sal_Int32 Pos,bool bDirty)898 void* 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 
Read(void * pBuf,sal_Int32 n)917 sal_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 
Write(const void * pBuf,sal_Int32 n)972 sal_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 
StgSmallStrm(StgIo & r,sal_Int32 nBgn)1040 StgSmallStrm::StgSmallStrm( StgIo& r, sal_Int32 nBgn ) : StgStrm( r )
1041 {
1042     Init( nBgn, 0 );
1043 }
1044 
StgSmallStrm(StgIo & r,StgDirEntry & p)1045 StgSmallStrm::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 
Init(sal_Int32 nBgn,sal_Int32 nLen)1052 void 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!" );
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 
Read(void * pBuf,sal_Int32 n)1069 sal_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 
Write(const void * pBuf,sal_Int32 n)1108 sal_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 THRESHOLD 32768L
1157 
StgTmpStrm(sal_uInt64 nInitSize)1158 StgTmpStrm::StgTmpStrm( sal_uInt64 nInitSize )
1159           : SvMemoryStream( nInitSize > THRESHOLD
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 > THRESHOLD )
1167         SetSize( nInitSize );
1168 }
1169 
Copy(StgTmpStrm & rSrc)1170 bool 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_NONE )
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 
~StgTmpStrm()1198 StgTmpStrm::~StgTmpStrm()
1199 {
1200     if( m_pStrm )
1201     {
1202         m_pStrm->Close();
1203         osl::File::remove( m_aName );
1204         delete m_pStrm;
1205     }
1206 }
1207 
GetSize() const1208 sal_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 
SetSize(sal_uInt64 n)1220 void StgTmpStrm::SetSize(sal_uInt64 n)
1221 {
1222     if( m_pStrm )
1223         m_pStrm->SetStreamSize( n );
1224     else
1225     {
1226         if( n > THRESHOLD )
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_NONE )
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 
GetData(void * pData,std::size_t n)1289 std::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 
PutData(const void * pData,std::size_t n)1301 std::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 > THRESHOLD && !m_pStrm )
1306     {
1307         SetSize( nNew );
1308         if( GetError() != ERRCODE_NONE )
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 
SeekPos(sal_uInt64 n)1321 sal_uInt64 StgTmpStrm::SeekPos(sal_uInt64 n)
1322 {
1323     // check if a truncated STREAM_SEEK_TO_END was passed
1324     assert(n != SAL_MAX_UINT32);
1325     if( n == STREAM_SEEK_TO_END )
1326         n = GetSize();
1327     if( n > THRESHOLD && !m_pStrm )
1328     {
1329         SetSize( n );
1330         if( GetError() != ERRCODE_NONE )
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 
FlushData()1345 void 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: */
1357