VK_0-9 and VK_A-Z are not defined in the Windows headers, removed them
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 = (LPBIGBLOCKFILE)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     char buf[10];
426
427     /*
428      * close file-mapping object, must be done before call to SetEndFile
429      */
430     if( This->hfilemap )
431       CloseHandle(This->hfilemap);
432     This->hfilemap = 0;
433
434     /*
435      * BEGIN HACK
436      * This fixes a bug when saving through smbfs.
437      * smbmount a Windows shared directory, save a structured storage file
438      * to that dir: crash.
439      *
440      * The problem is that the SetFilePointer-SetEndOfFile combo below
441      * doesn't always succeed. The file is not grown. It seems like the
442      * operation is cached. By doing the WriteFile, the file is actually
443      * grown on disk.
444      * This hack is only needed when saving to smbfs.
445      */
446     memset(buf, '0', 10);
447     SetFilePointer(This->hfile, newSize.u.LowPart, NULL, FILE_BEGIN);
448     WriteFile(This->hfile, buf, 10, NULL, NULL);
449     /*
450      * END HACK
451      */
452
453     /*
454      * set the new end of file
455      */
456     SetFilePointer(This->hfile, newSize.u.LowPart, NULL, FILE_BEGIN);
457     SetEndOfFile(This->hfile);
458
459     /*
460      * re-create the file mapping object
461      */
462     This->hfilemap = CreateFileMappingA(This->hfile,
463                                         NULL,
464                                         This->flProtect,
465                                         0, 0,
466                                         NULL);
467   }
468   else
469   {
470     GlobalUnlock(This->hbytearray);
471
472     /*
473      * Resize the byte array object.
474      */
475     ILockBytes_SetSize(This->pLkbyt, newSize);
476
477     /*
478      * Re-acquire the handle, it may have changed.
479      */
480     GetHGlobalFromILockBytes(This->pLkbyt, &This->hbytearray);
481     This->pbytearray = GlobalLock(This->hbytearray);
482   }
483
484   This->filesize.u.LowPart = newSize.u.LowPart;
485   This->filesize.u.HighPart = newSize.u.HighPart;
486
487   BIGBLOCKFILE_RemapAllMappedPages(This);
488 }
489
490 /******************************************************************************
491  *      BIGBLOCKFILE_GetSize
492  *
493  * Returns the size of the file.
494  *
495  */
496 ULARGE_INTEGER BIGBLOCKFILE_GetSize(LPBIGBLOCKFILE This)
497 {
498   return This->filesize;
499 }
500
501 /******************************************************************************
502  *      BIGBLOCKFILE_AccessCheck     [PRIVATE]
503  *
504  * block_index is the index within the page.
505  */
506 static BOOL BIGBLOCKFILE_AccessCheck(MappedPage *page, ULONG block_index,
507                                      DWORD desired_access)
508 {
509     assert(block_index < BLOCKS_PER_PAGE);
510
511     if (desired_access == FILE_MAP_READ)
512     {
513         if (BIGBLOCKFILE_TestBit(&page->writable_blocks, block_index))
514             return FALSE;
515
516         BIGBLOCKFILE_SetBit(&page->readable_blocks, block_index);
517     }
518     else
519     {
520         assert(desired_access == FILE_MAP_WRITE);
521
522         if (BIGBLOCKFILE_TestBit(&page->readable_blocks, block_index))
523             return FALSE;
524
525         BIGBLOCKFILE_SetBit(&page->writable_blocks, block_index);
526     }
527
528     return TRUE;
529 }
530
531 /******************************************************************************
532  *      BIGBLOCKFILE_GetBigBlockPointer     [PRIVATE]
533  *
534  * Returns a pointer to the specified block.
535  */
536 static void* BIGBLOCKFILE_GetBigBlockPointer(
537   LPBIGBLOCKFILE This,
538   ULONG          block_index,
539   DWORD          desired_access)
540 {
541     DWORD page_index = block_index / BLOCKS_PER_PAGE;
542     DWORD block_on_page = block_index % BLOCKS_PER_PAGE;
543
544     MappedPage *page = BIGBLOCKFILE_GetMappedView(This, page_index);
545     if (!page || !page->lpBytes) return NULL;
546
547     if (!BIGBLOCKFILE_AccessCheck(page, block_on_page, desired_access))
548     {
549         BIGBLOCKFILE_ReleaseMappedPage(This, page);
550         return NULL;
551     }
552
553     return (LPBYTE)page->lpBytes + (block_on_page * This->blocksize);
554 }
555
556 /******************************************************************************
557  *      BIGBLOCKFILE_GetMappedPageFromPointer     [PRIVATE]
558  *
559  * pBlock is a pointer to a block on a page.
560  * The page has to be on the in-use list. (As oppsed to the victim list.)
561  *
562  * Does not increment the usage count.
563  */
564 static MappedPage *BIGBLOCKFILE_GetPageFromPointer(LPBIGBLOCKFILE This,
565                                                    void *pBlock)
566 {
567     MappedPage *page;
568
569     for (page = This->maplist; page != NULL; page = page->next)
570     {
571         if ((LPBYTE)pBlock >= (LPBYTE)page->lpBytes
572             && (LPBYTE)pBlock <= (LPBYTE)page->lpBytes + PAGE_SIZE)
573             break;
574
575     }
576
577     return page;
578 }
579
580 /******************************************************************************
581  *      BIGBLOCKFILE_FindPageInList      [PRIVATE]
582  *
583  */
584 static MappedPage *BIGBLOCKFILE_FindPageInList(MappedPage *head,
585                                                ULONG page_index)
586 {
587     for (; head != NULL; head = head->next)
588     {
589         if (head->page_index == page_index)
590         {
591             InterlockedIncrement(&head->refcnt);
592             break;
593         }
594     }
595
596     return head;
597
598 }
599
600 static void BIGBLOCKFILE_UnlinkPage(MappedPage *page)
601 {
602     if (page->next) page->next->prev = page->prev;
603     if (page->prev) page->prev->next = page->next;
604 }
605
606 static void BIGBLOCKFILE_LinkHeadPage(MappedPage **head, MappedPage *page)
607 {
608     if (*head) (*head)->prev = page;
609     page->next = *head;
610     page->prev = NULL;
611     *head = page;
612 }
613
614 /******************************************************************************
615  *      BIGBLOCKFILE_GetMappedView      [PRIVATE]
616  *
617  * Gets the page requested if it is already mapped.
618  * If it's not already mapped, this method will map it
619  */
620 static void * BIGBLOCKFILE_GetMappedView(
621   LPBIGBLOCKFILE This,
622   DWORD          page_index)
623 {
624     MappedPage *page;
625
626     page = BIGBLOCKFILE_FindPageInList(This->maplist, page_index);
627     if (!page)
628     {
629         page = BIGBLOCKFILE_FindPageInList(This->victimhead, page_index);
630         if (page)
631         {
632             This->num_victim_pages--;
633
634             BIGBLOCKFILE_Zero(&page->readable_blocks);
635             BIGBLOCKFILE_Zero(&page->writable_blocks);
636         }
637     }
638
639     if (page)
640     {
641         /* If the page is not already at the head of the list, move
642          * it there. (Also moves pages from victim to main list.) */
643         if (This->maplist != page)
644         {
645             if (This->victimhead == page) This->victimhead = page->next;
646             if (This->victimtail == page) This->victimtail = page->prev;
647
648             BIGBLOCKFILE_UnlinkPage(page);
649
650             BIGBLOCKFILE_LinkHeadPage(&This->maplist, page);
651         }
652
653         return page;
654     }
655
656     page = BIGBLOCKFILE_CreatePage(This, page_index);
657     if (!page) return NULL;
658
659     BIGBLOCKFILE_LinkHeadPage(&This->maplist, page);
660
661     return page;
662 }
663
664 static BOOL BIGBLOCKFILE_MapPage(LPBIGBLOCKFILE This, MappedPage *page)
665 {
666     DWORD lowoffset = PAGE_SIZE * page->page_index;
667
668     if (This->fileBased)
669     {
670         DWORD numBytesToMap;
671         DWORD desired_access;
672
673         if( !This->hfilemap )
674             return FALSE;
675
676         if (lowoffset + PAGE_SIZE > This->filesize.u.LowPart)
677             numBytesToMap = This->filesize.u.LowPart - lowoffset;
678         else
679             numBytesToMap = PAGE_SIZE;
680
681         if (This->flProtect == PAGE_READONLY)
682             desired_access = FILE_MAP_READ;
683         else
684             desired_access = FILE_MAP_WRITE;
685
686         page->lpBytes = MapViewOfFile(This->hfilemap, desired_access, 0,
687                                       lowoffset, numBytesToMap);
688     }
689     else
690     {
691         page->lpBytes = (LPBYTE)This->pbytearray + lowoffset;
692     }
693
694     TRACE("mapped page %lu to %p\n", page->page_index, page->lpBytes);
695
696     return page->lpBytes != NULL;
697 }
698
699 static MappedPage *BIGBLOCKFILE_CreatePage(LPBIGBLOCKFILE This,
700                                            ULONG page_index)
701 {
702     MappedPage *page;
703
704     page = HeapAlloc(GetProcessHeap(), 0, sizeof(MappedPage));
705     if (page == NULL)
706       return NULL;
707
708     page->page_index = page_index;
709     page->refcnt = 1;
710
711     page->next = NULL;
712     page->prev = NULL;
713
714     BIGBLOCKFILE_MapPage(This, page);
715
716     BIGBLOCKFILE_Zero(&page->readable_blocks);
717     BIGBLOCKFILE_Zero(&page->writable_blocks);
718
719     return page;
720 }
721
722 static void BIGBLOCKFILE_UnmapPage(LPBIGBLOCKFILE This, MappedPage *page)
723 {
724     TRACE("%ld at %p\n", page->page_index, page->lpBytes);
725     if (page->refcnt > 0)
726         ERR("unmapping inuse page %p\n", page->lpBytes);
727
728     if (This->fileBased && page->lpBytes)
729         UnmapViewOfFile(page->lpBytes);
730
731     page->lpBytes = NULL;
732 }
733
734 static void BIGBLOCKFILE_DeletePage(LPBIGBLOCKFILE This, MappedPage *page)
735 {
736     BIGBLOCKFILE_UnmapPage(This, page);
737
738     HeapFree(GetProcessHeap(), 0, page);
739 }
740
741 /******************************************************************************
742  *      BIGBLOCKFILE_ReleaseMappedPage      [PRIVATE]
743  *
744  * Decrements the reference count of the mapped page.
745  */
746 static void BIGBLOCKFILE_ReleaseMappedPage(
747   LPBIGBLOCKFILE This,
748   MappedPage    *page)
749 {
750     assert(This != NULL);
751     assert(page != NULL);
752
753     /* If the page is no longer refenced, move it to the victim list.
754      * If the victim list is too long, kick somebody off. */
755     if (!InterlockedDecrement(&page->refcnt))
756     {
757         if (This->maplist == page) This->maplist = page->next;
758
759         BIGBLOCKFILE_UnlinkPage(page);
760
761         if (MAX_VICTIM_PAGES > 0)
762         {
763             if (This->num_victim_pages >= MAX_VICTIM_PAGES)
764             {
765                 MappedPage *victim = This->victimtail;
766                 if (victim)
767                 {
768                     This->victimtail = victim->prev;
769                     if (This->victimhead == victim)
770                         This->victimhead = victim->next;
771
772                     BIGBLOCKFILE_UnlinkPage(victim);
773                     BIGBLOCKFILE_DeletePage(This, victim);
774                 }
775             }
776             else This->num_victim_pages++;
777
778             BIGBLOCKFILE_LinkHeadPage(&This->victimhead, page);
779             if (This->victimtail == NULL) This->victimtail = page;
780         }
781         else
782             BIGBLOCKFILE_DeletePage(This, page);
783     }
784 }
785
786 static void BIGBLOCKFILE_DeleteList(LPBIGBLOCKFILE This, MappedPage *list)
787 {
788     while (list != NULL)
789     {
790         MappedPage *next = list->next;
791
792         BIGBLOCKFILE_DeletePage(This, list);
793
794         list = next;
795     }
796 }
797
798 /******************************************************************************
799  *      BIGBLOCKFILE_FreeAllMappedPages     [PRIVATE]
800  *
801  * Unmap all currently mapped pages.
802  * Empty mapped pages list.
803  */
804 static void BIGBLOCKFILE_FreeAllMappedPages(
805   LPBIGBLOCKFILE This)
806 {
807     BIGBLOCKFILE_DeleteList(This, This->maplist);
808     BIGBLOCKFILE_DeleteList(This, This->victimhead);
809
810     This->maplist = NULL;
811     This->victimhead = NULL;
812     This->victimtail = NULL;
813     This->num_victim_pages = 0;
814 }
815
816 static void BIGBLOCKFILE_UnmapList(LPBIGBLOCKFILE This, MappedPage *list)
817 {
818     for (; list != NULL; list = list->next)
819     {
820         BIGBLOCKFILE_UnmapPage(This, list);
821     }
822 }
823
824 static void BIGBLOCKFILE_UnmapAllMappedPages(LPBIGBLOCKFILE This)
825 {
826     BIGBLOCKFILE_UnmapList(This, This->maplist);
827     BIGBLOCKFILE_UnmapList(This, This->victimhead);
828 }
829
830 static void BIGBLOCKFILE_RemapList(LPBIGBLOCKFILE This, MappedPage *list)
831 {
832     while (list != NULL)
833     {
834         MappedPage *next = list->next;
835
836         if (list->page_index * PAGE_SIZE > This->filesize.u.LowPart)
837         {
838             TRACE("discarding %lu\n", list->page_index);
839
840             /* page is entirely outside of the file, delete it */
841             BIGBLOCKFILE_UnlinkPage(list);
842             BIGBLOCKFILE_DeletePage(This, list);
843         }
844         else
845         {
846             /* otherwise, remap it */
847             BIGBLOCKFILE_MapPage(This, list);
848         }
849
850         list = next;
851     }
852 }
853
854 static void BIGBLOCKFILE_RemapAllMappedPages(LPBIGBLOCKFILE This)
855 {
856     BIGBLOCKFILE_RemapList(This, This->maplist);
857     BIGBLOCKFILE_RemapList(This, This->victimhead);
858 }
859
860 /****************************************************************************
861  *      BIGBLOCKFILE_GetProtectMode
862  *
863  * This function will return a protection mode flag for a file-mapping object
864  * from the open flags of a file.
865  */
866 static DWORD BIGBLOCKFILE_GetProtectMode(DWORD openFlags)
867 {
868     if (openFlags & (STGM_WRITE | STGM_READWRITE))
869         return PAGE_READWRITE;
870     else
871         return PAGE_READONLY;
872 }