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