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