Use DrawFrameControl instead of bitmaps in certain cases.
[wine] / dlls / ole32 / stg_bigblockfile.c
1 /******************************************************************************
2  *
3  * BigBlockFile
4  *
5  * This is the implementation of a file that consists of blocks of 
6  * a predetermined size.
7  * This class is used in the Compound File implementation of the 
8  * IStorage and IStream interfaces. It provides the functionality 
9  * to read and write any blocks in the file as well as setting and 
10  * obtaining the size of the file.
11  * The blocks are indexed sequentially from the start of the file
12  * starting with -1.
13  * 
14  * TODO:
15  * - Support for a transacted mode
16  *
17  * Copyright 1999 Thuy Nguyen
18  *
19  */
20
21 #include <assert.h>
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <limits.h>
26
27 #include "winbase.h"
28 #include "winerror.h"
29 #include "wine/obj_base.h"
30 #include "wine/obj_storage.h"
31 #include "ole2.h"
32
33 #include "storage32.h"
34
35 #include "debugtools.h"
36
37 DEFAULT_DEBUG_CHANNEL(storage);
38
39 /***********************************************************
40  * Data structures used internally by the BigBlockFile
41  * class.
42  */
43
44 /* We map in PAGE_SIZE-sized chunks. Must be a multiple of 4096. */
45 #define PAGE_SIZE       131072
46
47 #define BLOCKS_PER_PAGE (PAGE_SIZE / BIG_BLOCK_SIZE)
48
49 /* We keep a list of recently-discarded pages. This controls the
50  * size of that list. */
51 #define MAX_VICTIM_PAGES 16
52
53 /* This structure provides one bit for each block in a page.
54  * Use BIGBLOCKFILE_{Test,Set,Clear}Bit to manipulate it. */
55 typedef struct
56 {
57     unsigned int bits[BLOCKS_PER_PAGE / (CHAR_BIT * sizeof(unsigned int))];
58 } BlockBits;
59
60 /***
61  * This structure identifies the paged that are mapped
62  * from the file and their position in memory. It is 
63  * also used to hold a reference count to those pages.
64  *
65  * page_index identifies which PAGE_SIZE chunk from the
66  * file this mapping represents. (The mappings are always
67  * PAGE_SIZE-aligned.)
68  */
69 struct MappedPage
70 {
71     MappedPage *next;
72     MappedPage *prev;
73
74     DWORD  page_index;
75     LPVOID lpBytes;
76     LONG   refcnt;
77
78     BlockBits readable_blocks;
79     BlockBits writable_blocks;
80 };
81
82 /***********************************************************
83  * Prototypes for private methods
84  */
85 static void*     BIGBLOCKFILE_GetMappedView(LPBIGBLOCKFILE This,
86                                             DWORD          page_index);
87 static void      BIGBLOCKFILE_ReleaseMappedPage(LPBIGBLOCKFILE This,
88                                                 MappedPage *page);
89 static void      BIGBLOCKFILE_FreeAllMappedPages(LPBIGBLOCKFILE This);
90 static void      BIGBLOCKFILE_UnmapAllMappedPages(LPBIGBLOCKFILE This);
91 static void      BIGBLOCKFILE_RemapAllMappedPages(LPBIGBLOCKFILE This);
92 static void*     BIGBLOCKFILE_GetBigBlockPointer(LPBIGBLOCKFILE This,
93                                                  ULONG          index,
94                                                  DWORD          desired_access);
95 static MappedPage* BIGBLOCKFILE_GetPageFromPointer(LPBIGBLOCKFILE This, 
96                                                    void*         pBlock);
97 static MappedPage* BIGBLOCKFILE_CreatePage(LPBIGBLOCKFILE This,
98                                            ULONG page_index);
99 static DWORD     BIGBLOCKFILE_GetProtectMode(DWORD openFlags);
100 static BOOL      BIGBLOCKFILE_FileInit(LPBIGBLOCKFILE This, HANDLE hFile);
101 static BOOL      BIGBLOCKFILE_MemInit(LPBIGBLOCKFILE This, ILockBytes* plkbyt);
102
103 /* Note that this evaluates a and b multiple times, so don't
104  * pass expressions with side effects. */
105 #define ROUND_UP(a, b) ((((a) + (b) - 1)/(b))*(b))
106
107 /***********************************************************
108  * Blockbits functions.
109  */
110 static inline BOOL BIGBLOCKFILE_TestBit(const BlockBits *bb,
111                                         unsigned int index)
112 {
113     unsigned int array_index = index / (CHAR_BIT * sizeof(unsigned int));
114     unsigned int bit_index = index % (CHAR_BIT * sizeof(unsigned int));
115
116     return bb->bits[array_index] & (1 << bit_index);
117 }
118
119 static inline void BIGBLOCKFILE_SetBit(BlockBits *bb, unsigned int index)
120 {
121     unsigned int array_index = index / (CHAR_BIT * sizeof(unsigned int));
122     unsigned int bit_index = index % (CHAR_BIT * sizeof(unsigned int));
123
124     bb->bits[array_index] |= (1 << bit_index);
125 }
126
127 static inline void BIGBLOCKFILE_ClearBit(BlockBits *bb, unsigned int index)
128 {
129     unsigned int array_index = index / (CHAR_BIT * sizeof(unsigned int));
130     unsigned int bit_index = index % (CHAR_BIT * sizeof(unsigned int));
131
132     bb->bits[array_index] &= ~(1 << bit_index);
133 }
134
135 static inline void BIGBLOCKFILE_Zero(BlockBits *bb)
136 {
137     memset(bb->bits, 0, sizeof(bb->bits));
138 }
139
140 /******************************************************************************
141  *      BIGBLOCKFILE_Construct
142  *
143  * Construct a big block file. Create the file mapping object. 
144  * Create the read only mapped pages list, the writable mapped page list
145  * and the blocks in use list.
146  */
147 BigBlockFile * BIGBLOCKFILE_Construct(
148   HANDLE   hFile,
149   ILockBytes* pLkByt,
150   DWORD    openFlags,
151   ULONG    blocksize,
152   BOOL     fileBased)
153 {
154   LPBIGBLOCKFILE This;
155
156   This = (LPBIGBLOCKFILE)HeapAlloc(GetProcessHeap(), 0, sizeof(BigBlockFile));
157
158   if (This == NULL)
159     return NULL;
160
161   This->fileBased = fileBased;
162
163   This->flProtect = BIGBLOCKFILE_GetProtectMode(openFlags);
164
165   This->blocksize = blocksize;
166
167   This->maplist = NULL;
168   This->victimhead = NULL;
169   This->victimtail = NULL;
170   This->num_victim_pages = 0;
171
172   if (This->fileBased)
173   {
174     if (!BIGBLOCKFILE_FileInit(This, hFile))
175     {
176       HeapFree(GetProcessHeap(), 0, This);
177       return NULL;
178     }
179   }
180   else
181   {
182     if (!BIGBLOCKFILE_MemInit(This, pLkByt))
183     {
184       HeapFree(GetProcessHeap(), 0, This);
185       return NULL;
186     }
187   }
188
189   return This;
190 }
191
192 /******************************************************************************
193  *      BIGBLOCKFILE_FileInit
194  *
195  * Initialize a big block object supported by a file.
196  */
197 static BOOL BIGBLOCKFILE_FileInit(LPBIGBLOCKFILE This, HANDLE hFile)
198 {
199   This->pLkbyt     = NULL;
200   This->hbytearray = 0;
201   This->pbytearray = NULL;
202
203   This->hfile = hFile;
204
205   if (This->hfile == INVALID_HANDLE_VALUE)
206     return FALSE;
207
208   /* create the file mapping object
209    */
210   This->hfilemap = CreateFileMappingA(This->hfile,
211                                       NULL,
212                                       This->flProtect,
213                                       0, 0,
214                                       NULL);
215
216   if (!This->hfilemap)
217   {
218     CloseHandle(This->hfile);
219     return FALSE;
220   }
221
222   This->filesize.s.LowPart = GetFileSize(This->hfile,
223                                          &This->filesize.s.HighPart);
224
225   This->maplist = NULL;
226
227   TRACE("file len %lu\n", This->filesize.s.LowPart);
228
229   return TRUE;
230 }
231
232 /******************************************************************************
233  *      BIGBLOCKFILE_MemInit
234  *
235  * Initialize a big block object supported by an ILockBytes on HGLOABL.
236  */
237 static BOOL BIGBLOCKFILE_MemInit(LPBIGBLOCKFILE This, ILockBytes* plkbyt)
238 {
239   This->hfile       = 0;
240   This->hfilemap    = 0;
241
242   /*
243    * Retrieve the handle to the byte array from the LockByte object.
244    */
245   if (GetHGlobalFromILockBytes(plkbyt, &(This->hbytearray)) != S_OK)
246   {
247     FIXME("May not be an ILockBytes on HGLOBAL\n");
248     return FALSE;
249   }
250
251   This->pLkbyt = plkbyt;
252
253   /*
254    * Increment the reference count of the ILockByte object since
255    * we're keeping a reference to it.
256    */
257   ILockBytes_AddRef(This->pLkbyt);
258
259   This->filesize.s.LowPart = GlobalSize(This->hbytearray);
260   This->filesize.s.HighPart = 0;
261
262   This->pbytearray = GlobalLock(This->hbytearray);
263
264   TRACE("mem on %p len %lu\n", This->pbytearray, This->filesize.s.LowPart);
265
266   return TRUE;
267 }
268
269 /******************************************************************************
270  *      BIGBLOCKFILE_Destructor
271  *
272  * Destructor. Clean up, free memory.
273  */
274 void BIGBLOCKFILE_Destructor(
275   LPBIGBLOCKFILE This)
276 {
277   BIGBLOCKFILE_FreeAllMappedPages(This);
278
279   if (This->fileBased)
280   {
281     CloseHandle(This->hfilemap);
282     CloseHandle(This->hfile);
283   }
284   else
285   {
286     GlobalUnlock(This->hbytearray);
287     ILockBytes_Release(This->pLkbyt);
288   }
289
290   /* destroy this
291    */
292   HeapFree(GetProcessHeap(), 0, This);
293 }
294
295 /******************************************************************************
296  *      BIGBLOCKFILE_GetROBigBlock
297  *
298  * Returns the specified block in read only mode.
299  * Will return NULL if the block doesn't exists.
300  */
301 void* BIGBLOCKFILE_GetROBigBlock(
302   LPBIGBLOCKFILE This,
303   ULONG          index)
304 {
305   /*
306    * block index starts at -1
307    * translate to zero based index
308    */
309   if (index == 0xffffffff)
310     index = 0;
311   else
312     index++;
313
314   /*
315    * validate the block index
316    * 
317    */
318   if (This->blocksize * (index + 1)
319       > ROUND_UP(This->filesize.s.LowPart, This->blocksize))
320   {
321     TRACE("out of range %lu vs %lu\n", This->blocksize * (index + 1),
322           This->filesize.s.LowPart);
323     return NULL;
324   }
325
326   return BIGBLOCKFILE_GetBigBlockPointer(This, index, FILE_MAP_READ);
327 }
328
329 /******************************************************************************
330  *      BIGBLOCKFILE_GetBigBlock
331  *
332  * Returns the specified block.
333  * Will grow the file if necessary.
334  */
335 void* BIGBLOCKFILE_GetBigBlock(LPBIGBLOCKFILE This, ULONG index)
336 {
337   /*
338    * block index starts at -1
339    * translate to zero based index
340    */
341   if (index == 0xffffffff)
342     index = 0;
343   else
344     index++;
345
346   /*
347    * make sure that the block physically exists
348    */
349   if ((This->blocksize * (index + 1)) > This->filesize.s.LowPart)
350   {
351     ULARGE_INTEGER newSize;
352
353     newSize.s.HighPart = 0;
354     newSize.s.LowPart = This->blocksize * (index + 1);
355
356     BIGBLOCKFILE_SetSize(This, newSize);
357   }
358
359   return BIGBLOCKFILE_GetBigBlockPointer(This, index, FILE_MAP_WRITE);
360 }
361
362 /******************************************************************************
363  *      BIGBLOCKFILE_ReleaseBigBlock
364  *
365  * Releases the specified block.
366  */
367 void BIGBLOCKFILE_ReleaseBigBlock(LPBIGBLOCKFILE This, void *pBlock)
368 {
369     MappedPage *page;
370
371     if (pBlock == NULL)
372         return;
373
374     page = BIGBLOCKFILE_GetPageFromPointer(This, pBlock);
375
376     if (page == NULL)
377         return;
378
379     BIGBLOCKFILE_ReleaseMappedPage(This, page);
380 }
381
382 /******************************************************************************
383  *      BIGBLOCKFILE_SetSize
384  *
385  * Sets the size of the file.
386  * 
387  */
388 void BIGBLOCKFILE_SetSize(LPBIGBLOCKFILE This, ULARGE_INTEGER newSize)
389 {
390   if (This->filesize.s.LowPart == newSize.s.LowPart)
391     return;
392
393   TRACE("from %lu to %lu\n", This->filesize.s.LowPart, newSize.s.LowPart);
394   /*
395    * unmap all views, must be done before call to SetEndFile
396    */
397   BIGBLOCKFILE_UnmapAllMappedPages(This);
398   
399   if (This->fileBased)
400   {
401     char buf[10];
402
403     /*
404      * close file-mapping object, must be done before call to SetEndFile
405      */
406     CloseHandle(This->hfilemap);
407     This->hfilemap = 0;
408
409     /*
410      * BEGIN HACK
411      * This fixes a bug when saving through smbfs.
412      * smbmount a Windows shared directory, save a structured storage file
413      * to that dir: crash.
414      *
415      * The problem is that the SetFilePointer-SetEndOfFile combo below
416      * doesn't always succeed. The file is not grown. It seems like the 
417      * operation is cached. By doing the WriteFile, the file is actually
418      * grown on disk.
419      * This hack is only needed when saving to smbfs.
420      */
421     memset(buf, '0', 10);
422     SetFilePointer(This->hfile, newSize.s.LowPart, NULL, FILE_BEGIN);
423     WriteFile(This->hfile, buf, 10, NULL, NULL);
424     /*
425      * END HACK 
426      */
427
428     /*
429      * set the new end of file
430      */
431     SetFilePointer(This->hfile, newSize.s.LowPart, NULL, FILE_BEGIN);
432     SetEndOfFile(This->hfile);
433   
434     /*
435      * re-create the file mapping object
436      */
437     This->hfilemap = CreateFileMappingA(This->hfile,
438                                         NULL,
439                                         This->flProtect,
440                                         0, 0, 
441                                         NULL);
442   }
443   else
444   {
445     GlobalUnlock(This->hbytearray);
446
447     /*
448      * Resize the byte array object.
449      */
450     ILockBytes_SetSize(This->pLkbyt, newSize);
451
452     /*
453      * Re-acquire the handle, it may have changed.
454      */
455     GetHGlobalFromILockBytes(This->pLkbyt, &This->hbytearray);
456     This->pbytearray = GlobalLock(This->hbytearray);
457   }
458
459   This->filesize.s.LowPart = newSize.s.LowPart;
460   This->filesize.s.HighPart = newSize.s.HighPart;
461
462   BIGBLOCKFILE_RemapAllMappedPages(This);
463 }
464
465 /******************************************************************************
466  *      BIGBLOCKFILE_GetSize
467  *
468  * Returns the size of the file.
469  * 
470  */
471 ULARGE_INTEGER BIGBLOCKFILE_GetSize(LPBIGBLOCKFILE This)
472 {
473   return This->filesize;
474 }
475
476 /******************************************************************************
477  *      BIGBLOCKFILE_AccessCheck     [PRIVATE]
478  *
479  * block_index is the index within the page.
480  */
481 static BOOL BIGBLOCKFILE_AccessCheck(MappedPage *page, ULONG block_index,
482                                      DWORD desired_access)
483 {
484     assert(block_index < BLOCKS_PER_PAGE);
485
486     if (desired_access == FILE_MAP_READ)
487     {
488         if (BIGBLOCKFILE_TestBit(&page->writable_blocks, block_index))
489             return FALSE;
490
491         BIGBLOCKFILE_SetBit(&page->readable_blocks, block_index);
492     }
493     else
494     {
495         assert(desired_access == FILE_MAP_WRITE);
496
497         if (BIGBLOCKFILE_TestBit(&page->readable_blocks, block_index))
498             return FALSE;
499
500         BIGBLOCKFILE_SetBit(&page->writable_blocks, block_index);
501     }
502
503     return TRUE;
504 }
505
506 /******************************************************************************
507  *      BIGBLOCKFILE_GetBigBlockPointer     [PRIVATE]
508  *
509  * Returns a pointer to the specified block.
510  */
511 static void* BIGBLOCKFILE_GetBigBlockPointer(
512   LPBIGBLOCKFILE This, 
513   ULONG          block_index, 
514   DWORD          desired_access)
515 {
516     DWORD page_index = block_index / BLOCKS_PER_PAGE;
517     DWORD block_on_page = block_index % BLOCKS_PER_PAGE;
518
519     MappedPage *page = BIGBLOCKFILE_GetMappedView(This, page_index);
520     if (!page || !page->lpBytes) return NULL;
521
522     if (!BIGBLOCKFILE_AccessCheck(page, block_on_page, desired_access))
523     {
524         BIGBLOCKFILE_ReleaseMappedPage(This, page);
525         return NULL;
526     }
527
528     return (LPBYTE)page->lpBytes + (block_on_page * This->blocksize);
529 }
530
531 /******************************************************************************
532  *      BIGBLOCKFILE_GetMappedPageFromPointer     [PRIVATE]
533  *
534  * pBlock is a pointer to a block on a page.
535  * The page has to be on the in-use list. (As oppsed to the victim list.)
536  *
537  * Does not increment the usage count.
538  */
539 static MappedPage *BIGBLOCKFILE_GetPageFromPointer(LPBIGBLOCKFILE This,
540                                                    void *pBlock)
541 {
542     MappedPage *page;
543
544     for (page = This->maplist; page != NULL; page = page->next)
545     {
546         if ((LPBYTE)pBlock >= (LPBYTE)page->lpBytes
547             && (LPBYTE)pBlock <= (LPBYTE)page->lpBytes + PAGE_SIZE)
548             break;
549
550     }
551
552     return page;
553 }
554
555 /******************************************************************************
556  *      BIGBLOCKFILE_FindPageInList      [PRIVATE]
557  *
558  */
559 static MappedPage *BIGBLOCKFILE_FindPageInList(MappedPage *head,
560                                                ULONG page_index)
561 {
562     for (; head != NULL; head = head->next)
563     {
564         if (head->page_index == page_index)
565         {
566             InterlockedIncrement(&head->refcnt);
567             break;
568         }
569     }
570
571     return head;
572
573 }
574
575 static void BIGBLOCKFILE_UnlinkPage(MappedPage *page)
576 {
577     if (page->next) page->next->prev = page->prev;
578     if (page->prev) page->prev->next = page->next;
579 }
580
581 static void BIGBLOCKFILE_LinkHeadPage(MappedPage **head, MappedPage *page)
582 {
583     if (*head) (*head)->prev = page;
584     page->next = *head;
585     page->prev = NULL;
586     *head = page;
587 }
588
589 /******************************************************************************
590  *      BIGBLOCKFILE_GetMappedView      [PRIVATE]
591  *
592  * Gets the page requested if it is already mapped.
593  * If it's not already mapped, this method will map it
594  */
595 static void * BIGBLOCKFILE_GetMappedView(
596   LPBIGBLOCKFILE This,
597   DWORD          page_index)
598 {
599     MappedPage *page;
600
601     page = BIGBLOCKFILE_FindPageInList(This->maplist, page_index);
602     if (!page)
603     {
604         page = BIGBLOCKFILE_FindPageInList(This->victimhead, page_index);
605         if (page)
606         {
607             This->num_victim_pages--;
608
609             BIGBLOCKFILE_Zero(&page->readable_blocks);
610             BIGBLOCKFILE_Zero(&page->writable_blocks);
611         }
612     }
613
614     if (page)
615     {
616         /* If the page is not already at the head of the list, move
617          * it there. (Also moves pages from victim to main list.) */
618         if (This->maplist != page)
619         {
620             if (This->victimhead == page) This->victimhead = page->next;
621             if (This->victimtail == page) This->victimtail = page->prev;
622
623             BIGBLOCKFILE_UnlinkPage(page);
624
625             BIGBLOCKFILE_LinkHeadPage(&This->maplist, page);
626         }
627
628         return page;
629     }
630
631     page = BIGBLOCKFILE_CreatePage(This, page_index);
632     if (!page) return NULL;
633
634     BIGBLOCKFILE_LinkHeadPage(&This->maplist, page);
635
636     return page;
637 }
638
639 static BOOL BIGBLOCKFILE_MapPage(LPBIGBLOCKFILE This, MappedPage *page)
640 {
641     DWORD lowoffset = PAGE_SIZE * page->page_index;
642
643     if (This->fileBased)
644     {
645         DWORD numBytesToMap;
646         DWORD desired_access;
647
648         if (lowoffset + PAGE_SIZE > This->filesize.s.LowPart)
649             numBytesToMap = This->filesize.s.LowPart - lowoffset;
650         else
651             numBytesToMap = PAGE_SIZE;
652
653         if (This->flProtect == PAGE_READONLY)
654             desired_access = FILE_MAP_READ;
655         else
656             desired_access = FILE_MAP_WRITE;
657
658         page->lpBytes = MapViewOfFile(This->hfilemap, desired_access, 0,
659                                       lowoffset, numBytesToMap);
660     }
661     else
662     {
663         page->lpBytes = (LPBYTE)This->pbytearray + lowoffset;
664     }
665
666     TRACE("mapped page %lu to %p\n", page->page_index, page->lpBytes);
667
668     return page->lpBytes != NULL;
669 }
670
671 static MappedPage *BIGBLOCKFILE_CreatePage(LPBIGBLOCKFILE This,
672                                            ULONG page_index)
673 {
674     MappedPage *page;
675
676     page = HeapAlloc(GetProcessHeap(), 0, sizeof(MappedPage));
677     if (page == NULL)
678       return NULL;
679
680     page->page_index = page_index;
681     page->refcnt = 1;
682
683     page->next = NULL;
684     page->prev = NULL;
685
686     BIGBLOCKFILE_MapPage(This, page);
687
688     BIGBLOCKFILE_Zero(&page->readable_blocks);
689     BIGBLOCKFILE_Zero(&page->writable_blocks);
690
691     return page;
692 }
693
694 static void BIGBLOCKFILE_UnmapPage(LPBIGBLOCKFILE This, MappedPage *page)
695 {
696     TRACE("%ld at %p\n", page->page_index, page->lpBytes);
697     if (page->refcnt > 0)
698         ERR("unmapping inuse page %p\n", page->lpBytes);
699
700     if (This->fileBased && page->lpBytes)
701         UnmapViewOfFile(page->lpBytes);
702
703     page->lpBytes = NULL;
704 }
705
706 static void BIGBLOCKFILE_DeletePage(LPBIGBLOCKFILE This, MappedPage *page)
707 {
708     BIGBLOCKFILE_UnmapPage(This, page);
709
710     HeapFree(GetProcessHeap(), 0, page);
711 }
712
713 /******************************************************************************
714  *      BIGBLOCKFILE_ReleaseMappedPage      [PRIVATE]
715  *
716  * Decrements the reference count of the mapped page.
717  */
718 static void BIGBLOCKFILE_ReleaseMappedPage(
719   LPBIGBLOCKFILE This,
720   MappedPage    *page)
721 {
722     assert(This != NULL);
723     assert(page != NULL);
724
725     /* If the page is no longer refenced, move it to the victim list.
726      * If the victim list is too long, kick somebody off. */
727     if (!InterlockedDecrement(&page->refcnt))
728     {
729         if (This->maplist == page) This->maplist = page->next;
730
731         BIGBLOCKFILE_UnlinkPage(page);
732
733         if (MAX_VICTIM_PAGES > 0)
734         {
735             if (This->num_victim_pages >= MAX_VICTIM_PAGES)
736             {
737                 MappedPage *victim = This->victimtail;
738                 if (victim)
739                 {
740                     This->victimtail = victim->prev;
741                     if (This->victimhead == victim)
742                         This->victimhead = victim->next;
743
744                     BIGBLOCKFILE_UnlinkPage(victim);
745                     BIGBLOCKFILE_DeletePage(This, victim);
746                 }
747             }
748             else This->num_victim_pages++;
749
750             BIGBLOCKFILE_LinkHeadPage(&This->victimhead, page);
751             if (This->victimtail == NULL) This->victimtail = page;
752         }
753         else
754             BIGBLOCKFILE_DeletePage(This, page);
755     }
756 }
757
758 static void BIGBLOCKFILE_DeleteList(LPBIGBLOCKFILE This, MappedPage *list)
759 {
760     while (list != NULL)
761     {
762         MappedPage *next = list->next;
763
764         BIGBLOCKFILE_DeletePage(This, list);
765
766         list = next;
767     }
768 }
769
770 /******************************************************************************
771  *      BIGBLOCKFILE_FreeAllMappedPages     [PRIVATE]
772  *
773  * Unmap all currently mapped pages.
774  * Empty mapped pages list.
775  */
776 static void BIGBLOCKFILE_FreeAllMappedPages(
777   LPBIGBLOCKFILE This)
778 {
779     BIGBLOCKFILE_DeleteList(This, This->maplist);
780     BIGBLOCKFILE_DeleteList(This, This->victimhead);
781
782     This->maplist = NULL;
783     This->victimhead = NULL;
784     This->victimtail = NULL;
785     This->num_victim_pages = 0;
786 }
787
788 static void BIGBLOCKFILE_UnmapList(LPBIGBLOCKFILE This, MappedPage *list)
789 {
790     for (; list != NULL; list = list->next)
791     {
792         BIGBLOCKFILE_UnmapPage(This, list);
793     }
794 }
795
796 static void BIGBLOCKFILE_UnmapAllMappedPages(LPBIGBLOCKFILE This)
797 {
798     BIGBLOCKFILE_UnmapList(This, This->maplist);
799     BIGBLOCKFILE_UnmapList(This, This->victimhead);
800 }
801
802 static void BIGBLOCKFILE_RemapList(LPBIGBLOCKFILE This, MappedPage *list)
803 {
804     while (list != NULL)
805     {
806         MappedPage *next = list->next;
807
808         if (list->page_index * PAGE_SIZE > This->filesize.s.LowPart)
809         {
810             TRACE("discarding %lu\n", list->page_index);
811
812             /* page is entirely outside of the file, delete it */
813             BIGBLOCKFILE_UnlinkPage(list);
814             BIGBLOCKFILE_DeletePage(This, list);
815         }
816         else
817         {
818             /* otherwise, remap it */
819             BIGBLOCKFILE_MapPage(This, list);
820         }
821
822         list = next;
823     }
824 }
825
826 static void BIGBLOCKFILE_RemapAllMappedPages(LPBIGBLOCKFILE This)
827 {
828     BIGBLOCKFILE_RemapList(This, This->maplist);
829     BIGBLOCKFILE_RemapList(This, This->victimhead);
830 }
831
832 /****************************************************************************
833  *      BIGBLOCKFILE_GetProtectMode
834  *
835  * This function will return a protection mode flag for a file-mapping object
836  * from the open flags of a file.
837  */
838 static DWORD BIGBLOCKFILE_GetProtectMode(DWORD openFlags)
839 {
840     if (openFlags & (STGM_WRITE | STGM_READWRITE))
841         return PAGE_READWRITE;
842     else
843         return PAGE_READONLY;
844 }