xref: /trunk/main/sot/source/sdstor/stgstrms.cxx (revision 92160db5)
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