Added stub for StgSetTimes.
[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
26 #include "winbase.h"
27 #include "winerror.h"
28 #include "wine/obj_storage.h"
29 #include "ole2.h"
30
31 #include "storage32.h"
32
33 #include "debugtools.h"
34
35 DEFAULT_DEBUG_CHANNEL(storage)
36
37 /***********************************************************
38  * Data structures used internally by the BigBlockFile
39  * class.
40  */
41
42 /***
43  * Itdentifies a single big block and the related 
44  * information
45  */
46 struct BigBlock
47 {
48   BigBlock * next;
49   DWORD index;
50   DWORD access_mode;
51   LPVOID lpBlock;
52 };
53
54 /***
55  * This structure identifies the paged that are mapped
56  * from the file and their position in memory. It is 
57  * also used to hold a reference count to those pages.
58  */
59 struct MappedPage
60 {
61   MappedPage * next;
62   DWORD number;
63   int ref;
64   LPVOID lpBytes;
65 };
66
67 #define PAGE_SIZE       131072
68 #define BLOCKS_PER_PAGE 256
69
70 #define NUMBER_OF_MAPPED_PAGES 100
71
72 /***********************************************************
73  * Prototypes for private methods
74  */
75 static void*     BIGBLOCKFILE_GetMappedView(LPBIGBLOCKFILE This,
76                                             DWORD          pagenum,
77                                             DWORD          desired_access);
78 static void      BIGBLOCKFILE_ReleaseMappedPage(LPBIGBLOCKFILE This,
79                                                 DWORD          pagenum,
80                                                 DWORD          access);
81 static void      BIGBLOCKFILE_FreeAllMappedPages(LPBIGBLOCKFILE This);
82 static void*     BIGBLOCKFILE_GetBigBlockPointer(LPBIGBLOCKFILE This,
83                                                  ULONG          index,
84                                                  DWORD          desired_access);
85 static BigBlock* BIGBLOCKFILE_GetBigBlockFromPointer(LPBIGBLOCKFILE This, 
86                                                      void*          pBlock);
87 static void      BIGBLOCKFILE_RemoveBlock(LPBIGBLOCKFILE This,
88                                           ULONG          index);
89 static BigBlock* BIGBLOCKFILE_AddBigBlock(LPBIGBLOCKFILE This, 
90                                           ULONG          index);
91 static BigBlock* BIGBLOCKFILE_CreateBlock(ULONG 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 static void      BIGBLOCKFILE_RemoveAllBlocks(LPBIGBLOCKFILE This);
96
97 /******************************************************************************
98  *      BIGBLOCKFILE_Construct
99  *
100  * Construct a big block file. Create the file mapping object. 
101  * Create the read only mapped pages list, the writeable mapped page list
102  * and the blocks in use list.
103  */
104 BigBlockFile * BIGBLOCKFILE_Construct(
105   HANDLE   hFile,
106   ILockBytes* pLkByt,
107   DWORD    openFlags,
108   ULONG    blocksize,
109   BOOL     fileBased)
110 {
111   LPBIGBLOCKFILE This;
112
113   This = (LPBIGBLOCKFILE)HeapAlloc(GetProcessHeap(), 0, sizeof(BigBlockFile));
114
115   if (This == NULL)
116     return NULL;
117
118   This->fileBased = fileBased;
119
120   This->flProtect = BIGBLOCKFILE_GetProtectMode(openFlags);
121
122   This->blocksize = blocksize;
123
124   /* initialize the block list
125    */
126   This->headblock = NULL;
127
128   if (This->fileBased)
129   {
130     if (!BIGBLOCKFILE_FileInit(This, hFile))
131     {
132       HeapFree(GetProcessHeap(), 0, This);
133       return NULL;
134     }
135   }
136   else
137   {
138     if (!BIGBLOCKFILE_MemInit(This, pLkByt))
139     {
140       HeapFree(GetProcessHeap(), 0, This);
141       return NULL;
142     }
143   }
144
145   return This;
146 }
147
148 /******************************************************************************
149  *      BIGBLOCKFILE_FileInit
150  *
151  * Initialize a big block object supported by a file.
152  */
153 static BOOL BIGBLOCKFILE_FileInit(LPBIGBLOCKFILE This, HANDLE hFile)
154 {
155   This->pLkbyt     = NULL;
156   This->hbytearray = 0;
157   This->pbytearray = NULL;
158
159   This->hfile = hFile;
160
161   if (This->hfile == INVALID_HANDLE_VALUE)
162     return FALSE;
163
164   /* create the file mapping object
165    */
166   This->hfilemap = CreateFileMappingA(This->hfile,
167                                       NULL,
168                                       This->flProtect,
169                                       0, 0,
170                                       NULL);
171
172   if (This->hfilemap == NULL)
173   {
174     CloseHandle(This->hfile);
175     return FALSE;
176   }
177
178   This->filesize.s.LowPart = GetFileSize(This->hfile, NULL);
179
180   /* create the mapped pages list
181    */
182   This->maplisthead = HeapAlloc(GetProcessHeap(), 0, sizeof(MappedPage));
183
184   if (This->maplisthead == NULL)
185   {
186     CloseHandle(This->hfilemap);
187     CloseHandle(This->hfile);
188     return FALSE;
189   }
190
191   This->maplisthead->next = NULL;
192
193   return TRUE;
194 }
195
196 /******************************************************************************
197  *      BIGBLOCKFILE_MemInit
198  *
199  * Initialize a big block object supported by an ILockBytes on HGLOABL.
200  */
201 static BOOL BIGBLOCKFILE_MemInit(LPBIGBLOCKFILE This, ILockBytes* plkbyt)
202 {
203   This->hfile       = 0;
204   This->hfilemap    = NULL;
205   This->maplisthead = NULL;
206
207   /*
208    * Retrieve the handle to the byte array from the LockByte object.
209    */
210   if (GetHGlobalFromILockBytes(plkbyt, &(This->hbytearray)) != S_OK)
211   {
212     FIXME("May not be an ILockBytes on HGLOBAL\n");
213     return FALSE;
214   }
215
216   This->pLkbyt = plkbyt;
217
218   /*
219    * Increment the reference count of the ILockByte object since
220    * we're keeping a reference to it.
221    */
222   ILockBytes_AddRef(This->pLkbyt);
223
224   This->filesize.s.LowPart = GlobalSize(This->hbytearray);
225   This->filesize.s.HighPart = 0;
226
227   This->pbytearray = GlobalLock(This->hbytearray);
228
229   return TRUE;
230 }
231
232 /******************************************************************************
233  *      BIGBLOCKFILE_Destructor
234  *
235  * Destructor. Clean up, free memory.
236  */
237 void BIGBLOCKFILE_Destructor(
238   LPBIGBLOCKFILE This)
239 {
240   if (This->fileBased)
241   {
242     /* unmap all views and destroy the mapped page list
243      */
244     BIGBLOCKFILE_FreeAllMappedPages(This);
245     HeapFree(GetProcessHeap(), 0, This->maplisthead);
246   
247     /* close all open handles
248      */
249     CloseHandle(This->hfilemap);
250     CloseHandle(This->hfile);
251   }
252   else
253   {
254     GlobalUnlock(This->hbytearray);
255     ILockBytes_Release(This->pLkbyt);
256   }
257
258   /*
259    * Destroy the blocks list.
260    */
261   BIGBLOCKFILE_RemoveAllBlocks(This);
262
263   /* destroy this
264    */
265   HeapFree(GetProcessHeap(), 0, This);
266 }
267
268 /******************************************************************************
269  *      BIGBLOCKFILE_GetROBigBlock
270  *
271  * Returns the specified block in read only mode.
272  * Will return NULL if the block doesn't exists.
273  */
274 void* BIGBLOCKFILE_GetROBigBlock(
275   LPBIGBLOCKFILE This,
276   ULONG          index)
277 {
278   /*
279    * block index starts at -1
280    * translate to zero based index
281    */
282   if (index == 0xffffffff)
283     index = 0;
284   else
285     index++;
286
287   /*
288    * validate the block index
289    * 
290    */
291   if ((This->blocksize * (index + 1)) >
292       (This->filesize.s.LowPart +
293        (This->blocksize - (This->filesize.s.LowPart % This->blocksize))))
294     return 0;
295
296   return BIGBLOCKFILE_GetBigBlockPointer(This, index, FILE_MAP_READ);
297 }
298
299 /******************************************************************************
300  *      BIGBLOCKFILE_GetBigBlock
301  *
302  * Returns the specified block.
303  * Will grow the file if necessary.
304  */
305 void* BIGBLOCKFILE_GetBigBlock(LPBIGBLOCKFILE This, ULONG index)
306 {
307   /*
308    * block index starts at -1
309    * translate to zero based index
310    */
311   if (index == 0xffffffff)
312     index = 0;
313   else
314     index++;
315
316   /*
317    * make sure that the block physically exists
318    */
319   if ((This->blocksize * (index + 1)) > This->filesize.s.LowPart)
320   {
321     ULARGE_INTEGER newSize;
322
323     newSize.s.HighPart = 0;
324     newSize.s.LowPart = This->blocksize * (index + 1);
325
326     BIGBLOCKFILE_SetSize(This, newSize);
327   }
328
329   return BIGBLOCKFILE_GetBigBlockPointer(This, index, FILE_MAP_WRITE);
330 }
331
332 /******************************************************************************
333  *      BIGBLOCKFILE_ReleaseBigBlock
334  *
335  * Releases the specified block.
336  */
337 void BIGBLOCKFILE_ReleaseBigBlock(LPBIGBLOCKFILE This, void *pBlock)
338 {
339   DWORD page_num;
340   BigBlock* theBigBlock;
341
342   if (pBlock == NULL)
343     return;
344
345   /*
346    * get the block from the block list
347    */
348   theBigBlock = BIGBLOCKFILE_GetBigBlockFromPointer(This, pBlock);
349
350   if (theBigBlock == NULL)
351     return;
352
353   if (This->fileBased)
354   {
355     /*
356      * find out which page this block is in
357      */
358     page_num = theBigBlock->index / BLOCKS_PER_PAGE;
359   
360     /*
361      * release this page
362      */
363     BIGBLOCKFILE_ReleaseMappedPage(This, page_num, theBigBlock->access_mode);
364   }
365
366   /*
367    * remove block from list
368    */
369   BIGBLOCKFILE_RemoveBlock(This, theBigBlock->index);
370 }
371
372 /******************************************************************************
373  *      BIGBLOCKFILE_SetSize
374  *
375  * Sets the size of the file.
376  * 
377  */
378 void BIGBLOCKFILE_SetSize(LPBIGBLOCKFILE This, ULARGE_INTEGER newSize)
379 {
380   if (This->filesize.s.LowPart == newSize.s.LowPart)
381     return;
382
383   if (This->fileBased)
384   {
385     /*
386      * unmap all views, must be done before call to SetEndFile
387      */
388     BIGBLOCKFILE_FreeAllMappedPages(This);
389   
390     /*
391      * close file-mapping object, must be done before call to SetEndFile
392      */
393     CloseHandle(This->hfilemap);
394     This->hfilemap = NULL;
395   
396     /*
397      * set the new end of file
398      */
399     SetFilePointer(This->hfile, newSize.s.LowPart, NULL, FILE_BEGIN);
400     SetEndOfFile(This->hfile);
401   
402     /*
403      * re-create the file mapping object
404      */
405     This->hfilemap = CreateFileMappingA(This->hfile,
406                                         NULL,
407                                         This->flProtect,
408                                         0, 0, 
409                                         NULL);
410   }
411   else
412   {
413     GlobalUnlock(This->hbytearray);
414
415     /*
416      * Resize the byte array object.
417      */
418     ILockBytes_SetSize(This->pLkbyt, newSize);
419
420     /*
421      * Re-acquire the handle, it may have changed.
422      */
423     GetHGlobalFromILockBytes(This->pLkbyt, &This->hbytearray);
424     This->pbytearray = GlobalLock(This->hbytearray);
425   }
426
427   /*
428    * empty the blocks list.
429    */
430   BIGBLOCKFILE_RemoveAllBlocks(This);
431
432   This->filesize.s.LowPart = newSize.s.LowPart;
433   This->filesize.s.HighPart = newSize.s.HighPart;
434 }
435
436 /******************************************************************************
437  *      BIGBLOCKFILE_GetSize
438  *
439  * Returns the size of the file.
440  * 
441  */
442 ULARGE_INTEGER BIGBLOCKFILE_GetSize(LPBIGBLOCKFILE This)
443 {
444   return This->filesize;
445 }
446
447 /******************************************************************************
448  *      BIGBLOCKFILE_GetBigBlockPointer     [PRIVATE]
449  *
450  * Returns a pointer to the specified block.
451  */
452 static void* BIGBLOCKFILE_GetBigBlockPointer(
453   LPBIGBLOCKFILE This, 
454   ULONG          index, 
455   DWORD          desired_access)
456 {
457   DWORD block_num;
458   void * pBytes;
459   BigBlock *aBigBlock;
460
461   /* get the big block from the list or add it to the list
462    */
463   aBigBlock = BIGBLOCKFILE_AddBigBlock(This, index);
464
465   if (aBigBlock == NULL)
466     return NULL;
467
468   /* we already have an address for this block
469    */
470   if (aBigBlock->lpBlock != NULL)
471   {
472     /* make sure the desired access matches what we already have
473      */
474     if (aBigBlock->access_mode == desired_access)
475       return aBigBlock->lpBlock;
476     else
477       return NULL;
478   }
479
480   /*
481    * else aBigBlock->lpBigBlock == NULL, it's a new block
482    */
483
484   if (This->fileBased)
485   {
486     DWORD page_num;
487
488     /* find out which page this block is in
489      */
490     page_num = index / BLOCKS_PER_PAGE;
491   
492     /* offset of the block in the page
493      */
494     block_num = index % BLOCKS_PER_PAGE;
495   
496     /* get a pointer to the first byte in the page
497      */
498     pBytes = BIGBLOCKFILE_GetMappedView(This, page_num, desired_access);
499   }
500   else
501   {
502     pBytes = This->pbytearray; 
503     block_num = index;
504   }
505
506   if (pBytes == NULL)
507     return NULL;
508
509   /* initialize block
510    */
511   aBigBlock->lpBlock = ((BYTE*)pBytes + (block_num*This->blocksize));
512   aBigBlock->access_mode = desired_access;
513
514   return aBigBlock->lpBlock;
515 }
516
517 /******************************************************************************
518  *      BIGBLOCKFILE_CreateBlock      [PRIVATE]
519  *
520  * Creates a node of the blocks list.
521  */
522 static BigBlock* BIGBLOCKFILE_CreateBlock(
523   ULONG index)
524 {
525   BigBlock *newBigBlock;
526
527   /* create new list node
528    */
529   newBigBlock = HeapAlloc(GetProcessHeap(), 0, sizeof(BigBlock));
530
531   if (newBigBlock == NULL)
532     return NULL;
533
534   /* initialize node
535    */
536   newBigBlock->index = index;
537   newBigBlock->lpBlock = NULL;
538
539   return newBigBlock;
540 }
541
542 /******************************************************************************
543  *      BIGBLOCKFILE_AddBigBlock      [PRIVATE]
544  *
545  * Returns the specified block from the blocks list.
546  * If the block is not found in the list, we will create it and add it to the
547  * list.
548  */
549 static BigBlock* BIGBLOCKFILE_AddBigBlock(
550   LPBIGBLOCKFILE This, 
551   ULONG          index)
552 {
553   BigBlock *current = This->headblock;
554   BigBlock *newBigBlock;
555
556   if (current == NULL) /* empty list */
557   {
558     newBigBlock = BIGBLOCKFILE_CreateBlock(index);
559     
560     if (newBigBlock != NULL)
561     {
562       newBigBlock->next = NULL;
563       This->headblock = newBigBlock;
564     }
565
566     return newBigBlock;
567   }
568   else
569   {
570     /* 
571      * special handling for head of the list
572      */
573
574     if (current->index == index) /* it's already here */
575       return current;
576     else if (current->index > index) /* insertion at head of the list */
577     {
578       newBigBlock = BIGBLOCKFILE_CreateBlock(index);
579
580       if (newBigBlock != NULL)
581       {
582         newBigBlock->next = current;
583         This->headblock = newBigBlock;
584       }
585
586       return newBigBlock;
587     }
588   }
589
590   /* iterate through rest the list
591    */
592   while (current->next != NULL)
593   {
594     if (current->next->index == index) /* found it */
595     {
596       return current->next;
597     }
598     else if (current->next->index > index) /* it's not in the list */
599     {
600       newBigBlock = BIGBLOCKFILE_CreateBlock(index);
601
602       if (newBigBlock != NULL)
603       {
604         newBigBlock->next = current->next;
605         current->next = newBigBlock;
606       }
607
608       return newBigBlock;
609     }
610     else
611       current = current->next;
612   }
613
614   /* 
615    * insertion at end of the list
616    */
617   if (current->next == NULL)
618   {
619     newBigBlock = BIGBLOCKFILE_CreateBlock(index);
620
621     if (newBigBlock != NULL)
622     {
623       newBigBlock->next = NULL;
624       current->next = newBigBlock;
625     }
626
627     return newBigBlock;
628   }
629
630   return NULL;
631 }
632
633 /******************************************************************************
634  *      BIGBLOCKFILE_RemoveAllBlocks      [PRIVATE]
635  *
636  * Removes all blocks from the blocks list.
637  */
638 static void BIGBLOCKFILE_RemoveAllBlocks(
639   LPBIGBLOCKFILE This)
640 {
641   BigBlock *current;
642
643   while (This->headblock != NULL)
644   {
645     current = This->headblock;
646     This->headblock = current->next;
647     HeapFree(GetProcessHeap(), 0, current);
648   }
649 }
650
651 /******************************************************************************
652  *      BIGBLOCKFILE_RemoveBlock      [PRIVATE]
653  *
654  * Removes the specified block from the blocks list.
655  */
656 static void BIGBLOCKFILE_RemoveBlock(
657   LPBIGBLOCKFILE This, 
658   ULONG          index)
659 {
660   BigBlock *current = This->headblock;
661
662   /* 
663    * empty list
664    */
665   if (current == NULL)
666     return;
667
668   /* 
669    *special case: removing head of list
670    */
671   if (current->index == index)
672   {
673     /*
674      * set new head free the old one
675      */
676     This->headblock = current->next;
677     HeapFree(GetProcessHeap(), 0, current);
678     
679     return;
680   }
681
682   /* 
683    * iterate through rest of the list
684    */
685   while (current->next != NULL)
686   {
687     if (current->next->index == index) /* found it */
688     {
689       /* 
690        * unlink the block and free the block
691        */
692       current->next = current->next->next;
693       HeapFree(GetProcessHeap(), 0, current->next);
694
695       return;
696     }
697     else
698     {
699       /* next node
700        */
701       current = current->next;
702     }
703   }
704 }
705
706 /******************************************************************************
707  *      BIGBLOCKFILE_GetBigBlockFromPointer     [PRIVATE]
708  *
709  * Given a block pointer, this will return the corresponding block
710  * from the blocks list.
711  */
712 static BigBlock* BIGBLOCKFILE_GetBigBlockFromPointer(
713   LPBIGBLOCKFILE This, 
714   void*          pBlock)
715 {
716   BigBlock *current = This->headblock;
717
718   while (current != NULL)
719   {
720     if (current->lpBlock == pBlock)
721     {
722       break;
723     }
724     else
725       current = current->next;
726   }
727
728   return current;
729 }
730
731 /******************************************************************************
732  *      BIGBLOCKFILE_GetMappedView      [PRIVATE]
733  *
734  * Gets the page requested if it is already mapped.
735  * If it's not already mapped, this method will map it
736  */
737 static void * BIGBLOCKFILE_GetMappedView(
738   LPBIGBLOCKFILE This,
739   DWORD          pagenum,
740   DWORD          desired_access)
741 {
742   MappedPage* current = This->maplisthead;
743   ULONG       count   = 1;
744   BOOL        found   = FALSE;
745
746   assert(This->maplisthead != NULL);
747
748   /*
749    * Search for the page in the list.
750    */
751   while ((found == FALSE) && (current->next != NULL))
752   {
753     if (current->next->number == pagenum)
754     {
755       found = TRUE;
756
757       /*
758        * If it's not already at the head of the list
759        * move it there.
760        */
761       if (current != This->maplisthead)
762       {
763         MappedPage* temp = current->next;
764
765         current->next = current->next->next;
766
767         temp->next = This->maplisthead->next;
768         This->maplisthead->next = temp;
769       }
770     }
771
772     /*
773      * The list is full and we haven't found it.
774      * Free the last element of the list because we'll add a new
775      * one at the head.
776      */
777     if ((found == FALSE) && 
778         (count >= NUMBER_OF_MAPPED_PAGES) &&
779         (current->next != NULL))
780     {
781       UnmapViewOfFile(current->next->lpBytes);
782
783       HeapFree(GetProcessHeap(), 0, current->next);
784       current->next = NULL;
785     }
786
787     if (current->next != NULL)
788       current = current->next;
789
790     count++;
791   }
792
793   /*
794    * Add the page at the head of the list.
795    */
796   if (found == FALSE)
797   {
798     MappedPage* newMappedPage;
799     DWORD       numBytesToMap;
800     DWORD       hioffset  = 0;
801     DWORD       lowoffset = PAGE_SIZE * pagenum;
802
803     newMappedPage = HeapAlloc(GetProcessHeap(), 0, sizeof(MappedPage));
804
805     if (newMappedPage == NULL)
806       return NULL;
807
808     newMappedPage->number = pagenum;
809     newMappedPage->ref = 0;
810
811     newMappedPage->next = This->maplisthead->next;
812     This->maplisthead->next = newMappedPage;
813
814     if (((pagenum + 1) * PAGE_SIZE) > This->filesize.s.LowPart)
815       numBytesToMap = This->filesize.s.LowPart - (pagenum * PAGE_SIZE);
816     else
817       numBytesToMap = PAGE_SIZE;
818
819     if (This->flProtect == PAGE_READONLY)
820       desired_access = FILE_MAP_READ;
821     else
822       desired_access = FILE_MAP_WRITE;
823
824     newMappedPage->lpBytes = MapViewOfFile(This->hfilemap,
825                                            desired_access,
826                                            hioffset,
827                                            lowoffset,
828                                            numBytesToMap);
829   }
830
831   /*
832    * The page we want should now be at the head of the list.
833    */
834   assert(This->maplisthead->next != NULL);
835
836   current = This->maplisthead->next;
837   current->ref++;
838
839   return current->lpBytes;
840 }
841
842 /******************************************************************************
843  *      BIGBLOCKFILE_ReleaseMappedPage      [PRIVATE]
844  *
845  * Decrements the reference count of the mapped page.
846  */
847 static void BIGBLOCKFILE_ReleaseMappedPage(
848   LPBIGBLOCKFILE This,
849   DWORD          pagenum,
850   DWORD          access)
851 {
852   MappedPage* previous = This->maplisthead;
853   MappedPage* current;
854
855   assert(This->maplisthead->next != NULL);
856
857   current = previous->next;
858
859   /* search for the page in the list
860    */
861   while (current != NULL)
862   {
863     if (current->number == pagenum)
864     {
865       /* decrement the reference count
866        */
867       current->ref--;
868       return;
869     }
870     else
871     {
872       previous = current;
873       current = current->next;
874     }
875   }
876 }
877
878 /******************************************************************************
879  *      BIGBLOCKFILE_FreeAllMappedPages     [PRIVATE]
880  *
881  * Unmap all currently mapped pages.
882  * Empty mapped pages list.
883  */
884 static void BIGBLOCKFILE_FreeAllMappedPages(
885   LPBIGBLOCKFILE This)
886 {
887   MappedPage * current = This->maplisthead->next;
888
889   while (current != NULL)
890   {
891     /* Unmap views.
892      */
893     UnmapViewOfFile(current->lpBytes);
894
895     /* Free the nodes.
896      */
897     This->maplisthead->next = current->next;
898     HeapFree(GetProcessHeap(), 0, current);
899
900     current = This->maplisthead->next;
901   }
902 }
903
904 /****************************************************************************
905  *      BIGBLOCKFILE_GetProtectMode
906  *
907  * This function will return a protection mode flag for a file-mapping object
908  * from the open flags of a file.
909  */
910 static DWORD BIGBLOCKFILE_GetProtectMode(DWORD openFlags)
911 {
912   DWORD flProtect        = PAGE_READONLY;
913   BOOL bSTGM_WRITE     = ((openFlags & STGM_WRITE) == STGM_WRITE);
914   BOOL bSTGM_READWRITE = ((openFlags & STGM_READWRITE) == STGM_READWRITE);
915   BOOL bSTGM_READ      = ! (bSTGM_WRITE || bSTGM_READWRITE);
916
917   if (bSTGM_READ)
918     flProtect = PAGE_READONLY;
919
920   if ((bSTGM_WRITE) || (bSTGM_READWRITE))
921     flProtect = PAGE_READWRITE;
922
923   return flProtect;
924 }
925
926