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