xref: /AOO41X/main/sot/source/sdstor/stgstrms.cxx (revision fe22d2cfc602815794415026f1317bd625db6f83)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sot.hxx"
26 
27 #include <string.h>     // memcpy()
28 
29 #include <osl/file.hxx>
30 #include <tools/tempfile.hxx>
31 #include <tools/debug.hxx>
32 #include <set>
33 
34 #include "sot/stg.hxx"
35 #include "stgelem.hxx"
36 #include "stgcache.hxx"
37 #include "stgstrms.hxx"
38 #include "stgdir.hxx"
39 #include "stgio.hxx"
40 
41 #define __HUGE
42 
43 ///////////////////////////// class StgFAT ///////////////////////////////
44 
45 // The FAT class performs FAT operations on an underlying storage stream.
46 // This stream is either the master FAT stream (m == sal_True ) or a normal
47 // storage stream, which then holds the FAT for small data allocations.
48 
StgFAT(StgStrm & r,sal_Bool m)49 StgFAT::StgFAT( StgStrm& r, sal_Bool m ) : rStrm( r )
50 {
51     bPhys   = m;
52     nPageSize = rStrm.GetIo().GetPhysPageSize();
53     nEntries  = nPageSize >> 2;
54     nOffset   = 0;
55     nMaxPage  = 0;
56     nLimit    = 0;
57 }
58 
59 // Retrieve the physical page for a given byte offset.
60 
GetPhysPage(sal_Int32 nByteOff)61 StgPage* StgFAT::GetPhysPage( sal_Int32 nByteOff )
62 {
63     StgPage* pPg = NULL;
64     // Position within the underlying stream
65     // use the Pos2Page() method of the stream
66     if( rStrm.Pos2Page( nByteOff ) )
67     {
68         nOffset = rStrm.GetOffset();
69         sal_Int32 nPhysPage = rStrm.GetPage();
70         // get the physical page (must be present)
71         pPg = rStrm.GetIo().Get( nPhysPage, sal_True );
72     }
73     return pPg;
74 }
75 
76 // Get the follow page for a certain FAT page.
77 
GetNextPage(sal_Int32 nPg)78 sal_Int32 StgFAT::GetNextPage( sal_Int32 nPg )
79 {
80     if( nPg >= 0 )
81     {
82       StgPage* pPg = GetPhysPage( nPg << 2 );
83         nPg = pPg ? pPg->GetPage( nOffset >> 2 ) : STG_EOF;
84     }
85     return nPg;
86 }
87 
88 // Find the best fit block for the given size. Return
89 // the starting block and its size or STG_EOF and 0.
90 // nLastPage is a stopper which tells the current
91 // underlying stream size. It is treated as a recommendation
92 // to abort the search to inhibit excessive file growth.
93 
FindBlock(sal_Int32 & nPgs)94 sal_Int32 StgFAT::FindBlock( sal_Int32& nPgs )
95 {
96     sal_Int32 nMinStart = STG_EOF, nMinLen = 0;
97     sal_Int32 nMaxStart = STG_EOF, nMaxLen = 0x7FFFFFFFL;
98     sal_Int32 nTmpStart = STG_EOF, nTmpLen = 0;
99     sal_Int32 nPages    = rStrm.GetSize() >> 2;
100     sal_Bool bFound     = sal_False;
101     StgPage* pPg = NULL;
102     short nEntry = 0;
103     for( sal_Int32 i = 0; i < nPages; i++, nEntry++ )
104     {
105         if( !( nEntry % nEntries ) )
106         {
107             // load the next page for that stream
108             nEntry = 0;
109             pPg = GetPhysPage( i << 2 );
110             if( !pPg )
111                 return STG_EOF;
112         }
113         sal_Int32 nCur = pPg->GetPage( nEntry );
114         if( nCur == STG_FREE )
115         {
116             // count the size of this area
117             if( nTmpLen )
118                 nTmpLen++;
119             else
120                 nTmpStart = i,
121                 nTmpLen   = 1;
122             if( nTmpLen == nPgs
123              // If we already did find a block, stop when reaching the limit
124              || ( bFound && ( nEntry >= nLimit ) ) )
125                 break;
126         }
127         else if( nTmpLen )
128         {
129             if( nTmpLen > nPgs && nTmpLen < nMaxLen )
130                 // block > requested size
131                 nMaxLen = nTmpLen, nMaxStart = nTmpStart, bFound = sal_True;
132             else if( nTmpLen >= nMinLen )
133             {
134                 // block < requested size
135                 nMinLen = nTmpLen, nMinStart = nTmpStart;
136                 bFound = sal_True;
137                 if( nTmpLen == nPgs )
138                     break;
139             }
140             nTmpStart = STG_EOF;
141             nTmpLen   = 0;
142         }
143     }
144     // Determine which block to use.
145     if( nTmpLen )
146     {
147         if( nTmpLen > nPgs  && nTmpLen < nMaxLen )
148             // block > requested size
149             nMaxLen = nTmpLen, nMaxStart = nTmpStart;
150         else if( nTmpLen >= nMinLen )
151             // block < requested size
152             nMinLen = nTmpLen, nMinStart = nTmpStart;
153     }
154     if( nMinStart != STG_EOF && nMaxStart != STG_EOF )
155     {
156         // two areas found; return the best fit area
157         sal_Int32 nMinDiff = nPgs - nMinLen;
158         sal_Int32 nMaxDiff = nMaxLen - nPgs;
159         if( nMinDiff > nMaxDiff )
160             nMinStart = STG_EOF;
161     }
162     if( nMinStart != STG_EOF )
163     {
164         nPgs = nMinLen; return nMinStart;
165     }
166     else
167     {
168         return nMaxStart;
169     }
170 }
171 
172 // Set up the consecutive chain for a given block.
173 
MakeChain(sal_Int32 nStart,sal_Int32 nPgs)174 sal_Bool StgFAT::MakeChain( sal_Int32 nStart, sal_Int32 nPgs )
175 {
176     sal_Int32 nPos = nStart << 2;
177     StgPage* pPg = GetPhysPage( nPos );
178     if( !pPg || !nPgs )
179         return sal_False;
180     while( --nPgs )
181     {
182         if( nOffset >= nPageSize )
183         {
184             pPg = GetPhysPage( nPos );
185             if( !pPg )
186                 return sal_False;
187         }
188         pPg->SetPage( nOffset >> 2, ++nStart );
189         nOffset += 4;
190         nPos += 4;
191     }
192     if( nOffset >= nPageSize )
193     {
194         pPg = GetPhysPage( nPos );
195         if( !pPg )
196             return sal_False;
197     }
198     pPg->SetPage( nOffset >> 2, STG_EOF );
199     return sal_True;
200 }
201 
202 // Allocate a block of data from the given page number on.
203 // It the page number is != STG_EOF, chain the block.
204 
AllocPages(sal_Int32 nBgn,sal_Int32 nPgs)205 sal_Int32 StgFAT::AllocPages( sal_Int32 nBgn, sal_Int32 nPgs )
206 {
207     sal_Int32 nOrig = nBgn;
208     sal_Int32 nLast = nBgn;
209     sal_Int32 nBegin = STG_EOF;
210     sal_Int32 nAlloc;
211     sal_Int32 nPages = rStrm.GetSize() >> 2;
212     short nPasses = 0;
213     // allow for two passes
214     while( nPasses < 2 )
215     {
216         // try to satisfy the request from the pool of free pages
217         while( nPgs )
218         {
219             nAlloc = nPgs;
220             nBegin = FindBlock( nAlloc );
221             // no more blocks left in present alloc chain
222             if( nBegin == STG_EOF )
223                 break;
224             if( ( nBegin + nAlloc ) > nMaxPage )
225                 nMaxPage = nBegin + nAlloc;
226             if( !MakeChain( nBegin, nAlloc ) )
227                 return STG_EOF;
228             if( nOrig == STG_EOF )
229                 nOrig = nBegin;
230             else
231             {
232                 // Patch the chain
233                 StgPage* pPg = GetPhysPage( nLast << 2 );
234                 if( !pPg )
235                     return STG_EOF;
236                 pPg->SetPage( nOffset >> 2, nBegin );
237             }
238             nLast = nBegin + nAlloc - 1;
239             nPgs -= nAlloc;
240         }
241         if( nPgs && !nPasses )
242         {
243             // we need new, fresh space, so allocate and retry
244             if( !rStrm.SetSize( ( nPages + nPgs ) << 2 ) )
245                 return STG_EOF;
246             if( !bPhys && !InitNew( nPages ) )
247                 return sal_False;
248             nPages = rStrm.GetSize() >> 2;
249             nPasses++;
250         }
251         else
252             break;
253     }
254     // now we should have a chain for the complete block
255     if( nBegin == STG_EOF || nPgs )
256     {
257         rStrm.GetIo().SetError( SVSTREAM_FILEFORMAT_ERROR );
258         return STG_EOF; // bad structure
259     }
260     return nOrig;
261 }
262 
263 // Initialize newly allocated pages for a standard FAT stream
264 // It can be assumed that the stream size is always on
265 // a page boundary
266 
InitNew(sal_Int32 nPage1)267 sal_Bool StgFAT::InitNew( sal_Int32 nPage1 )
268 {
269     sal_Int32 n = ( ( rStrm.GetSize() >> 2 ) - nPage1 ) / nEntries;
270     if ( n > 0 )
271     {
272         while( n-- )
273         {
274             StgPage* pPg = NULL;
275             // Position within the underlying stream
276             // use the Pos2Page() method of the stream
277             rStrm.Pos2Page( nPage1 << 2 );
278             // Initialize the page
279             pPg = rStrm.GetIo().Copy( rStrm.GetPage(), STG_FREE );
280             if ( !pPg )
281                 return sal_False;
282             for( short i = 0; i < nEntries; i++ )
283                 pPg->SetPage( i, STG_FREE );
284             nPage1++;
285         }
286     }
287     return sal_True;
288 }
289 
290 // Release a chain
291 
FreePages(sal_Int32 nStart,sal_Bool bAll)292 sal_Bool StgFAT::FreePages( sal_Int32 nStart, sal_Bool bAll )
293 {
294     while( nStart >= 0 )
295     {
296         StgPage* pPg = GetPhysPage( nStart << 2 );
297         if( !pPg )
298             return sal_False;
299         nStart = pPg->GetPage( nOffset >> 2 );
300         // The first released page is either set to EOF or FREE
301         pPg->SetPage( nOffset >> 2, bAll ? STG_FREE : STG_EOF );
302         bAll = sal_True;
303     }
304     return sal_True;
305 }
306 
307 ///////////////////////////// class StgStrm ////////////////////////////////
308 
309 // The base stream class provides basic functionality for seeking
310 // and accessing the data on a physical basis. It uses the built-in
311 // FAT class for the page allocations.
312 
StgStrm(StgIo & r)313 StgStrm::StgStrm( StgIo& r ) : rIo( r )
314 {
315     pFat    = NULL;
316     nStart  = nPage = STG_EOF;
317     nOffset = 0;
318     pEntry  = NULL;
319     nPos = nSize = 0;
320     nPageSize = rIo.GetPhysPageSize();
321 }
322 
~StgStrm()323 StgStrm::~StgStrm()
324 {
325     delete pFat;
326 }
327 
328 // Attach the stream to the given entry.
329 
SetEntry(StgDirEntry & r)330 void StgStrm::SetEntry( StgDirEntry& r )
331 {
332     r.aEntry.SetLeaf( STG_DATA, nStart );
333     r.aEntry.SetSize( nSize );
334     pEntry = &r;
335     r.SetDirty();
336 }
337 
338 // Compute page number and offset for the given byte position.
339 // If the position is behind the size, set the stream right
340 // behind the EOF.
341 
Pos2Page(sal_Int32 nBytePos)342 sal_Bool StgStrm::Pos2Page( sal_Int32 nBytePos )
343 {
344     if ( !pFat )
345         return sal_False;
346 
347     sal_Int32 nRel, nBgn;
348     // Values < 0 seek to the end
349     if( nBytePos < 0 || nBytePos >= nSize )
350         nBytePos = nSize;
351     // Adjust the position back to offset 0
352     nPos -= nOffset;
353     sal_Int32 nMask = ~( nPageSize - 1 );
354     sal_Int32 nOld = nPos & nMask;
355     sal_Int32 nNew = nBytePos & nMask;
356     nOffset = (short) ( nBytePos & ~nMask );
357     nPos = nBytePos;
358     if( nOld == nNew )
359         return sal_True;
360     if( nNew > nOld )
361     {
362         // the new position is behind the current, so an incremental
363         // positioning is OK. Set the page relative position
364         nRel = nNew - nOld;
365         nBgn = nPage;
366     }
367     else
368     {
369         // the new position is before the current, so we have to scan
370         // the entire chain.
371         nRel = nNew;
372         nBgn = nStart;
373     }
374     // now, traverse the FAT chain.
375     nRel /= nPageSize;
376     sal_Int32 nLast = STG_EOF;
377     while( nRel && nBgn >= 0 )
378     {
379         nLast = nBgn;
380         nBgn = pFat->GetNextPage( nBgn );
381         nRel--;
382     }
383     // special case: seek to 1st byte of new, unallocated page
384     // (in case the file size is a multiple of the page size)
385     if( nBytePos == nSize && nBgn == STG_EOF && !nRel && !nOffset )
386         nBgn = nLast, nOffset = nPageSize;
387     if( nBgn < 0 && nBgn != STG_EOF )
388     {
389         rIo.SetError( SVSTREAM_FILEFORMAT_ERROR );
390         nBgn = STG_EOF;
391         nOffset = nPageSize;
392     }
393     nPage = nBgn;
394     return sal_Bool( nRel == 0 && nPage >= 0 );
395 }
396 
397 // Retrieve the physical page for a given byte offset.
398 
GetPhysPage(sal_Int32 nBytePos,sal_Bool bForce)399 StgPage* StgStrm::GetPhysPage( sal_Int32 nBytePos, sal_Bool bForce )
400 {
401     if( !Pos2Page( nBytePos ) )
402         return NULL;
403     return rIo.Get( nPage, bForce );
404 }
405 
406 // Copy an entire stream. Both streams are allocated in the FAT.
407 // The target stream is this stream.
408 
Copy(sal_Int32 nFrom,sal_Int32 nBytes)409 sal_Bool StgStrm::Copy( sal_Int32 nFrom, sal_Int32 nBytes )
410 {
411     if ( !pFat )
412         return sal_False;
413 
414     sal_Int32 nTo = nStart;
415     sal_Int32 nPgs = ( nBytes + nPageSize - 1 ) / nPageSize;
416     while( nPgs-- )
417     {
418         if( nTo < 0 )
419         {
420             rIo.SetError( SVSTREAM_FILEFORMAT_ERROR );
421             return sal_False;
422         }
423         rIo.Copy( nTo, nFrom );
424         if( nFrom >= 0 )
425         {
426             nFrom = pFat->GetNextPage( nFrom );
427             if( nFrom < 0 )
428             {
429                 rIo.SetError( SVSTREAM_FILEFORMAT_ERROR );
430                 return sal_False;
431             }
432         }
433         nTo = pFat->GetNextPage( nTo );
434     }
435     return sal_True;
436 }
437 
SetSize(sal_Int32 nBytes)438 sal_Bool StgStrm::SetSize( sal_Int32 nBytes )
439 {
440     if ( nBytes < 0 || !pFat )
441         return sal_False;
442 
443     // round up to page size
444     sal_Int32 nOld = ( ( nSize + nPageSize - 1 ) / nPageSize ) * nPageSize;
445     sal_Int32 nNew = ( ( nBytes + nPageSize - 1 ) / nPageSize ) * nPageSize;
446     if( nNew > nOld )
447     {
448         if( !Pos2Page( nSize ) )
449             return sal_False;
450         sal_Int32 nBgn = pFat->AllocPages( nPage, ( nNew - nOld ) / nPageSize );
451         if( nBgn == STG_EOF )
452             return sal_False;
453         if( nStart == STG_EOF )
454             nStart = nPage = nBgn;
455     }
456     else if( nNew < nOld )
457     {
458         sal_Bool bAll = sal_Bool( nBytes == 0 );
459         if( !Pos2Page( nBytes ) || !pFat->FreePages( nPage, bAll ) )
460             return sal_False;
461         if( bAll )
462             nStart = nPage = STG_EOF;
463     }
464     if( pEntry )
465     {
466         // change the dir entry?
467         if( !nSize || !nBytes )
468             pEntry->aEntry.SetLeaf( STG_DATA, nStart );
469         pEntry->aEntry.SetSize( nBytes );
470         pEntry->SetDirty();
471     }
472     nSize = nBytes;
473     pFat->SetLimit( GetPages() );
474     return sal_True;
475 }
476 
477 // Return the # of allocated pages
478 
GetPages()479 sal_Int32 StgStrm::GetPages()
480 {
481     return ( nSize + nPageSize - 1 ) / nPageSize;
482 }
483 
484 //////////////////////////// class StgFATStrm //////////////////////////////
485 
486 // The FAT stream class provides physical access to the master FAT.
487 // Since this access is implemented as a StgStrm, we can use the
488 // FAT allocator.
489 
StgFATStrm(StgIo & r)490 StgFATStrm::StgFATStrm( StgIo& r ) : StgStrm( r )
491 {
492     pFat = new StgFAT( *this, sal_True );
493     nSize = rIo.aHdr.GetFATSize() * nPageSize;
494 }
495 
Pos2Page(sal_Int32 nBytePos)496 sal_Bool StgFATStrm::Pos2Page( sal_Int32 nBytePos )
497 {
498     // Values < 0 seek to the end
499     if( nBytePos < 0 || nBytePos >= nSize  )
500         nBytePos = nSize ? nSize - 1 : 0;
501     nPage   = nBytePos / nPageSize;
502     nOffset = (short) ( nBytePos % nPageSize );
503     nPos    = nBytePos;
504     nPage   = GetPage( (short) nPage, sal_False );
505     return sal_Bool( nPage >= 0 );
506 }
507 
508 // Retrieve the physical page for a given byte offset.
509 // Since Pos2Page() already has computed the physical offset,
510 // use the byte offset directly.
511 
GetPhysPage(sal_Int32 nBytePos,sal_Bool bForce)512 StgPage* StgFATStrm::GetPhysPage( sal_Int32 nBytePos, sal_Bool bForce )
513 {
514     OSL_ENSURE( nBytePos >= 0, "The value may not be negative!" );
515     return rIo.Get( nBytePos / ( nPageSize >> 2 ), bForce );
516 }
517 
518 // Get the page number entry for the given page offset.
519 
GetPage(short nOff,sal_Bool bMake,sal_uInt16 * pnMasterAlloc)520 sal_Int32 StgFATStrm::GetPage( short nOff, sal_Bool bMake, sal_uInt16 *pnMasterAlloc )
521 {
522     OSL_ENSURE( nOff >= 0, "The offset may not be negative!" );
523     if( pnMasterAlloc ) *pnMasterAlloc = 0;
524     if( nOff < rIo.aHdr.GetFAT1Size() )
525         return rIo.aHdr.GetFATPage( nOff );
526     sal_Int32 nMaxPage = nSize >> 2;
527     nOff = nOff - rIo.aHdr.GetFAT1Size();
528     // Anzahl der Masterpages, durch die wir iterieren muessen
529     sal_uInt16 nMasterCount =  ( nPageSize >> 2 ) - 1;
530     sal_uInt16 nBlocks = nOff / nMasterCount;
531     // Offset in letzter Masterpage
532     nOff = nOff % nMasterCount;
533 
534     StgPage* pOldPage = 0;
535     StgPage* pMaster = 0;
536     sal_Int32 nFAT = rIo.aHdr.GetFATChain();
537     for( sal_uInt16 nCount = 0; nCount <= nBlocks; nCount++ )
538     {
539         if( nFAT == STG_EOF || nFAT == STG_FREE )
540         {
541             if( bMake )
542             {
543                 // create a new master page
544                 nFAT = nMaxPage++;
545                 pMaster = rIo.Copy( nFAT, STG_FREE );
546                 if ( pMaster )
547                 {
548                     for( short k = 0; k < ( nPageSize >> 2 ); k++ )
549                         pMaster->SetPage( k, STG_FREE );
550                     // Verkettung herstellen
551                     if( !pOldPage )
552                         rIo.aHdr.SetFATChain( nFAT );
553                     else
554                         pOldPage->SetPage( nMasterCount, nFAT );
555                     if( nMaxPage >= rIo.GetPhysPages() )
556                         if( !rIo.SetSize( nMaxPage ) )
557                             return STG_EOF;
558                     // mark the page as used
559                     // Platz fuer Masterpage schaffen
560                     if( !pnMasterAlloc ) // Selbst Platz schaffen
561                     {
562                         if( !Pos2Page( nFAT << 2 ) )
563                             return STG_EOF;
564                         StgPage* pPg = rIo.Get( nPage, sal_True );
565                         if( !pPg )
566                             return STG_EOF;
567                         pPg->SetPage( nOffset >> 2, STG_MASTER );
568                     }
569                     else
570                         (*pnMasterAlloc)++;
571                     rIo.aHdr.SetMasters( nCount + 1 );
572                     pOldPage = pMaster;
573                 }
574             }
575         }
576         else
577         {
578             pMaster = rIo.Get( nFAT, sal_True );
579             if ( pMaster )
580             {
581                 nFAT = pMaster->GetPage( nMasterCount );
582                 pOldPage = pMaster;
583             }
584         }
585     }
586     if( pMaster )
587         return pMaster->GetPage( nOff );
588     rIo.SetError( SVSTREAM_GENERALERROR );
589     return STG_EOF;
590 }
591 
592 
593 // Set the page number entry for the given page offset.
594 
SetPage(short nOff,sal_Int32 nNewPage)595 sal_Bool StgFATStrm::SetPage( short nOff, sal_Int32 nNewPage )
596 {
597     OSL_ENSURE( nOff >= 0, "The offset may not be negative!" );
598     sal_Bool bRes = sal_True;
599     if( nOff < rIo.aHdr.GetFAT1Size() )
600         rIo.aHdr.SetFATPage( nOff, nNewPage );
601     else
602     {
603         nOff = nOff - rIo.aHdr.GetFAT1Size();
604         // Anzahl der Masterpages, durch die wir iterieren muessen
605         sal_uInt16 nMasterCount =  ( nPageSize >> 2 ) - 1;
606         sal_uInt16 nBlocks = nOff / nMasterCount;
607         // Offset in letzter Masterpage
608         nOff = nOff % nMasterCount;
609 
610         StgPage* pMaster = 0;
611         sal_Int32 nFAT = rIo.aHdr.GetFATChain();
612         for( sal_uInt16 nCount = 0; nCount <= nBlocks; nCount++ )
613         {
614             if( nFAT == STG_EOF || nFAT == STG_FREE )
615             {
616                 pMaster = 0;
617                 break;
618             }
619             pMaster = rIo.Get( nFAT, sal_True );
620             if ( pMaster )
621                 nFAT = pMaster->GetPage( nMasterCount );
622         }
623         if( pMaster )
624             pMaster->SetPage( nOff, nNewPage );
625         else
626         {
627             rIo.SetError( SVSTREAM_GENERALERROR );
628             bRes = sal_False;
629         }
630     }
631 
632     // lock the page against access
633     if( bRes )
634     {
635         Pos2Page( nNewPage << 2 );
636         StgPage* pPg = rIo.Get( nPage, sal_True );
637         if( pPg )
638             pPg->SetPage( nOffset >> 2, STG_FAT );
639         else
640             bRes = sal_False;
641     }
642     return bRes;
643 }
644 
SetSize(sal_Int32 nBytes)645 sal_Bool StgFATStrm::SetSize( sal_Int32 nBytes )
646 {
647     if ( nBytes < 0 )
648         return sal_False;
649 
650     // Set the number of entries to a multiple of the page size
651     short nOld = (short) ( ( nSize + ( nPageSize - 1 ) ) / nPageSize );
652     short nNew = (short) (
653         ( nBytes + ( nPageSize - 1 ) ) / nPageSize ) ;
654     if( nNew < nOld )
655     {
656         // release master pages
657         for( short i = nNew; i < nOld; i++ )
658             SetPage( i, STG_FREE );
659     }
660     else
661     {
662         while( nOld < nNew )
663         {
664             // allocate master pages
665             // find a free master page slot
666             sal_Int32 nPg = 0;
667             sal_uInt16 nMasterAlloc = 0;
668             nPg = GetPage( nOld, sal_True, &nMasterAlloc );
669             if( nPg == STG_EOF )
670                 return sal_False;
671             // 4 Bytes have been used for Allocation of each MegaMasterPage
672             nBytes += nMasterAlloc << 2;
673 
674             // find a free page using the FAT allocator
675             sal_Int32 n = 1;
676             OSL_ENSURE( pFat, "The pointer is always initializer here!" );
677             sal_Int32 nNewPage = pFat->FindBlock( n );
678             if( nNewPage == STG_EOF )
679             {
680                 // no free pages found; create a new page
681                 // Since all pages are allocated, extend
682                 // the file size for the next page!
683                 nNewPage = nSize >> 2;
684                 // if a MegaMasterPage was created avoid taking
685                 // the same Page
686                 nNewPage += nMasterAlloc;
687                 // adjust the file size if necessary
688                 if( nNewPage >= rIo.GetPhysPages() )
689                     if( !rIo.SetSize( nNewPage + 1 ) )
690                         return sal_False;
691             }
692             // Set up the page with empty entries
693             StgPage* pPg = rIo.Copy( nNewPage, STG_FREE );
694             if ( !pPg )
695                 return sal_False;
696             for( short j = 0; j < ( nPageSize >> 2 ); j++ )
697                 pPg->SetPage( j, STG_FREE );
698 
699             // store the page number into the master FAT
700             // Set the size before so the correct FAT can be found
701             nSize = ( nOld + 1 ) * nPageSize;
702             SetPage( nOld, nNewPage );
703 
704             // MegaMasterPages were created, mark it them as used
705 
706             sal_uInt32 nMax = rIo.aHdr.GetMasters( );
707             sal_uInt32 nFAT = rIo.aHdr.GetFATChain();
708             if( nMasterAlloc )
709                 for( sal_uInt16 nCount = 0; nCount < nMax; nCount++ )
710                 {
711                     if( !Pos2Page( nFAT << 2 ) )
712                         return sal_False;
713                     if( nMax - nCount <= nMasterAlloc )
714                     {
715                         StgPage* piPg = rIo.Get( nPage, sal_True );
716                         if( !piPg )
717                             return sal_False;
718                         piPg->SetPage( nOffset >> 2, STG_MASTER );
719                     }
720                     StgPage* pPage = rIo.Get( nFAT, sal_True );
721                     if( !pPage ) return sal_False;
722                     nFAT = pPage->GetPage( (nPageSize >> 2 ) - 1 );
723                 }
724 
725             nOld++;
726             // We have used up 4 bytes for the STG_FAT entry
727             nBytes += 4;
728             nNew = (short) (
729                 ( nBytes + ( nPageSize - 1 ) ) / nPageSize );
730         }
731     }
732     nSize = nNew * nPageSize;
733     rIo.aHdr.SetFATSize( nNew );
734     return sal_True;
735 }
736 
737 /////////////////////////// class StgDataStrm //////////////////////////////
738 
739 // This class is a normal physical stream which can be initialized
740 // either with an existing dir entry or an existing FAT chain.
741 // The stream has a size increment which normally is 1, but which can be
742 // set to any value is you want the size to be incremented by certain values.
743 
StgDataStrm(StgIo & r,sal_Int32 nBgn,sal_Int32 nLen)744 StgDataStrm::StgDataStrm( StgIo& r, sal_Int32 nBgn, sal_Int32 nLen ) : StgStrm( r )
745 {
746     Init( nBgn, nLen );
747 }
748 
StgDataStrm(StgIo & r,StgDirEntry & p)749 StgDataStrm::StgDataStrm( StgIo& r, StgDirEntry& p ) : StgStrm( r )
750 {
751     pEntry = &p;
752     Init( p.aEntry.GetLeaf( STG_DATA ),
753           p.aEntry.GetSize() );
754 }
755 
Init(sal_Int32 nBgn,sal_Int32 nLen)756 void StgDataStrm::Init( sal_Int32 nBgn, sal_Int32 nLen )
757 {
758     if ( rIo.pFAT )
759         pFat = new StgFAT( *rIo.pFAT, sal_True );
760 
761     OSL_ENSURE( pFat, "The pointer should not be empty!" );
762 
763     nStart = nPage = nBgn;
764     nSize  = nLen;
765     nIncr  = 1;
766     nOffset = 0;
767     if( nLen < 0 && pFat )
768     {
769         // determine the actual size of the stream by scanning
770         // the FAT chain and counting the # of pages allocated
771         nSize = 0;
772 
773         // there may be files with double page numbers or loops of page
774         // references. This is not allowed. To be able to track this and
775         // to exit with an error, track already scanned PageNumbers here
776         // and use them to see if an already counted page is re-visited
777         std::set< sal_Int32 > nUsedPageNumbers;
778 
779         while(nBgn >= 0)
780         {
781             if(nUsedPageNumbers.find(nBgn) == nUsedPageNumbers.end())
782             {
783                 nUsedPageNumbers.insert(nBgn);
784                 nSize += nPageSize;
785                 nBgn = pFat->GetNextPage(nBgn);
786             }
787             else
788             {
789                 rIo.SetError(ERRCODE_IO_WRONGFORMAT);
790                 nBgn = -1;
791             }
792         }
793     }
794 }
795 
796 // Set the size of a physical stream.
797 
SetSize(sal_Int32 nBytes)798 sal_Bool StgDataStrm::SetSize( sal_Int32 nBytes )
799 {
800     if ( !pFat )
801         return sal_False;
802 
803     nBytes = ( ( nBytes + nIncr - 1 ) / nIncr ) * nIncr;
804     sal_Int32 nOldSz = nSize;
805     if( ( nOldSz != nBytes ) )
806     {
807         if( !StgStrm::SetSize( nBytes ) )
808             return sal_False;
809         sal_Int32 nMaxPage = pFat->GetMaxPage();
810         if( nMaxPage > rIo.GetPhysPages() )
811             if( !rIo.SetSize( nMaxPage ) )
812                 return sal_False;
813         // If we only allocated one page or less, create this
814         // page in the cache for faster throughput. The current
815         // position is the former EOF point.
816         if( ( nSize - 1 )  / nPageSize - ( nOldSz - 1 ) / nPageSize == 1 )
817         {
818             Pos2Page( nBytes );
819             if( nPage >= 0 )
820                 rIo.Copy( nPage, STG_FREE );
821         }
822     }
823     return sal_True;
824 }
825 
826 // Get the address of the data byte at a specified offset.
827 // If bForce = sal_True, a read of non-existent data causes
828 // a read fault.
829 
GetPtr(sal_Int32 Pos,sal_Bool bForce,sal_Bool bDirty)830 void* StgDataStrm::GetPtr( sal_Int32 Pos, sal_Bool bForce, sal_Bool bDirty )
831 {
832     if( Pos2Page( Pos ) )
833     {
834         StgPage* pPg = rIo.Get( nPage, bForce );
835         if( pPg )
836         {
837             pPg->SetOwner( pEntry );
838             if( bDirty )
839                 pPg->SetDirty();
840             return ((sal_uInt8 *)pPg->GetData()) + nOffset;
841         }
842     }
843     return NULL;
844 }
845 
846 // This could easily be adapted to a better algorithm by determining
847 // the amount of consecutable blocks before doing a read. The result
848 // is the number of bytes read. No error is generated on EOF.
849 
Read(void * pBuf,sal_Int32 n)850 sal_Int32 StgDataStrm::Read( void* pBuf, sal_Int32 n )
851 {
852     if ( n < 0 )
853         return 0;
854 
855     if( ( nPos + n ) > nSize )
856         n = nSize - nPos;
857     sal_Int32 nDone = 0;
858     while( n )
859     {
860         short nBytes = nPageSize - nOffset;
861         short nRes;
862         StgPage* pPg;
863         if( (sal_Int32) nBytes > n )
864             nBytes = (short) n;
865         if( nBytes )
866         {
867             void *p = (sal_uInt8 *) pBuf + nDone;
868             if( nBytes == nPageSize )
869             {
870                 pPg = rIo.Find( nPage );
871                 if( pPg )
872                 {
873                     // data is present, so use the cached data
874                     pPg->SetOwner( pEntry );
875                     memcpy( p, pPg->GetData(), nBytes );
876                     nRes = nBytes;
877                 }
878                 else
879                     // do a direct (unbuffered) read
880                     nRes = (short) rIo.Read( nPage, p, 1 ) * nPageSize;
881             }
882             else
883             {
884                 // partial block read thru the cache.
885                 pPg = rIo.Get( nPage, sal_False );
886                 if( !pPg )
887                     break;
888                 pPg->SetOwner( pEntry );
889                 memcpy( p, (sal_uInt8*)pPg->GetData() + nOffset, nBytes );
890                 nRes = nBytes;
891             }
892             nDone += nRes;
893             nPos += nRes;
894             n -= nRes;
895             nOffset = nOffset + nRes;
896             if( nRes != nBytes )
897                 break;  // read error or EOF
898         }
899         // Switch to next page if necessary
900         if( nOffset >= nPageSize && !Pos2Page( nPos ) )
901             break;
902     }
903     return nDone;
904 }
905 
Write(const void * pBuf,sal_Int32 n)906 sal_Int32 StgDataStrm::Write( const void* pBuf, sal_Int32 n )
907 {
908     if ( n < 0 )
909         return 0;
910 
911     sal_Int32 nDone = 0;
912     if( ( nPos + n ) > nSize )
913     {
914         sal_Int32 nOld = nPos;
915         if( !SetSize( nPos + n ) )
916             return 0;
917         Pos2Page( nOld );
918     }
919     while( n )
920     {
921         short nBytes = nPageSize - nOffset;
922         short nRes;
923         StgPage* pPg;
924         if( (sal_Int32) nBytes > n )
925             nBytes = (short) n;
926         if( nBytes )
927         {
928             const void *p = (const sal_uInt8 *) pBuf + nDone;
929             if( nBytes == nPageSize )
930             {
931                 pPg = rIo.Find( nPage );
932                 if( pPg )
933                 {
934                     // data is present, so use the cached data
935                     pPg->SetOwner( pEntry );
936                     memcpy( pPg->GetData(), p, nBytes );
937                     pPg->SetDirty();
938                     nRes = nBytes;
939                 }
940                 else
941                     // do a direct (unbuffered) write
942                     nRes = (short) rIo.Write( nPage, (void*) p, 1 ) * nPageSize;
943             }
944             else
945             {
946                 // partial block read thru the cache.
947                 pPg = rIo.Get( nPage, sal_False );
948                 if( !pPg )
949                     break;
950                 pPg->SetOwner( pEntry );
951                 memcpy( (sal_uInt8*)pPg->GetData() + nOffset, p, nBytes );
952                 pPg->SetDirty();
953                 nRes = nBytes;
954             }
955             nDone += nRes;
956             nPos += nRes;
957             n -= nRes;
958             nOffset = nOffset + nRes;
959             if( nRes != nBytes )
960                 break;  // read error
961         }
962         // Switch to next page if necessary
963         if( nOffset >= nPageSize && !Pos2Page( nPos ) )
964             break;
965     }
966     return nDone;
967 }
968 
969 //////////////////////////// class StgSmallStream ///////////////////////////
970 
971 // The small stream class provides access to streams with a size < 4096 bytes.
972 // This stream is a StgStream containing small pages. The FAT for this stream
973 // is also a StgStream. The start of the FAT is in the header at DataRootPage,
974 // the stream itself is pointed to by the root entry (it holds start & size).
975 
StgSmallStrm(StgIo & r,sal_Int32 nBgn,sal_Int32 nLen)976 StgSmallStrm::StgSmallStrm( StgIo& r, sal_Int32 nBgn, sal_Int32 nLen ) : StgStrm( r )
977 {
978     Init( nBgn, nLen );
979 }
980 
StgSmallStrm(StgIo & r,StgDirEntry & p)981 StgSmallStrm::StgSmallStrm( StgIo& r, StgDirEntry& p ) : StgStrm( r )
982 {
983     pEntry = &p;
984     Init( p.aEntry.GetLeaf( STG_DATA ),
985           p.aEntry.GetSize() );
986 }
987 
Init(sal_Int32 nBgn,sal_Int32 nLen)988 void StgSmallStrm::Init( sal_Int32 nBgn, sal_Int32 nLen )
989 {
990     if ( rIo.pDataFAT )
991         pFat = new StgFAT( *rIo.pDataFAT, sal_False );
992     pData = rIo.pDataStrm;
993     OSL_ENSURE( pFat && pData, "The pointers should not be empty!" );
994 
995     nPageSize = rIo.GetDataPageSize();
996     nStart =
997     nPage  = nBgn;
998     nSize  = nLen;
999 }
1000 
1001 // This could easily be adapted to a better algorithm by determining
1002 // the amount of consecutable blocks before doing a read. The result
1003 // is the number of bytes read. No error is generated on EOF.
1004 
Read(void * pBuf,sal_Int32 n)1005 sal_Int32 StgSmallStrm::Read( void* pBuf, sal_Int32 n )
1006 {
1007     // We can safely assume that reads are not huge, since the
1008     // small stream is likely to be < 64 KBytes.
1009     if( ( nPos + n ) > nSize )
1010         n = nSize - nPos;
1011     short nDone = 0;
1012     while( n )
1013     {
1014         short nBytes = nPageSize - nOffset;
1015         if( (sal_Int32) nBytes > n )
1016             nBytes = (short) n;
1017         if( nBytes )
1018         {
1019             if( !pData || !pData->Pos2Page( nPage * nPageSize + nOffset ) )
1020                 break;
1021             // all reading thru the stream
1022             short nRes = (short) pData->Read( (sal_uInt8*)pBuf + nDone, nBytes );
1023             nDone = nDone + nRes;
1024             nPos += nRes;
1025             n -= nRes;
1026             nOffset = nOffset + nRes;
1027             // read problem?
1028             if( nRes != nBytes )
1029                 break;
1030         }
1031         // Switch to next page if necessary
1032         if( nOffset >= nPageSize && !Pos2Page( nPos ) )
1033             break;
1034     }
1035     return nDone;
1036 }
1037 
Write(const void * pBuf,sal_Int32 n)1038 sal_Int32 StgSmallStrm::Write( const void* pBuf, sal_Int32 n )
1039 {
1040     // you can safely assume that reads are not huge, since the
1041     // small stream is likely to be < 64 KBytes.
1042     short nDone = 0;
1043     if( ( nPos + n ) > nSize )
1044     {
1045         sal_Int32 nOld = nPos;
1046         if( !SetSize( nPos + n ) )
1047             return sal_False;
1048         Pos2Page( nOld );
1049     }
1050     while( n )
1051     {
1052         short nBytes = nPageSize - nOffset;
1053         if( (sal_Int32) nBytes > n )
1054             nBytes = (short) n;
1055         if( nBytes )
1056         {
1057             // all writing goes thru the stream
1058             sal_Int32 nDataPos = nPage * nPageSize + nOffset;
1059             if ( !pData
1060               || ( pData->GetSize() < ( nDataPos + nBytes )
1061                 && !pData->SetSize( nDataPos + nBytes ) ) )
1062                 break;
1063             if( !pData->Pos2Page( nDataPos ) )
1064                 break;
1065             short nRes = (short) pData->Write( (sal_uInt8*)pBuf + nDone, nBytes );
1066             nDone = nDone + nRes;
1067             nPos += nRes;
1068             n -= nRes;
1069             nOffset = nOffset + nRes;
1070             // write problem?
1071             if( nRes != nBytes )
1072                 break;
1073         }
1074         // Switch to next page if necessary
1075         if( nOffset >= nPageSize && !Pos2Page( nPos ) )
1076             break;
1077     }
1078     return nDone;
1079 }
1080 
1081 /////////////////////////// class StgTmpStrm /////////////////////////////
1082 
1083 // The temporary stream uses a memory stream if < 32K, otherwise a
1084 // temporary file.
1085 
1086 #define THRESHOLD 32768L
1087 
StgTmpStrm(sal_uLong nInitSize)1088 StgTmpStrm::StgTmpStrm( sal_uLong nInitSize )
1089           : SvMemoryStream( nInitSize > THRESHOLD
1090                             ? 16
1091                             : ( nInitSize ? nInitSize : 16 ), 4096 )
1092 {
1093     pStrm = NULL;
1094     // this calls FlushData, so all members should be set by this time
1095     SetBufferSize( 0 );
1096     if( nInitSize > THRESHOLD )
1097         SetSize( nInitSize );
1098 }
1099 
Copy(StgTmpStrm & rSrc)1100 sal_Bool StgTmpStrm::Copy( StgTmpStrm& rSrc )
1101 {
1102     sal_uLong n    = rSrc.GetSize();
1103     sal_uLong nCur = rSrc.Tell();
1104     SetSize( n );
1105     if( GetError() == SVSTREAM_OK )
1106     {
1107         sal_uInt8* p = new sal_uInt8[ 4096 ];
1108         rSrc.Seek( 0L );
1109         Seek( 0L );
1110         while( n )
1111         {
1112             sal_uLong nn = n;
1113             if( nn > 4096 )
1114                 nn = 4096;
1115             if( rSrc.Read( p, nn ) != nn )
1116                 break;
1117             if( Write( p, nn ) != nn )
1118                 break;
1119             n -= nn;
1120         }
1121         delete [] p;
1122         rSrc.Seek( nCur );
1123         Seek( nCur );
1124         return sal_Bool( n == 0 );
1125     }
1126     else
1127         return sal_False;
1128 }
1129 
~StgTmpStrm()1130 StgTmpStrm::~StgTmpStrm()
1131 {
1132     if( pStrm )
1133     {
1134         pStrm->Close();
1135         osl::File::remove( aName );
1136         delete pStrm;
1137     }
1138 }
1139 
GetSize() const1140 sal_uLong StgTmpStrm::GetSize() const
1141 {
1142     sal_uLong n;
1143     if( pStrm )
1144     {
1145         sal_uLong old = pStrm->Tell();
1146         n = pStrm->Seek( STREAM_SEEK_TO_END );
1147         pStrm->Seek( old );
1148     }
1149     else
1150         n = nEndOfData;
1151     return n;
1152 }
1153 
SetSize(sal_uLong n)1154 void StgTmpStrm::SetSize( sal_uLong n )
1155 {
1156     if( pStrm )
1157         pStrm->SetStreamSize( n );
1158     else
1159     {
1160         if( n > THRESHOLD )
1161         {
1162             aName = TempFile::CreateTempName();
1163             SvFileStream* s = new SvFileStream( aName, STREAM_READWRITE );
1164             sal_uLong nCur = Tell();
1165             sal_uLong i = nEndOfData;
1166             if( i )
1167             {
1168                 sal_uInt8* p = new sal_uInt8[ 4096 ];
1169                 Seek( 0L );
1170                 while( i )
1171                 {
1172                     sal_uLong nb = ( i > 4096 ) ? 4096 : i;
1173                     if( Read( p, nb ) == nb
1174                      && s->Write( p, nb ) == nb )
1175                         i -= nb;
1176                     else
1177                         break;
1178                 }
1179                 delete [] p;
1180             }
1181             if( !i && n > nEndOfData )
1182             {
1183                 // We have to write one byte at the end of the file
1184                 // if the file is bigger than the memstream to see
1185                 // if it fits on disk
1186                 s->Seek( n - 1 );
1187                 s->Write( &i, 1 );
1188                 s->Flush();
1189                 if( s->GetError() != SVSTREAM_OK )
1190                     i = 1;
1191             }
1192             Seek( nCur );
1193             s->Seek( nCur );
1194             if( i )
1195             {
1196                 SetError( s->GetError() );
1197                 delete s;
1198                 return;
1199             }
1200             pStrm = s;
1201             // Shrink the memory to 16 bytes, which seems to be the minimum
1202             ReAllocateMemory( - ( (long) nEndOfData - 16 ) );
1203         }
1204         else
1205         {
1206             if( n > nEndOfData )
1207             {
1208                 sal_uLong nCur = Tell();
1209                 Seek( nEndOfData - 1 );
1210                 *this << (sal_uInt8) 0;
1211                 Seek( nCur );
1212             }
1213             else
1214                 nEndOfData = n;
1215         }
1216     }
1217 }
1218 
GetData(void * pData,sal_uLong n)1219 sal_uLong StgTmpStrm::GetData( void* pData, sal_uLong n )
1220 {
1221     if( pStrm )
1222     {
1223         n = pStrm->Read( pData, n );
1224         SetError( pStrm->GetError() );
1225         return n;
1226     }
1227     else
1228         return SvMemoryStream::GetData( (sal_Char *)pData, n );
1229 }
1230 
PutData(const void * pData,sal_uLong n)1231 sal_uLong StgTmpStrm::PutData( const void* pData, sal_uLong n )
1232 {
1233     sal_uInt32 nCur = Tell();
1234     sal_uInt32 nNew = nCur + n;
1235     if( nNew > THRESHOLD && !pStrm )
1236     {
1237         SetSize( nNew );
1238         if( GetError() != SVSTREAM_OK )
1239             return 0;
1240     }
1241     if( pStrm )
1242     {
1243         nNew = pStrm->Write( pData, n );
1244         SetError( pStrm->GetError() );
1245     }
1246     else
1247         nNew = SvMemoryStream::PutData( (sal_Char*)pData, n );
1248     return nNew;
1249 }
1250 
SeekPos(sal_uLong n)1251 sal_uLong StgTmpStrm::SeekPos( sal_uLong n )
1252 {
1253     if( n == STREAM_SEEK_TO_END )
1254         n = GetSize();
1255     if( n && n > THRESHOLD && !pStrm )
1256     {
1257         SetSize( n );
1258         if( GetError() != SVSTREAM_OK )
1259             return Tell();
1260         else
1261             return n;
1262     }
1263     else if( pStrm )
1264     {
1265         n = pStrm->Seek( n );
1266         SetError( pStrm->GetError() );
1267         return n;
1268     }
1269     else
1270         return SvMemoryStream::SeekPos( n );
1271 }
1272 
FlushData()1273 void StgTmpStrm::FlushData()
1274 {
1275     if( pStrm )
1276     {
1277         pStrm->Flush();
1278         SetError( pStrm->GetError() );
1279     }
1280     else
1281         SvMemoryStream::FlushData();
1282 }
1283 
1284