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