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