dinput: Implement GetObjectInfo[A|W] in base device class.
[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 %u\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 %u\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 %u vs %u\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_EnsureExists
355  *
356  * Grows the file if necessary to make sure the block is valid.
357  */
358 void BIGBLOCKFILE_EnsureExists(LPBIGBLOCKFILE This, ULONG index)
359 {
360   /*
361    * block index starts at -1
362    * translate to zero based index
363    */
364   if (index == 0xffffffff)
365     index = 0;
366   else
367     index++;
368
369   /*
370    * make sure that the block physically exists
371    */
372   if ((This->blocksize * (index + 1)) > This->filesize.u.LowPart)
373   {
374     ULARGE_INTEGER newSize;
375
376     newSize.u.HighPart = 0;
377     newSize.u.LowPart = This->blocksize * (index + 1);
378
379     BIGBLOCKFILE_SetSize(This, newSize);
380   }
381 }
382
383 /******************************************************************************
384  *      BIGBLOCKFILE_GetBigBlock
385  *
386  * Returns the specified block.
387  * Will grow the file if necessary.
388  */
389 void* BIGBLOCKFILE_GetBigBlock(LPBIGBLOCKFILE This, ULONG index)
390 {
391   /* FIXME: is this necessary? */
392   BIGBLOCKFILE_EnsureExists(This, index);
393
394   /*
395    * block index starts at -1
396    * translate to zero based index
397    */
398   if (index == 0xffffffff)
399     index = 0;
400   else
401     index++;
402
403   return BIGBLOCKFILE_GetBigBlockPointer(This, index, FILE_MAP_WRITE);
404 }
405
406 /******************************************************************************
407  *      BIGBLOCKFILE_ReleaseBigBlock
408  *
409  * Releases the specified block.
410  */
411 void BIGBLOCKFILE_ReleaseBigBlock(LPBIGBLOCKFILE This, void *pBlock)
412 {
413     MappedPage *page;
414
415     if (pBlock == NULL)
416         return;
417
418     page = BIGBLOCKFILE_GetPageFromPointer(This, pBlock);
419
420     if (page == NULL)
421         return;
422
423     BIGBLOCKFILE_ReleaseMappedPage(This, page);
424 }
425
426 /******************************************************************************
427  *      BIGBLOCKFILE_SetSize
428  *
429  * Sets the size of the file.
430  *
431  */
432 void BIGBLOCKFILE_SetSize(LPBIGBLOCKFILE This, ULARGE_INTEGER newSize)
433 {
434   if (This->filesize.u.LowPart == newSize.u.LowPart)
435     return;
436
437   TRACE("from %u to %u\n", This->filesize.u.LowPart, newSize.u.LowPart);
438   /*
439    * unmap all views, must be done before call to SetEndFile
440    */
441   BIGBLOCKFILE_UnmapAllMappedPages(This);
442
443   if (This->fileBased)
444   {
445     LARGE_INTEGER newpos;
446
447     newpos.QuadPart = newSize.QuadPart;
448     if (SetFilePointerEx(This->hfile, newpos, NULL, FILE_BEGIN))
449     {
450         if( This->hfilemap ) CloseHandle(This->hfilemap);
451
452         SetEndOfFile(This->hfile);
453
454         /*
455          * re-create the file mapping object
456          */
457         This->hfilemap = CreateFileMappingA(This->hfile,
458                                             NULL,
459                                             This->flProtect,
460                                             0, 0,
461                                             NULL);
462     }
463   }
464   else
465   {
466     GlobalUnlock(This->hbytearray);
467
468     /*
469      * Resize the byte array object.
470      */
471     ILockBytes_SetSize(This->pLkbyt, newSize);
472
473     /*
474      * Re-acquire the handle, it may have changed.
475      */
476     GetHGlobalFromILockBytes(This->pLkbyt, &This->hbytearray);
477     This->pbytearray = GlobalLock(This->hbytearray);
478   }
479
480   This->filesize.u.LowPart = newSize.u.LowPart;
481   This->filesize.u.HighPart = newSize.u.HighPart;
482
483   BIGBLOCKFILE_RemapAllMappedPages(This);
484 }
485
486 /******************************************************************************
487  *      BIGBLOCKFILE_GetSize
488  *
489  * Returns the size of the file.
490  *
491  */
492 ULARGE_INTEGER BIGBLOCKFILE_GetSize(LPBIGBLOCKFILE This)
493 {
494   return This->filesize;
495 }
496
497 /******************************************************************************
498  *      BIGBLOCKFILE_AccessCheck     [PRIVATE]
499  *
500  * block_index is the index within the page.
501  */
502 static BOOL BIGBLOCKFILE_AccessCheck(MappedPage *page, ULONG block_index,
503                                      DWORD desired_access)
504 {
505     assert(block_index < BLOCKS_PER_PAGE);
506
507     if (desired_access == FILE_MAP_READ)
508     {
509         if (BIGBLOCKFILE_TestBit(&page->writable_blocks, block_index))
510             return FALSE;
511
512         BIGBLOCKFILE_SetBit(&page->readable_blocks, block_index);
513     }
514     else
515     {
516         assert(desired_access == FILE_MAP_WRITE);
517
518         if (BIGBLOCKFILE_TestBit(&page->readable_blocks, block_index))
519             return FALSE;
520
521         BIGBLOCKFILE_SetBit(&page->writable_blocks, block_index);
522     }
523
524     return TRUE;
525 }
526
527 /******************************************************************************
528  *      BIGBLOCKFILE_GetBigBlockPointer     [PRIVATE]
529  *
530  * Returns a pointer to the specified block.
531  */
532 static void* BIGBLOCKFILE_GetBigBlockPointer(
533   LPBIGBLOCKFILE This,
534   ULONG          block_index,
535   DWORD          desired_access)
536 {
537     DWORD page_index = block_index / BLOCKS_PER_PAGE;
538     DWORD block_on_page = block_index % BLOCKS_PER_PAGE;
539
540     MappedPage *page = BIGBLOCKFILE_GetMappedView(This, page_index);
541     if (!page || !page->lpBytes) return NULL;
542
543     if (!BIGBLOCKFILE_AccessCheck(page, block_on_page, desired_access))
544     {
545         BIGBLOCKFILE_ReleaseMappedPage(This, page);
546         return NULL;
547     }
548
549     return (LPBYTE)page->lpBytes + (block_on_page * This->blocksize);
550 }
551
552 /******************************************************************************
553  *      BIGBLOCKFILE_GetMappedPageFromPointer     [PRIVATE]
554  *
555  * pBlock is a pointer to a block on a page.
556  * The page has to be on the in-use list. (As oppsed to the victim list.)
557  *
558  * Does not increment the usage count.
559  */
560 static MappedPage *BIGBLOCKFILE_GetPageFromPointer(LPBIGBLOCKFILE This,
561                                                    void *pBlock)
562 {
563     MappedPage *page;
564
565     for (page = This->maplist; page != NULL; page = page->next)
566     {
567         if ((LPBYTE)pBlock >= (LPBYTE)page->lpBytes
568             && (LPBYTE)pBlock <= (LPBYTE)page->lpBytes + PAGE_SIZE)
569             break;
570
571     }
572
573     return page;
574 }
575
576 /******************************************************************************
577  *      BIGBLOCKFILE_FindPageInList      [PRIVATE]
578  *
579  */
580 static MappedPage *BIGBLOCKFILE_FindPageInList(MappedPage *head,
581                                                ULONG page_index)
582 {
583     for (; head != NULL; head = head->next)
584     {
585         if (head->page_index == page_index)
586         {
587             InterlockedIncrement(&head->refcnt);
588             break;
589         }
590     }
591
592     return head;
593
594 }
595
596 static void BIGBLOCKFILE_UnlinkPage(MappedPage *page)
597 {
598     if (page->next) page->next->prev = page->prev;
599     if (page->prev) page->prev->next = page->next;
600 }
601
602 static void BIGBLOCKFILE_LinkHeadPage(MappedPage **head, MappedPage *page)
603 {
604     if (*head) (*head)->prev = page;
605     page->next = *head;
606     page->prev = NULL;
607     *head = page;
608 }
609
610 /******************************************************************************
611  *      BIGBLOCKFILE_GetMappedView      [PRIVATE]
612  *
613  * Gets the page requested if it is already mapped.
614  * If it's not already mapped, this method will map it
615  */
616 static void * BIGBLOCKFILE_GetMappedView(
617   LPBIGBLOCKFILE This,
618   DWORD          page_index)
619 {
620     MappedPage *page;
621
622     page = BIGBLOCKFILE_FindPageInList(This->maplist, page_index);
623     if (!page)
624     {
625         page = BIGBLOCKFILE_FindPageInList(This->victimhead, page_index);
626         if (page)
627         {
628             This->num_victim_pages--;
629
630             BIGBLOCKFILE_Zero(&page->readable_blocks);
631             BIGBLOCKFILE_Zero(&page->writable_blocks);
632         }
633     }
634
635     if (page)
636     {
637         /* If the page is not already at the head of the list, move
638          * it there. (Also moves pages from victim to main list.) */
639         if (This->maplist != page)
640         {
641             if (This->victimhead == page) This->victimhead = page->next;
642             if (This->victimtail == page) This->victimtail = page->prev;
643
644             BIGBLOCKFILE_UnlinkPage(page);
645
646             BIGBLOCKFILE_LinkHeadPage(&This->maplist, page);
647         }
648
649         return page;
650     }
651
652     page = BIGBLOCKFILE_CreatePage(This, page_index);
653     if (!page) return NULL;
654
655     BIGBLOCKFILE_LinkHeadPage(&This->maplist, page);
656
657     return page;
658 }
659
660 static BOOL BIGBLOCKFILE_MapPage(LPBIGBLOCKFILE This, MappedPage *page)
661 {
662     DWORD lowoffset = PAGE_SIZE * page->page_index;
663
664     if (This->fileBased)
665     {
666         DWORD numBytesToMap;
667         DWORD desired_access;
668
669         if( !This->hfilemap )
670             return FALSE;
671
672         if (lowoffset + PAGE_SIZE > This->filesize.u.LowPart)
673             numBytesToMap = This->filesize.u.LowPart - lowoffset;
674         else
675             numBytesToMap = PAGE_SIZE;
676
677         if (This->flProtect == PAGE_READONLY)
678             desired_access = FILE_MAP_READ;
679         else
680             desired_access = FILE_MAP_WRITE;
681
682         page->lpBytes = MapViewOfFile(This->hfilemap, desired_access, 0,
683                                       lowoffset, numBytesToMap);
684     }
685     else
686     {
687         page->lpBytes = (LPBYTE)This->pbytearray + lowoffset;
688     }
689
690     TRACE("mapped page %u to %p\n", page->page_index, page->lpBytes);
691
692     return page->lpBytes != NULL;
693 }
694
695 static MappedPage *BIGBLOCKFILE_CreatePage(LPBIGBLOCKFILE This,
696                                            ULONG page_index)
697 {
698     MappedPage *page;
699
700     page = HeapAlloc(GetProcessHeap(), 0, sizeof(MappedPage));
701     if (page == NULL)
702       return NULL;
703
704     page->page_index = page_index;
705     page->refcnt = 1;
706
707     page->next = NULL;
708     page->prev = NULL;
709
710     BIGBLOCKFILE_MapPage(This, page);
711
712     BIGBLOCKFILE_Zero(&page->readable_blocks);
713     BIGBLOCKFILE_Zero(&page->writable_blocks);
714
715     return page;
716 }
717
718 static void BIGBLOCKFILE_UnmapPage(LPBIGBLOCKFILE This, MappedPage *page)
719 {
720     TRACE("%d at %p\n", page->page_index, page->lpBytes);
721     if (page->refcnt > 0)
722         ERR("unmapping inuse page %p\n", page->lpBytes);
723
724     if (This->fileBased && page->lpBytes)
725         UnmapViewOfFile(page->lpBytes);
726
727     page->lpBytes = NULL;
728 }
729
730 static void BIGBLOCKFILE_DeletePage(LPBIGBLOCKFILE This, MappedPage *page)
731 {
732     BIGBLOCKFILE_UnmapPage(This, page);
733
734     HeapFree(GetProcessHeap(), 0, page);
735 }
736
737 /******************************************************************************
738  *      BIGBLOCKFILE_ReleaseMappedPage      [PRIVATE]
739  *
740  * Decrements the reference count of the mapped page.
741  */
742 static void BIGBLOCKFILE_ReleaseMappedPage(
743   LPBIGBLOCKFILE This,
744   MappedPage    *page)
745 {
746     assert(This != NULL);
747     assert(page != NULL);
748
749     /* If the page is no longer refenced, move it to the victim list.
750      * If the victim list is too long, kick somebody off. */
751     if (!InterlockedDecrement(&page->refcnt))
752     {
753         if (This->maplist == page) This->maplist = page->next;
754
755         BIGBLOCKFILE_UnlinkPage(page);
756
757         if (MAX_VICTIM_PAGES > 0)
758         {
759             if (This->num_victim_pages >= MAX_VICTIM_PAGES)
760             {
761                 MappedPage *victim = This->victimtail;
762                 if (victim)
763                 {
764                     This->victimtail = victim->prev;
765                     if (This->victimhead == victim)
766                         This->victimhead = victim->next;
767
768                     BIGBLOCKFILE_UnlinkPage(victim);
769                     BIGBLOCKFILE_DeletePage(This, victim);
770                 }
771             }
772             else This->num_victim_pages++;
773
774             BIGBLOCKFILE_LinkHeadPage(&This->victimhead, page);
775             if (This->victimtail == NULL) This->victimtail = page;
776         }
777         else
778             BIGBLOCKFILE_DeletePage(This, page);
779     }
780 }
781
782 static void BIGBLOCKFILE_DeleteList(LPBIGBLOCKFILE This, MappedPage *list)
783 {
784     while (list != NULL)
785     {
786         MappedPage *next = list->next;
787
788         BIGBLOCKFILE_DeletePage(This, list);
789
790         list = next;
791     }
792 }
793
794 /******************************************************************************
795  *      BIGBLOCKFILE_FreeAllMappedPages     [PRIVATE]
796  *
797  * Unmap all currently mapped pages.
798  * Empty mapped pages list.
799  */
800 static void BIGBLOCKFILE_FreeAllMappedPages(
801   LPBIGBLOCKFILE This)
802 {
803     BIGBLOCKFILE_DeleteList(This, This->maplist);
804     BIGBLOCKFILE_DeleteList(This, This->victimhead);
805
806     This->maplist = NULL;
807     This->victimhead = NULL;
808     This->victimtail = NULL;
809     This->num_victim_pages = 0;
810 }
811
812 static void BIGBLOCKFILE_UnmapList(LPBIGBLOCKFILE This, MappedPage *list)
813 {
814     for (; list != NULL; list = list->next)
815     {
816         BIGBLOCKFILE_UnmapPage(This, list);
817     }
818 }
819
820 static void BIGBLOCKFILE_UnmapAllMappedPages(LPBIGBLOCKFILE This)
821 {
822     BIGBLOCKFILE_UnmapList(This, This->maplist);
823     BIGBLOCKFILE_UnmapList(This, This->victimhead);
824 }
825
826 static void BIGBLOCKFILE_RemapList(LPBIGBLOCKFILE This, MappedPage *list)
827 {
828     while (list != NULL)
829     {
830         MappedPage *next = list->next;
831
832         if (list->page_index * PAGE_SIZE > This->filesize.u.LowPart)
833         {
834             TRACE("discarding %u\n", list->page_index);
835
836             /* page is entirely outside of the file, delete it */
837             BIGBLOCKFILE_UnlinkPage(list);
838             BIGBLOCKFILE_DeletePage(This, list);
839         }
840         else
841         {
842             /* otherwise, remap it */
843             BIGBLOCKFILE_MapPage(This, list);
844         }
845
846         list = next;
847     }
848 }
849
850 static void BIGBLOCKFILE_RemapAllMappedPages(LPBIGBLOCKFILE This)
851 {
852     BIGBLOCKFILE_RemapList(This, This->maplist);
853     BIGBLOCKFILE_RemapList(This, This->victimhead);
854 }
855
856 /****************************************************************************
857  *      BIGBLOCKFILE_GetProtectMode
858  *
859  * This function will return a protection mode flag for a file-mapping object
860  * from the open flags of a file.
861  */
862 static DWORD BIGBLOCKFILE_GetProtectMode(DWORD openFlags)
863 {
864     switch(STGM_ACCESS_MODE(openFlags))
865     {
866     case STGM_WRITE:
867     case STGM_READWRITE:
868         return PAGE_READWRITE;
869     }
870     return PAGE_READONLY;
871 }