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
28 #include "wine/obj_storage.h"
31 #include "storage32.h"
33 #include "debugtools.h"
35 DEFAULT_DEBUG_CHANNEL(storage
)
37 /***********************************************************
38 * Data structures used internally by the BigBlockFile
43 * Itdentifies a single big block and the related
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.
67 #define PAGE_SIZE 131072
68 #define BLOCKS_PER_PAGE 256
70 #define NUMBER_OF_MAPPED_PAGES 100
72 /***********************************************************
73 * Prototypes for private methods
75 static void* BIGBLOCKFILE_GetMappedView(LPBIGBLOCKFILE This
,
77 DWORD desired_access
);
78 static void BIGBLOCKFILE_ReleaseMappedPage(LPBIGBLOCKFILE This
,
81 static void BIGBLOCKFILE_FreeAllMappedPages(LPBIGBLOCKFILE This
);
82 static void* BIGBLOCKFILE_GetBigBlockPointer(LPBIGBLOCKFILE This
,
84 DWORD desired_access
);
85 static BigBlock
* BIGBLOCKFILE_GetBigBlockFromPointer(LPBIGBLOCKFILE This
,
87 static void BIGBLOCKFILE_RemoveBlock(LPBIGBLOCKFILE This
,
89 static BigBlock
* BIGBLOCKFILE_AddBigBlock(LPBIGBLOCKFILE This
,
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
);
97 /******************************************************************************
98 * BIGBLOCKFILE_Construct
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.
104 BigBlockFile
* BIGBLOCKFILE_Construct(
113 This
= (LPBIGBLOCKFILE
)HeapAlloc(GetProcessHeap(), 0, sizeof(BigBlockFile
));
118 This
->fileBased
= fileBased
;
120 This
->flProtect
= BIGBLOCKFILE_GetProtectMode(openFlags
);
122 This
->blocksize
= blocksize
;
124 /* initialize the block list
126 This
->headblock
= NULL
;
130 if (!BIGBLOCKFILE_FileInit(This
, hFile
))
132 HeapFree(GetProcessHeap(), 0, This
);
138 if (!BIGBLOCKFILE_MemInit(This
, pLkByt
))
140 HeapFree(GetProcessHeap(), 0, This
);
148 /******************************************************************************
149 * BIGBLOCKFILE_FileInit
151 * Initialize a big block object supported by a file.
153 static BOOL
BIGBLOCKFILE_FileInit(LPBIGBLOCKFILE This
, HANDLE hFile
)
156 This
->hbytearray
= 0;
157 This
->pbytearray
= NULL
;
161 if (This
->hfile
== INVALID_HANDLE_VALUE
)
164 /* create the file mapping object
166 This
->hfilemap
= CreateFileMappingA(This
->hfile
,
172 if (This
->hfilemap
== NULL
)
174 CloseHandle(This
->hfile
);
178 This
->filesize
.LowPart
= GetFileSize(This
->hfile
, NULL
);
180 /* create the mapped pages list
182 This
->maplisthead
= HeapAlloc(GetProcessHeap(), 0, sizeof(MappedPage
));
184 if (This
->maplisthead
== NULL
)
186 CloseHandle(This
->hfilemap
);
187 CloseHandle(This
->hfile
);
191 This
->maplisthead
->next
= NULL
;
196 /******************************************************************************
197 * BIGBLOCKFILE_MemInit
199 * Initialize a big block object supported by an ILockBytes on HGLOABL.
201 static BOOL
BIGBLOCKFILE_MemInit(LPBIGBLOCKFILE This
, ILockBytes
* plkbyt
)
204 This
->hfilemap
= NULL
;
205 This
->maplisthead
= NULL
;
208 * Retrieve the handle to the byte array from the LockByte object.
210 if (GetHGlobalFromILockBytes(plkbyt
, &(This
->hbytearray
)) != S_OK
)
212 FIXME("May not be an ILockBytes on HGLOBAL\n");
216 This
->pLkbyt
= plkbyt
;
219 * Increment the reference count of the ILockByte object since
220 * we're keeping a reference to it.
222 ILockBytes_AddRef(This
->pLkbyt
);
224 This
->filesize
.LowPart
= GlobalSize(This
->hbytearray
);
225 This
->filesize
.HighPart
= 0;
227 This
->pbytearray
= GlobalLock(This
->hbytearray
);
232 /******************************************************************************
233 * BIGBLOCKFILE_Destructor
235 * Destructor. Clean up, free memory.
237 void BIGBLOCKFILE_Destructor(
242 /* unmap all views and destroy the mapped page list
244 BIGBLOCKFILE_FreeAllMappedPages(This
);
245 HeapFree(GetProcessHeap(), 0, This
->maplisthead
);
247 /* close all open handles
249 CloseHandle(This
->hfilemap
);
250 CloseHandle(This
->hfile
);
254 GlobalUnlock(This
->hbytearray
);
255 ILockBytes_Release(This
->pLkbyt
);
259 * Destroy the blocks list.
261 BIGBLOCKFILE_RemoveAllBlocks(This
);
265 HeapFree(GetProcessHeap(), 0, This
);
268 /******************************************************************************
269 * BIGBLOCKFILE_GetROBigBlock
271 * Returns the specified block in read only mode.
272 * Will return NULL if the block doesn't exists.
274 void* BIGBLOCKFILE_GetROBigBlock(
279 * block index starts at -1
280 * translate to zero based index
282 if (index
== 0xffffffff)
288 * validate the block index
291 if ((This
->blocksize
* (index
+ 1)) >
292 (This
->filesize
.LowPart
+
293 (This
->blocksize
- (This
->filesize
.LowPart
% This
->blocksize
))))
296 return BIGBLOCKFILE_GetBigBlockPointer(This
, index
, FILE_MAP_READ
);
299 /******************************************************************************
300 * BIGBLOCKFILE_GetBigBlock
302 * Returns the specified block.
303 * Will grow the file if necessary.
305 void* BIGBLOCKFILE_GetBigBlock(LPBIGBLOCKFILE This
, ULONG index
)
308 * block index starts at -1
309 * translate to zero based index
311 if (index
== 0xffffffff)
317 * make sure that the block physically exists
319 if ((This
->blocksize
* (index
+ 1)) > This
->filesize
.LowPart
)
321 ULARGE_INTEGER newSize
;
323 newSize
.HighPart
= 0;
324 newSize
.LowPart
= This
->blocksize
* (index
+ 1);
326 BIGBLOCKFILE_SetSize(This
, newSize
);
329 return BIGBLOCKFILE_GetBigBlockPointer(This
, index
, FILE_MAP_WRITE
);
332 /******************************************************************************
333 * BIGBLOCKFILE_ReleaseBigBlock
335 * Releases the specified block.
337 void BIGBLOCKFILE_ReleaseBigBlock(LPBIGBLOCKFILE This
, void *pBlock
)
340 BigBlock
* theBigBlock
;
346 * get the block from the block list
348 theBigBlock
= BIGBLOCKFILE_GetBigBlockFromPointer(This
, pBlock
);
350 if (theBigBlock
== NULL
)
356 * find out which page this block is in
358 page_num
= theBigBlock
->index
/ BLOCKS_PER_PAGE
;
363 BIGBLOCKFILE_ReleaseMappedPage(This
, page_num
, theBigBlock
->access_mode
);
367 * remove block from list
369 BIGBLOCKFILE_RemoveBlock(This
, theBigBlock
->index
);
372 /******************************************************************************
373 * BIGBLOCKFILE_SetSize
375 * Sets the size of the file.
378 void BIGBLOCKFILE_SetSize(LPBIGBLOCKFILE This
, ULARGE_INTEGER newSize
)
380 if (This
->filesize
.LowPart
== newSize
.LowPart
)
386 * unmap all views, must be done before call to SetEndFile
388 BIGBLOCKFILE_FreeAllMappedPages(This
);
391 * close file-mapping object, must be done before call to SetEndFile
393 CloseHandle(This
->hfilemap
);
394 This
->hfilemap
= NULL
;
397 * set the new end of file
399 SetFilePointer(This
->hfile
, newSize
.LowPart
, NULL
, FILE_BEGIN
);
400 SetEndOfFile(This
->hfile
);
403 * re-create the file mapping object
405 This
->hfilemap
= CreateFileMappingA(This
->hfile
,
413 GlobalUnlock(This
->hbytearray
);
416 * Resize the byte array object.
418 ILockBytes_SetSize(This
->pLkbyt
, newSize
);
421 * Re-acquire the handle, it may have changed.
423 GetHGlobalFromILockBytes(This
->pLkbyt
, &This
->hbytearray
);
424 This
->pbytearray
= GlobalLock(This
->hbytearray
);
428 * empty the blocks list.
430 BIGBLOCKFILE_RemoveAllBlocks(This
);
432 This
->filesize
.LowPart
= newSize
.LowPart
;
433 This
->filesize
.HighPart
= newSize
.HighPart
;
436 /******************************************************************************
437 * BIGBLOCKFILE_GetSize
439 * Returns the size of the file.
442 ULARGE_INTEGER
BIGBLOCKFILE_GetSize(LPBIGBLOCKFILE This
)
444 return This
->filesize
;
447 /******************************************************************************
448 * BIGBLOCKFILE_GetBigBlockPointer [PRIVATE]
450 * Returns a pointer to the specified block.
452 static void* BIGBLOCKFILE_GetBigBlockPointer(
455 DWORD desired_access
)
461 /* get the big block from the list or add it to the list
463 aBigBlock
= BIGBLOCKFILE_AddBigBlock(This
, index
);
465 if (aBigBlock
== NULL
)
468 /* we already have an address for this block
470 if (aBigBlock
->lpBlock
!= NULL
)
472 /* make sure the desired access matches what we already have
474 if (aBigBlock
->access_mode
== desired_access
)
475 return aBigBlock
->lpBlock
;
481 * else aBigBlock->lpBigBlock == NULL, it's a new block
488 /* find out which page this block is in
490 page_num
= index
/ BLOCKS_PER_PAGE
;
492 /* offset of the block in the page
494 block_num
= index
% BLOCKS_PER_PAGE
;
496 /* get a pointer to the first byte in the page
498 pBytes
= BIGBLOCKFILE_GetMappedView(This
, page_num
, desired_access
);
502 pBytes
= This
->pbytearray
;
511 aBigBlock
->lpBlock
= ((BYTE
*)pBytes
+ (block_num
*This
->blocksize
));
512 aBigBlock
->access_mode
= desired_access
;
514 return aBigBlock
->lpBlock
;
517 /******************************************************************************
518 * BIGBLOCKFILE_CreateBlock [PRIVATE]
520 * Creates a node of the blocks list.
522 static BigBlock
* BIGBLOCKFILE_CreateBlock(
525 BigBlock
*newBigBlock
;
527 /* create new list node
529 newBigBlock
= HeapAlloc(GetProcessHeap(), 0, sizeof(BigBlock
));
531 if (newBigBlock
== NULL
)
536 newBigBlock
->index
= index
;
537 newBigBlock
->lpBlock
= NULL
;
542 /******************************************************************************
543 * BIGBLOCKFILE_AddBigBlock [PRIVATE]
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
549 static BigBlock
* BIGBLOCKFILE_AddBigBlock(
553 BigBlock
*current
= This
->headblock
;
554 BigBlock
*newBigBlock
;
556 if (current
== NULL
) /* empty list */
558 newBigBlock
= BIGBLOCKFILE_CreateBlock(index
);
560 if (newBigBlock
!= NULL
)
562 newBigBlock
->next
= NULL
;
563 This
->headblock
= newBigBlock
;
571 * special handling for head of the list
574 if (current
->index
== index
) /* it's already here */
576 else if (current
->index
> index
) /* insertion at head of the list */
578 newBigBlock
= BIGBLOCKFILE_CreateBlock(index
);
580 if (newBigBlock
!= NULL
)
582 newBigBlock
->next
= current
;
583 This
->headblock
= newBigBlock
;
590 /* iterate through rest the list
592 while (current
->next
!= NULL
)
594 if (current
->next
->index
== index
) /* found it */
596 return current
->next
;
598 else if (current
->next
->index
> index
) /* it's not in the list */
600 newBigBlock
= BIGBLOCKFILE_CreateBlock(index
);
602 if (newBigBlock
!= NULL
)
604 newBigBlock
->next
= current
->next
;
605 current
->next
= newBigBlock
;
611 current
= current
->next
;
615 * insertion at end of the list
617 if (current
->next
== NULL
)
619 newBigBlock
= BIGBLOCKFILE_CreateBlock(index
);
621 if (newBigBlock
!= NULL
)
623 newBigBlock
->next
= NULL
;
624 current
->next
= newBigBlock
;
633 /******************************************************************************
634 * BIGBLOCKFILE_RemoveAllBlocks [PRIVATE]
636 * Removes all blocks from the blocks list.
638 static void BIGBLOCKFILE_RemoveAllBlocks(
643 while (This
->headblock
!= NULL
)
645 current
= This
->headblock
;
646 This
->headblock
= current
->next
;
647 HeapFree(GetProcessHeap(), 0, current
);
651 /******************************************************************************
652 * BIGBLOCKFILE_RemoveBlock [PRIVATE]
654 * Removes the specified block from the blocks list.
656 static void BIGBLOCKFILE_RemoveBlock(
660 BigBlock
*current
= This
->headblock
;
669 *special case: removing head of list
671 if (current
->index
== index
)
674 * set new head free the old one
676 This
->headblock
= current
->next
;
677 HeapFree(GetProcessHeap(), 0, current
);
683 * iterate through rest of the list
685 while (current
->next
!= NULL
)
687 if (current
->next
->index
== index
) /* found it */
690 * unlink the block and free the block
692 current
->next
= current
->next
->next
;
693 HeapFree(GetProcessHeap(), 0, current
->next
);
701 current
= current
->next
;
706 /******************************************************************************
707 * BIGBLOCKFILE_GetBigBlockFromPointer [PRIVATE]
709 * Given a block pointer, this will return the corresponding block
710 * from the blocks list.
712 static BigBlock
* BIGBLOCKFILE_GetBigBlockFromPointer(
716 BigBlock
*current
= This
->headblock
;
718 while (current
!= NULL
)
720 if (current
->lpBlock
== pBlock
)
725 current
= current
->next
;
731 /******************************************************************************
732 * BIGBLOCKFILE_GetMappedView [PRIVATE]
734 * Gets the page requested if it is already mapped.
735 * If it's not already mapped, this method will map it
737 static void * BIGBLOCKFILE_GetMappedView(
740 DWORD desired_access
)
742 MappedPage
* current
= This
->maplisthead
;
746 assert(This
->maplisthead
!= NULL
);
749 * Search for the page in the list.
751 while ((found
== FALSE
) && (current
->next
!= NULL
))
753 if (current
->next
->number
== pagenum
)
758 * If it's not already at the head of the list
761 if (current
!= This
->maplisthead
)
763 MappedPage
* temp
= current
->next
;
765 current
->next
= current
->next
->next
;
767 temp
->next
= This
->maplisthead
->next
;
768 This
->maplisthead
->next
= temp
;
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
777 if ((found
== FALSE
) &&
778 (count
>= NUMBER_OF_MAPPED_PAGES
) &&
779 (current
->next
!= NULL
))
781 UnmapViewOfFile(current
->next
->lpBytes
);
783 HeapFree(GetProcessHeap(), 0, current
->next
);
784 current
->next
= NULL
;
787 if (current
->next
!= NULL
)
788 current
= current
->next
;
794 * Add the page at the head of the list.
798 MappedPage
* newMappedPage
;
801 DWORD lowoffset
= PAGE_SIZE
* pagenum
;
803 newMappedPage
= HeapAlloc(GetProcessHeap(), 0, sizeof(MappedPage
));
805 if (newMappedPage
== NULL
)
808 newMappedPage
->number
= pagenum
;
809 newMappedPage
->ref
= 0;
811 newMappedPage
->next
= This
->maplisthead
->next
;
812 This
->maplisthead
->next
= newMappedPage
;
814 if (((pagenum
+ 1) * PAGE_SIZE
) > This
->filesize
.LowPart
)
815 numBytesToMap
= This
->filesize
.LowPart
- (pagenum
* PAGE_SIZE
);
817 numBytesToMap
= PAGE_SIZE
;
819 if (This
->flProtect
== PAGE_READONLY
)
820 desired_access
= FILE_MAP_READ
;
822 desired_access
= FILE_MAP_WRITE
;
824 newMappedPage
->lpBytes
= MapViewOfFile(This
->hfilemap
,
832 * The page we want should now be at the head of the list.
834 assert(This
->maplisthead
->next
!= NULL
);
836 current
= This
->maplisthead
->next
;
839 return current
->lpBytes
;
842 /******************************************************************************
843 * BIGBLOCKFILE_ReleaseMappedPage [PRIVATE]
845 * Decrements the reference count of the mapped page.
847 static void BIGBLOCKFILE_ReleaseMappedPage(
852 MappedPage
* previous
= This
->maplisthead
;
855 assert(This
->maplisthead
->next
!= NULL
);
857 current
= previous
->next
;
859 /* search for the page in the list
861 while (current
!= NULL
)
863 if (current
->number
== pagenum
)
865 /* decrement the reference count
873 current
= current
->next
;
878 /******************************************************************************
879 * BIGBLOCKFILE_FreeAllMappedPages [PRIVATE]
881 * Unmap all currently mapped pages.
882 * Empty mapped pages list.
884 static void BIGBLOCKFILE_FreeAllMappedPages(
887 MappedPage
* current
= This
->maplisthead
->next
;
889 while (current
!= NULL
)
893 UnmapViewOfFile(current
->lpBytes
);
897 This
->maplisthead
->next
= current
->next
;
898 HeapFree(GetProcessHeap(), 0, current
);
900 current
= This
->maplisthead
->next
;
904 /****************************************************************************
905 * BIGBLOCKFILE_GetProtectMode
907 * This function will return a protection mode flag for a file-mapping object
908 * from the open flags of a file.
910 static DWORD
BIGBLOCKFILE_GetProtectMode(DWORD openFlags
)
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
);
918 flProtect
= PAGE_READONLY
;
920 if ((bSTGM_WRITE
) || (bSTGM_READWRITE
))
921 flProtect
= PAGE_READWRITE
;