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