1 /******************************************************************************
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
15 * - Support for a transacted mode
17 * Copyright 1999 Thuy Nguyen
30 #include "wine/obj_base.h"
31 #include "wine/obj_storage.h"
33 #include "storage32.h"
35 /***********************************************************
36 * Data structures used internally by the BigBlockFile
41 * Itdentifies a single big block and the related
53 * This structure identifies the paged that are mapped
54 * from the file and their position in memory. It is
55 * also used to hold a reference count to those pages.
65 #define BLOCKS_PER_PAGE 128
66 #define PAGE_SIZE 65536
68 /***********************************************************
69 * Prototypes for private methods
71 static void* BIGBLOCKFILE_GetMappedView(LPBIGBLOCKFILE This
,
73 DWORD desired_access
);
74 static void BIGBLOCKFILE_ReleaseMappedPage(LPBIGBLOCKFILE This
,
77 static void BIGBLOCKFILE_FreeAllMappedPages(LPBIGBLOCKFILE This
);
78 static void* BIGBLOCKFILE_GetBigBlockPointer(LPBIGBLOCKFILE This
,
80 DWORD desired_access
);
81 static BigBlock
* BIGBLOCKFILE_GetBigBlockFromPointer(LPBIGBLOCKFILE This
,
83 static void BIGBLOCKFILE_RemoveBlock(LPBIGBLOCKFILE This
,
85 static BigBlock
* BIGBLOCKFILE_AddBigBlock(LPBIGBLOCKFILE This
,
87 static BigBlock
* BIGBLOCKFILE_CreateBlock(ULONG index
);
89 static DWORD
BIGBLOCKFILE_GetProtectMode(DWORD openFlags
);
91 /******************************************************************************
92 * BIGBLOCKFILE_Construct
94 * Construct a big block file. Create the file mapping object.
95 * Create the read only mapped pages list, the writeable mapped page list
96 * and the blocks in use list.
98 BigBlockFile
* BIGBLOCKFILE_Construct(
105 This
= (LPBIGBLOCKFILE
)HeapAlloc(GetProcessHeap(), 0, sizeof(BigBlockFile
));
112 if (This
->hfile
== INVALID_HANDLE_VALUE
)
114 HeapFree(GetProcessHeap(), 0, This
);
118 This
->flProtect
= BIGBLOCKFILE_GetProtectMode(openFlags
);
120 /* create the file mapping object
122 This
->hfilemap
= CreateFileMappingA(This
->hfile
,
128 if (This
->hfilemap
== NULL
)
130 CloseHandle(This
->hfile
);
131 HeapFree(GetProcessHeap(), 0, This
);
137 This
->filesize
.LowPart
= GetFileSize(This
->hfile
, NULL
);
138 This
->blocksize
= blocksize
;
140 /* create the read only mapped pages list
142 This
->headmap_ro
= HeapAlloc(GetProcessHeap(), 0, sizeof(MappedPage
));
144 if (This
->headmap_ro
== NULL
)
146 CloseHandle(This
->hfilemap
);
147 CloseHandle(This
->hfile
);
148 HeapFree(GetProcessHeap(), 0, This
);
152 This
->headmap_ro
->next
= NULL
;
154 /* create the writeable mapped pages list
156 This
->headmap_w
= HeapAlloc(GetProcessHeap(), 0, sizeof(MappedPage
));
158 if (This
->headmap_w
== NULL
)
160 CloseHandle(This
->hfilemap
);
161 CloseHandle(This
->hfile
);
162 HeapFree(GetProcessHeap(), 0, This
->headmap_ro
);
163 HeapFree(GetProcessHeap(), 0, This
);
167 This
->headmap_w
->next
= NULL
;
169 /* initialize the block list
171 This
->headblock
= NULL
;
176 /******************************************************************************
177 * BIGBLOCKFILE_Destructor
179 * Destructor. Clean up, free memory.
181 void BIGBLOCKFILE_Destructor(
184 /* unmap all views and destroy the mapped page lists
186 BIGBLOCKFILE_FreeAllMappedPages(This
);
187 HeapFree(GetProcessHeap(), 0, This
->headmap_ro
);
188 HeapFree(GetProcessHeap(), 0, This
->headmap_w
);
190 /* close all open handles
192 CloseHandle(This
->hfilemap
);
193 CloseHandle(This
->hfile
);
197 HeapFree(GetProcessHeap(), 0, This
);
200 /******************************************************************************
201 * BIGBLOCKFILE_GetROBigBlock
203 * Returns the specified block in read only mode.
204 * Will return NULL if the block doesn't exists.
206 void* BIGBLOCKFILE_GetROBigBlock(
211 * block index starts at -1
212 * translate to zero based index
214 if (index
== 0xffffffff)
220 * validate the block index
223 if ((This
->blocksize
* (index
+ 1)) >
224 (This
->filesize
.LowPart
+
225 (This
->blocksize
- (This
->filesize
.LowPart
% This
->blocksize
))))
228 return BIGBLOCKFILE_GetBigBlockPointer(This
, index
, FILE_MAP_READ
);
231 /******************************************************************************
232 * BIGBLOCKFILE_GetBigBlock
234 * Returns the specified block.
235 * Will grow the file if necessary.
237 void* BIGBLOCKFILE_GetBigBlock(LPBIGBLOCKFILE This
, ULONG index
)
240 * block index starts at -1
241 * translate to zero based index
243 if (index
== 0xffffffff)
249 * make sure that the block physically exists
251 if ((This
->blocksize
* (index
+ 1)) > This
->filesize
.LowPart
)
253 ULARGE_INTEGER newSize
;
255 newSize
.HighPart
= 0;
256 newSize
.LowPart
= This
->blocksize
* (index
+ 1);
258 BIGBLOCKFILE_SetSize(This
, newSize
);
261 return BIGBLOCKFILE_GetBigBlockPointer(This
, index
, FILE_MAP_WRITE
);
264 /******************************************************************************
265 * BIGBLOCKFILE_ReleaseBigBlock
267 * Releases the specified block.
270 void BIGBLOCKFILE_ReleaseBigBlock(LPBIGBLOCKFILE This
, void *pBlock
)
273 BigBlock
* theBigBlock
;
279 * get the block from the block list
281 theBigBlock
= BIGBLOCKFILE_GetBigBlockFromPointer(This
, pBlock
);
283 if (theBigBlock
== NULL
)
287 * find out which page this block is in
289 page_num
= theBigBlock
->index
/ BLOCKS_PER_PAGE
;
294 BIGBLOCKFILE_ReleaseMappedPage(This
, page_num
, theBigBlock
->access_mode
);
297 * remove block from list
299 BIGBLOCKFILE_RemoveBlock(This
, theBigBlock
->index
);
302 /******************************************************************************
303 * BIGBLOCKFILE_SetSize
305 * Sets the size of the file.
308 void BIGBLOCKFILE_SetSize(LPBIGBLOCKFILE This
, ULARGE_INTEGER newSize
)
310 if (This
->filesize
.LowPart
== newSize
.LowPart
)
314 * unmap all views, must be done before call to SetEndFile
316 BIGBLOCKFILE_FreeAllMappedPages(This
);
319 * close file-mapping object, must be done before call to SetEndFile
321 CloseHandle(This
->hfilemap
);
322 This
->hfilemap
= NULL
;
325 * set the new end of file
327 SetFilePointer(This
->hfile
, newSize
.LowPart
, NULL
, FILE_BEGIN
);
328 SetEndOfFile(This
->hfile
);
331 * re-create the file mapping object
333 This
->hfilemap
= CreateFileMappingA(This
->hfile
,
339 This
->filesize
.LowPart
= newSize
.LowPart
;
340 This
->filesize
.HighPart
= newSize
.HighPart
;
343 /******************************************************************************
344 * BIGBLOCKFILE_GetSize
346 * Returns the size of the file.
349 ULARGE_INTEGER
BIGBLOCKFILE_GetSize(LPBIGBLOCKFILE This
)
351 return This
->filesize
;
354 /******************************************************************************
355 * BIGBLOCKFILE_GetBigBlockPointer [PRIVATE]
357 * Returns a pointer to the specified block.
359 static void* BIGBLOCKFILE_GetBigBlockPointer(
362 DWORD desired_access
)
364 DWORD page_num
, block_num
;
368 /* get the big block from the list or add it to the list
370 aBigBlock
= BIGBLOCKFILE_AddBigBlock(This
, index
);
372 if (aBigBlock
== NULL
)
375 /* we already have an address for this block
377 if (aBigBlock
->lpBlock
!= NULL
)
379 /* make sure the desired access matches what we already have
381 if (aBigBlock
->access_mode
== desired_access
)
382 return aBigBlock
->lpBlock
;
388 * else aBigBlock->lpBigBlock == NULL, it's a new block
391 /* find out which page this block is in
393 page_num
= index
/ BLOCKS_PER_PAGE
;
395 /* offset of the block in the page
397 block_num
= index
% BLOCKS_PER_PAGE
;
399 /* get a pointer to the first byte in the page
401 pBytes
= BIGBLOCKFILE_GetMappedView(This
, page_num
, desired_access
);
408 aBigBlock
->lpBlock
= ((BYTE
*)pBytes
+ (block_num
*This
->blocksize
));
409 aBigBlock
->access_mode
= desired_access
;
411 return aBigBlock
->lpBlock
;
414 /******************************************************************************
415 * BIGBLOCKFILE_CreateBlock [PRIVATE]
417 * Creates a node of the blocks list.
419 static BigBlock
* BIGBLOCKFILE_CreateBlock(
422 BigBlock
*newBigBlock
;
424 /* create new list node
426 newBigBlock
= HeapAlloc(GetProcessHeap(), 0, sizeof(BigBlock
));
428 if (newBigBlock
== NULL
)
433 newBigBlock
->index
= index
;
434 newBigBlock
->lpBlock
= NULL
;
439 /******************************************************************************
440 * BIGBLOCKFILE_AddBigBlock [PRIVATE]
442 * Returns the specified block from the blocks list.
443 * If the block is not found in the list, we will create it and add it to the
446 static BigBlock
* BIGBLOCKFILE_AddBigBlock(
450 BigBlock
*current
= This
->headblock
;
451 BigBlock
*newBigBlock
;
453 if (current
== NULL
) /* empty list */
455 newBigBlock
= BIGBLOCKFILE_CreateBlock(index
);
457 if (newBigBlock
!= NULL
)
459 newBigBlock
->next
= NULL
;
460 This
->headblock
= newBigBlock
;
468 * special handling for head of the list
471 if (current
->index
== index
) /* it's already here */
473 else if (current
->index
> index
) /* insertion at head of the list */
475 newBigBlock
= BIGBLOCKFILE_CreateBlock(index
);
477 if (newBigBlock
!= NULL
)
479 newBigBlock
->next
= current
;
480 This
->headblock
= newBigBlock
;
487 /* iterate through rest the list
489 while (current
->next
!= NULL
)
491 if (current
->next
->index
== index
) /* found it */
493 return current
->next
;
495 else if (current
->next
->index
> index
) /* it's not in the list */
497 newBigBlock
= BIGBLOCKFILE_CreateBlock(index
);
499 if (newBigBlock
!= NULL
)
501 newBigBlock
->next
= current
->next
;
502 current
->next
= newBigBlock
;
508 current
= current
->next
;
512 * insertion at end of the list
514 if (current
->next
== NULL
)
516 newBigBlock
= BIGBLOCKFILE_CreateBlock(index
);
518 if (newBigBlock
!= NULL
)
520 newBigBlock
->next
= NULL
;
521 current
->next
= newBigBlock
;
530 /******************************************************************************
531 * BIGBLOCKFILE_RemoveBlock [PRIVATE]
533 * Removes the specified block from the blocks list.
535 static void BIGBLOCKFILE_RemoveBlock(
539 BigBlock
*current
= This
->headblock
;
548 *special case: removing head of list
550 if (current
->index
== index
)
553 * set new head free the old one
555 This
->headblock
= current
->next
;
556 HeapFree(GetProcessHeap(), 0, current
);
562 * iterate through rest of the list
564 while (current
->next
!= NULL
)
566 if (current
->next
->index
== index
) /* found it */
569 * unlink the block and free the block
571 current
->next
= current
->next
->next
;
572 HeapFree(GetProcessHeap(), 0, current
->next
);
580 current
= current
->next
;
585 /******************************************************************************
586 * BIGBLOCKFILE_GetBigBlockFromPointer [PRIVATE]
588 * Given a block pointer, this will return the corresponding block
589 * from the blocks list.
591 static BigBlock
* BIGBLOCKFILE_GetBigBlockFromPointer(
595 BigBlock
*current
= This
->headblock
;
597 while (current
!= NULL
)
599 if (current
->lpBlock
== pBlock
)
604 current
= current
->next
;
610 /******************************************************************************
611 * BIGBLOCKFILE_GetMappedView [PRIVATE]
613 * Gets the page requested if it is already mapped.
614 * If it's not already mapped, this method will map it
616 static void * BIGBLOCKFILE_GetMappedView(
619 DWORD desired_access
)
621 MappedPage
* current
;
622 MappedPage
* newMappedPage
;
623 DWORD hioffset
, lowoffset
;
628 if (desired_access
== FILE_MAP_READ
)
629 current
= This
->headmap_ro
;
630 else if (desired_access
== FILE_MAP_WRITE
)
631 current
= This
->headmap_w
;
636 lowoffset
= PAGE_SIZE
* pagenum
;
638 while (current
->next
!= NULL
)
640 if (current
->next
->number
== pagenum
) /* page already mapped */
642 current
->next
->ref
++;
643 return current
->next
->lpBytes
;
645 else if (current
->next
->number
> pagenum
) /* this page is not mapped yet */
647 /* allocate new MappedPage
649 newMappedPage
= HeapAlloc(GetProcessHeap(), 0, sizeof(MappedPage
));
651 if (newMappedPage
== NULL
)
654 /* initialize the new MappedPage
656 newMappedPage
->number
= pagenum
;
657 newMappedPage
->ref
= 1;
659 newMappedPage
->next
= current
->next
;
660 current
->next
= newMappedPage
;
662 /* actually map the page
664 if (((pagenum
+ 1) * PAGE_SIZE
) > This
->filesize
.LowPart
)
665 numBytesToMap
= This
->filesize
.LowPart
- (pagenum
* PAGE_SIZE
);
667 numBytesToMap
= PAGE_SIZE
;
669 newMappedPage
->lpBytes
= MapViewOfFile(This
->hfilemap
,
675 return newMappedPage
->lpBytes
;
678 current
= current
->next
;
681 /* reached end of the list, this view is not mapped yet
683 if (current
->next
== NULL
)
685 /* allocate new MappedPage
687 newMappedPage
= HeapAlloc(GetProcessHeap(), 0, sizeof(MappedPage
));
689 if (newMappedPage
== NULL
)
692 /* initialize the new MappedPage
694 newMappedPage
->number
= pagenum
;
695 newMappedPage
->ref
= 1;
697 newMappedPage
->next
= NULL
;
698 current
->next
= newMappedPage
;
700 /* actually map the page
702 if (((pagenum
+ 1) * PAGE_SIZE
) > This
->filesize
.LowPart
)
703 numBytesToMap
= This
->filesize
.LowPart
- (pagenum
* PAGE_SIZE
);
705 numBytesToMap
= PAGE_SIZE
;
707 newMappedPage
->lpBytes
= MapViewOfFile(This
->hfilemap
,
713 return newMappedPage
->lpBytes
;
719 /******************************************************************************
720 * BIGBLOCKFILE_ReleaseMappedPage [PRIVATE]
722 * Decrements the reference count of the mapped page.
723 * If the page is not used anymore it will be unmapped.
725 static void BIGBLOCKFILE_ReleaseMappedPage(
730 MappedPage
* previous
;
731 MappedPage
* current
;
733 /* use the list corresponding to the desired access mode
735 if (access
== FILE_MAP_READ
)
736 previous
= This
->headmap_ro
;
737 else if (access
== FILE_MAP_WRITE
)
738 previous
= This
->headmap_w
;
742 current
= previous
->next
;
744 /* search for the page in the list
746 while (current
!= NULL
)
748 if (current
->number
== pagenum
)
750 /* decrement the reference count
754 if (current
->ref
== 0)
756 /* this page is not used anymore, we can unmap it
758 UnmapViewOfFile(current
->lpBytes
);
760 previous
->next
= current
->next
;
761 HeapFree(GetProcessHeap(), 0, current
);
769 current
= current
->next
;
774 /******************************************************************************
775 * BIGBLOCKFILE_FreeAllMappedPages [PRIVATE]
777 * Unmap all currently mapped pages.
778 * Empty both mapped pages lists.
780 static void BIGBLOCKFILE_FreeAllMappedPages(
784 * start with the read only list
786 MappedPage
* current
= This
->headmap_ro
->next
;
788 while (current
!= NULL
)
792 UnmapViewOfFile(current
->lpBytes
);
796 This
->headmap_ro
->next
= current
->next
;
797 HeapFree(GetProcessHeap(), 0, current
);
799 current
= This
->headmap_ro
->next
;
803 * then do the write list
805 current
= This
->headmap_w
->next
;
807 while (current
!= NULL
)
811 UnmapViewOfFile(current
->lpBytes
);
815 This
->headmap_w
->next
= current
->next
;
816 HeapFree(GetProcessHeap(), 0, current
);
818 current
= This
->headmap_w
->next
;
822 /****************************************************************************
823 * BIGBLOCKFILE_GetProtectMode
825 * This function will return a protection mode flag for a file-mapping object
826 * from the open flags of a file.
828 static DWORD
BIGBLOCKFILE_GetProtectMode(DWORD openFlags
)
830 DWORD flProtect
= PAGE_READONLY
;
831 BOOL bSTGM_WRITE
= ((openFlags
& STGM_WRITE
) == STGM_WRITE
);
832 BOOL bSTGM_READWRITE
= ((openFlags
& STGM_READWRITE
) == STGM_READWRITE
);
833 BOOL bSTGM_READ
= ! (bSTGM_WRITE
|| bSTGM_READWRITE
);
836 flProtect
= PAGE_READONLY
;
838 if ((bSTGM_WRITE
) || (bSTGM_READWRITE
))
839 flProtect
= PAGE_READWRITE
;