| /****************************************************************************** | |
| * | |
| * BigBlockFile | |
| * | |
| * This is the implementation of a file that consists of blocks of | |
| * a predetermined size. | |
| * This class is used in the Compound File implementation of the | |
| * IStorage and IStream interfaces. It provides the functionality | |
| * to read and write any blocks in the file as well as setting and | |
| * obtaining the size of the file. | |
| * The blocks are indexed sequentially from the start of the file | |
| * starting with -1. | |
| * | |
| * TODO: | |
| * - Support for a transacted mode | |
| * | |
| * Copyright 1999 Thuy Nguyen | |
| * | |
| */ | |
| #include <assert.h> | |
| #include <stdlib.h> | |
| #include <stdio.h> | |
| #include <string.h> | |
| #include <limits.h> | |
| #include "winbase.h" | |
| #include "winerror.h" | |
| #include "wine/obj_storage.h" | |
| #include "ole2.h" | |
| #include "storage32.h" | |
| #include "debugtools.h" | |
| DEFAULT_DEBUG_CHANNEL(storage) | |
| /*********************************************************** | |
| * Data structures used internally by the BigBlockFile | |
| * class. | |
| */ | |
| /* We map in PAGE_SIZE-sized chunks. Must be a multiple of 4096. */ | |
| #define PAGE_SIZE 131072 | |
| #define BLOCKS_PER_PAGE (PAGE_SIZE / BIG_BLOCK_SIZE) | |
| /* We keep a list of recently-discarded pages. This controls the | |
| * size of that list. */ | |
| #define MAX_VICTIM_PAGES 16 | |
| /* This structure provides one bit for each block in a page. | |
| * Use BIGBLOCKFILE_{Test,Set,Clear}Bit to manipulate it. */ | |
| typedef struct | |
| { | |
| unsigned int bits[BLOCKS_PER_PAGE / (CHAR_BIT * sizeof(unsigned int))]; | |
| } BlockBits; | |
| /*** | |
| * This structure identifies the paged that are mapped | |
| * from the file and their position in memory. It is | |
| * also used to hold a reference count to those pages. | |
| * | |
| * page_index identifies which PAGE_SIZE chunk from the | |
| * file this mapping represents. (The mappings are always | |
| * PAGE_SIZE-aligned.) | |
| */ | |
| struct MappedPage | |
| { | |
| MappedPage *next; | |
| MappedPage *prev; | |
| DWORD page_index; | |
| LPVOID lpBytes; | |
| LONG refcnt; | |
| BlockBits readable_blocks; | |
| BlockBits writable_blocks; | |
| }; | |
| /*********************************************************** | |
| * Prototypes for private methods | |
| */ | |
| static void* BIGBLOCKFILE_GetMappedView(LPBIGBLOCKFILE This, | |
| DWORD page_index); | |
| static void BIGBLOCKFILE_ReleaseMappedPage(LPBIGBLOCKFILE This, | |
| MappedPage *page); | |
| static void BIGBLOCKFILE_FreeAllMappedPages(LPBIGBLOCKFILE This); | |
| static void BIGBLOCKFILE_UnmapAllMappedPages(LPBIGBLOCKFILE This); | |
| static void BIGBLOCKFILE_RemapAllMappedPages(LPBIGBLOCKFILE This); | |
| static void* BIGBLOCKFILE_GetBigBlockPointer(LPBIGBLOCKFILE This, | |
| ULONG index, | |
| DWORD desired_access); | |
| static MappedPage* BIGBLOCKFILE_GetPageFromPointer(LPBIGBLOCKFILE This, | |
| void* pBlock); | |
| static MappedPage* BIGBLOCKFILE_CreatePage(LPBIGBLOCKFILE This, | |
| ULONG page_index); | |
| static DWORD BIGBLOCKFILE_GetProtectMode(DWORD openFlags); | |
| static BOOL BIGBLOCKFILE_FileInit(LPBIGBLOCKFILE This, HANDLE hFile); | |
| static BOOL BIGBLOCKFILE_MemInit(LPBIGBLOCKFILE This, ILockBytes* plkbyt); | |
| /* Note that this evaluates a and b multiple times, so don't | |
| * pass expressions with side effects. */ | |
| #define ROUND_UP(a, b) ((((a) + (b) - 1)/(b))*(b)) | |
| /*********************************************************** | |
| * Blockbits functions. | |
| */ | |
| static inline BOOL BIGBLOCKFILE_TestBit(const BlockBits *bb, | |
| unsigned int index) | |
| { | |
| unsigned int array_index = index / (CHAR_BIT * sizeof(unsigned int)); | |
| unsigned int bit_index = index % (CHAR_BIT * sizeof(unsigned int)); | |
| return bb->bits[array_index] & (1 << bit_index); | |
| } | |
| static inline void BIGBLOCKFILE_SetBit(BlockBits *bb, unsigned int index) | |
| { | |
| unsigned int array_index = index / (CHAR_BIT * sizeof(unsigned int)); | |
| unsigned int bit_index = index % (CHAR_BIT * sizeof(unsigned int)); | |
| bb->bits[array_index] |= (1 << bit_index); | |
| } | |
| static inline void BIGBLOCKFILE_ClearBit(BlockBits *bb, unsigned int index) | |
| { | |
| unsigned int array_index = index / (CHAR_BIT * sizeof(unsigned int)); | |
| unsigned int bit_index = index % (CHAR_BIT * sizeof(unsigned int)); | |
| bb->bits[array_index] &= ~(1 << bit_index); | |
| } | |
| static inline void BIGBLOCKFILE_Zero(BlockBits *bb) | |
| { | |
| memset(bb->bits, 0, sizeof(bb->bits)); | |
| } | |
| /****************************************************************************** | |
| * BIGBLOCKFILE_Construct | |
| * | |
| * Construct a big block file. Create the file mapping object. | |
| * Create the read only mapped pages list, the writable mapped page list | |
| * and the blocks in use list. | |
| */ | |
| BigBlockFile * BIGBLOCKFILE_Construct( | |
| HANDLE hFile, | |
| ILockBytes* pLkByt, | |
| DWORD openFlags, | |
| ULONG blocksize, | |
| BOOL fileBased) | |
| { | |
| LPBIGBLOCKFILE This; | |
| This = (LPBIGBLOCKFILE)HeapAlloc(GetProcessHeap(), 0, sizeof(BigBlockFile)); | |
| if (This == NULL) | |
| return NULL; | |
| This->fileBased = fileBased; | |
| This->flProtect = BIGBLOCKFILE_GetProtectMode(openFlags); | |
| This->blocksize = blocksize; | |
| This->maplist = NULL; | |
| This->victimhead = NULL; | |
| This->victimtail = NULL; | |
| This->num_victim_pages = 0; | |
| if (This->fileBased) | |
| { | |
| if (!BIGBLOCKFILE_FileInit(This, hFile)) | |
| { | |
| HeapFree(GetProcessHeap(), 0, This); | |
| return NULL; | |
| } | |
| } | |
| else | |
| { | |
| if (!BIGBLOCKFILE_MemInit(This, pLkByt)) | |
| { | |
| HeapFree(GetProcessHeap(), 0, This); | |
| return NULL; | |
| } | |
| } | |
| return This; | |
| } | |
| /****************************************************************************** | |
| * BIGBLOCKFILE_FileInit | |
| * | |
| * Initialize a big block object supported by a file. | |
| */ | |
| static BOOL BIGBLOCKFILE_FileInit(LPBIGBLOCKFILE This, HANDLE hFile) | |
| { | |
| This->pLkbyt = NULL; | |
| This->hbytearray = 0; | |
| This->pbytearray = NULL; | |
| This->hfile = hFile; | |
| if (This->hfile == INVALID_HANDLE_VALUE) | |
| return FALSE; | |
| /* create the file mapping object | |
| */ | |
| This->hfilemap = CreateFileMappingA(This->hfile, | |
| NULL, | |
| This->flProtect, | |
| 0, 0, | |
| NULL); | |
| if (This->hfilemap == NULL) | |
| { | |
| CloseHandle(This->hfile); | |
| return FALSE; | |
| } | |
| This->filesize.s.LowPart = GetFileSize(This->hfile, | |
| &This->filesize.s.HighPart); | |
| This->maplist = NULL; | |
| TRACE("file len %lu\n", This->filesize.s.LowPart); | |
| return TRUE; | |
| } | |
| /****************************************************************************** | |
| * BIGBLOCKFILE_MemInit | |
| * | |
| * Initialize a big block object supported by an ILockBytes on HGLOABL. | |
| */ | |
| static BOOL BIGBLOCKFILE_MemInit(LPBIGBLOCKFILE This, ILockBytes* plkbyt) | |
| { | |
| This->hfile = 0; | |
| This->hfilemap = NULL; | |
| /* | |
| * Retrieve the handle to the byte array from the LockByte object. | |
| */ | |
| if (GetHGlobalFromILockBytes(plkbyt, &(This->hbytearray)) != S_OK) | |
| { | |
| FIXME("May not be an ILockBytes on HGLOBAL\n"); | |
| return FALSE; | |
| } | |
| This->pLkbyt = plkbyt; | |
| /* | |
| * Increment the reference count of the ILockByte object since | |
| * we're keeping a reference to it. | |
| */ | |
| ILockBytes_AddRef(This->pLkbyt); | |
| This->filesize.s.LowPart = GlobalSize(This->hbytearray); | |
| This->filesize.s.HighPart = 0; | |
| This->pbytearray = GlobalLock(This->hbytearray); | |
| TRACE("mem on %p len %lu\n", This->pbytearray, This->filesize.s.LowPart); | |
| return TRUE; | |
| } | |
| /****************************************************************************** | |
| * BIGBLOCKFILE_Destructor | |
| * | |
| * Destructor. Clean up, free memory. | |
| */ | |
| void BIGBLOCKFILE_Destructor( | |
| LPBIGBLOCKFILE This) | |
| { | |
| BIGBLOCKFILE_FreeAllMappedPages(This); | |
| if (This->fileBased) | |
| { | |
| CloseHandle(This->hfilemap); | |
| CloseHandle(This->hfile); | |
| } | |
| else | |
| { | |
| GlobalUnlock(This->hbytearray); | |
| ILockBytes_Release(This->pLkbyt); | |
| } | |
| /* destroy this | |
| */ | |
| HeapFree(GetProcessHeap(), 0, This); | |
| } | |
| /****************************************************************************** | |
| * BIGBLOCKFILE_GetROBigBlock | |
| * | |
| * Returns the specified block in read only mode. | |
| * Will return NULL if the block doesn't exists. | |
| */ | |
| void* BIGBLOCKFILE_GetROBigBlock( | |
| LPBIGBLOCKFILE This, | |
| ULONG index) | |
| { | |
| /* | |
| * block index starts at -1 | |
| * translate to zero based index | |
| */ | |
| if (index == 0xffffffff) | |
| index = 0; | |
| else | |
| index++; | |
| /* | |
| * validate the block index | |
| * | |
| */ | |
| if (This->blocksize * (index + 1) | |
| > ROUND_UP(This->filesize.s.LowPart, This->blocksize)) | |
| { | |
| TRACE("out of range %lu vs %lu\n", This->blocksize * (index + 1), | |
| This->filesize.s.LowPart); | |
| return NULL; | |
| } | |
| return BIGBLOCKFILE_GetBigBlockPointer(This, index, FILE_MAP_READ); | |
| } | |
| /****************************************************************************** | |
| * BIGBLOCKFILE_GetBigBlock | |
| * | |
| * Returns the specified block. | |
| * Will grow the file if necessary. | |
| */ | |
| void* BIGBLOCKFILE_GetBigBlock(LPBIGBLOCKFILE This, ULONG index) | |
| { | |
| /* | |
| * block index starts at -1 | |
| * translate to zero based index | |
| */ | |
| if (index == 0xffffffff) | |
| index = 0; | |
| else | |
| index++; | |
| /* | |
| * make sure that the block physically exists | |
| */ | |
| if ((This->blocksize * (index + 1)) > This->filesize.s.LowPart) | |
| { | |
| ULARGE_INTEGER newSize; | |
| newSize.s.HighPart = 0; | |
| newSize.s.LowPart = This->blocksize * (index + 1); | |
| BIGBLOCKFILE_SetSize(This, newSize); | |
| } | |
| return BIGBLOCKFILE_GetBigBlockPointer(This, index, FILE_MAP_WRITE); | |
| } | |
| /****************************************************************************** | |
| * BIGBLOCKFILE_ReleaseBigBlock | |
| * | |
| * Releases the specified block. | |
| */ | |
| void BIGBLOCKFILE_ReleaseBigBlock(LPBIGBLOCKFILE This, void *pBlock) | |
| { | |
| MappedPage *page; | |
| if (pBlock == NULL) | |
| return; | |
| page = BIGBLOCKFILE_GetPageFromPointer(This, pBlock); | |
| if (page == NULL) | |
| return; | |
| BIGBLOCKFILE_ReleaseMappedPage(This, page); | |
| } | |
| /****************************************************************************** | |
| * BIGBLOCKFILE_SetSize | |
| * | |
| * Sets the size of the file. | |
| * | |
| */ | |
| void BIGBLOCKFILE_SetSize(LPBIGBLOCKFILE This, ULARGE_INTEGER newSize) | |
| { | |
| if (This->filesize.s.LowPart == newSize.s.LowPart) | |
| return; | |
| TRACE("from %lu to %lu\n", This->filesize.s.LowPart, newSize.s.LowPart); | |
| /* | |
| * unmap all views, must be done before call to SetEndFile | |
| */ | |
| BIGBLOCKFILE_UnmapAllMappedPages(This); | |
| if (This->fileBased) | |
| { | |
| char buf[10]; | |
| /* | |
| * close file-mapping object, must be done before call to SetEndFile | |
| */ | |
| CloseHandle(This->hfilemap); | |
| This->hfilemap = NULL; | |
| /* | |
| * BEGIN HACK | |
| * This fixes a bug when saving through smbfs. | |
| * smbmount a Windows shared directory, save a structured storage file | |
| * to that dir: crash. | |
| * | |
| * The problem is that the SetFilePointer-SetEndOfFile combo below | |
| * doesn't always succeed. The file is not grown. It seems like the | |
| * operation is cached. By doing the WriteFile, the file is actually | |
| * grown on disk. | |
| * This hack is only needed when saving to smbfs. | |
| */ | |
| memset(buf, '0', 10); | |
| SetFilePointer(This->hfile, newSize.s.LowPart, NULL, FILE_BEGIN); | |
| WriteFile(This->hfile, buf, 10, NULL, NULL); | |
| /* | |
| * END HACK | |
| */ | |
| /* | |
| * set the new end of file | |
| */ | |
| SetFilePointer(This->hfile, newSize.s.LowPart, NULL, FILE_BEGIN); | |
| SetEndOfFile(This->hfile); | |
| /* | |
| * re-create the file mapping object | |
| */ | |
| This->hfilemap = CreateFileMappingA(This->hfile, | |
| NULL, | |
| This->flProtect, | |
| 0, 0, | |
| NULL); | |
| } | |
| else | |
| { | |
| GlobalUnlock(This->hbytearray); | |
| /* | |
| * Resize the byte array object. | |
| */ | |
| ILockBytes_SetSize(This->pLkbyt, newSize); | |
| /* | |
| * Re-acquire the handle, it may have changed. | |
| */ | |
| GetHGlobalFromILockBytes(This->pLkbyt, &This->hbytearray); | |
| This->pbytearray = GlobalLock(This->hbytearray); | |
| } | |
| This->filesize.s.LowPart = newSize.s.LowPart; | |
| This->filesize.s.HighPart = newSize.s.HighPart; | |
| BIGBLOCKFILE_RemapAllMappedPages(This); | |
| } | |
| /****************************************************************************** | |
| * BIGBLOCKFILE_GetSize | |
| * | |
| * Returns the size of the file. | |
| * | |
| */ | |
| ULARGE_INTEGER BIGBLOCKFILE_GetSize(LPBIGBLOCKFILE This) | |
| { | |
| return This->filesize; | |
| } | |
| /****************************************************************************** | |
| * BIGBLOCKFILE_AccessCheck [PRIVATE] | |
| * | |
| * block_index is the index within the page. | |
| */ | |
| static BOOL BIGBLOCKFILE_AccessCheck(MappedPage *page, ULONG block_index, | |
| DWORD desired_access) | |
| { | |
| assert(block_index < BLOCKS_PER_PAGE); | |
| if (desired_access == FILE_MAP_READ) | |
| { | |
| if (BIGBLOCKFILE_TestBit(&page->writable_blocks, block_index)) | |
| return FALSE; | |
| BIGBLOCKFILE_SetBit(&page->readable_blocks, block_index); | |
| } | |
| else | |
| { | |
| assert(desired_access == FILE_MAP_WRITE); | |
| if (BIGBLOCKFILE_TestBit(&page->readable_blocks, block_index)) | |
| return FALSE; | |
| BIGBLOCKFILE_SetBit(&page->writable_blocks, block_index); | |
| } | |
| return TRUE; | |
| } | |
| /****************************************************************************** | |
| * BIGBLOCKFILE_GetBigBlockPointer [PRIVATE] | |
| * | |
| * Returns a pointer to the specified block. | |
| */ | |
| static void* BIGBLOCKFILE_GetBigBlockPointer( | |
| LPBIGBLOCKFILE This, | |
| ULONG block_index, | |
| DWORD desired_access) | |
| { | |
| DWORD page_index = block_index / BLOCKS_PER_PAGE; | |
| DWORD block_on_page = block_index % BLOCKS_PER_PAGE; | |
| MappedPage *page = BIGBLOCKFILE_GetMappedView(This, page_index); | |
| if (!page || !page->lpBytes) return NULL; | |
| if (!BIGBLOCKFILE_AccessCheck(page, block_on_page, desired_access)) | |
| { | |
| BIGBLOCKFILE_ReleaseMappedPage(This, page); | |
| return NULL; | |
| } | |
| return (LPBYTE)page->lpBytes + (block_on_page * This->blocksize); | |
| } | |
| /****************************************************************************** | |
| * BIGBLOCKFILE_GetMappedPageFromPointer [PRIVATE] | |
| * | |
| * pBlock is a pointer to a block on a page. | |
| * The page has to be on the in-use list. (As oppsed to the victim list.) | |
| * | |
| * Does not increment the usage count. | |
| */ | |
| static MappedPage *BIGBLOCKFILE_GetPageFromPointer(LPBIGBLOCKFILE This, | |
| void *pBlock) | |
| { | |
| MappedPage *page; | |
| for (page = This->maplist; page != NULL; page = page->next) | |
| { | |
| if ((LPBYTE)pBlock >= (LPBYTE)page->lpBytes | |
| && (LPBYTE)pBlock <= (LPBYTE)page->lpBytes + PAGE_SIZE) | |
| break; | |
| } | |
| return page; | |
| } | |
| /****************************************************************************** | |
| * BIGBLOCKFILE_FindPageInList [PRIVATE] | |
| * | |
| */ | |
| static MappedPage *BIGBLOCKFILE_FindPageInList(MappedPage *head, | |
| ULONG page_index) | |
| { | |
| for (; head != NULL; head = head->next) | |
| { | |
| if (head->page_index == page_index) | |
| { | |
| InterlockedIncrement(&head->refcnt); | |
| break; | |
| } | |
| } | |
| return head; | |
| } | |
| static void BIGBLOCKFILE_UnlinkPage(MappedPage *page) | |
| { | |
| if (page->next) page->next->prev = page->prev; | |
| if (page->prev) page->prev->next = page->next; | |
| } | |
| static void BIGBLOCKFILE_LinkHeadPage(MappedPage **head, MappedPage *page) | |
| { | |
| if (*head) (*head)->prev = page; | |
| page->next = *head; | |
| page->prev = NULL; | |
| *head = page; | |
| } | |
| /****************************************************************************** | |
| * BIGBLOCKFILE_GetMappedView [PRIVATE] | |
| * | |
| * Gets the page requested if it is already mapped. | |
| * If it's not already mapped, this method will map it | |
| */ | |
| static void * BIGBLOCKFILE_GetMappedView( | |
| LPBIGBLOCKFILE This, | |
| DWORD page_index) | |
| { | |
| MappedPage *page; | |
| page = BIGBLOCKFILE_FindPageInList(This->maplist, page_index); | |
| if (!page) | |
| { | |
| page = BIGBLOCKFILE_FindPageInList(This->victimhead, page_index); | |
| if (page) | |
| { | |
| This->num_victim_pages--; | |
| BIGBLOCKFILE_Zero(&page->readable_blocks); | |
| BIGBLOCKFILE_Zero(&page->writable_blocks); | |
| } | |
| } | |
| if (page) | |
| { | |
| /* If the page is not already at the head of the list, move | |
| * it there. (Also moves pages from victim to main list.) */ | |
| if (This->maplist != page) | |
| { | |
| if (This->victimhead == page) This->victimhead = page->next; | |
| if (This->victimtail == page) This->victimtail = page->prev; | |
| BIGBLOCKFILE_UnlinkPage(page); | |
| BIGBLOCKFILE_LinkHeadPage(&This->maplist, page); | |
| } | |
| return page; | |
| } | |
| page = BIGBLOCKFILE_CreatePage(This, page_index); | |
| if (!page) return NULL; | |
| BIGBLOCKFILE_LinkHeadPage(&This->maplist, page); | |
| return page; | |
| } | |
| static BOOL BIGBLOCKFILE_MapPage(LPBIGBLOCKFILE This, MappedPage *page) | |
| { | |
| DWORD lowoffset = PAGE_SIZE * page->page_index; | |
| if (This->fileBased) | |
| { | |
| DWORD numBytesToMap; | |
| DWORD desired_access; | |
| if (lowoffset + PAGE_SIZE > This->filesize.s.LowPart) | |
| numBytesToMap = This->filesize.s.LowPart - lowoffset; | |
| else | |
| numBytesToMap = PAGE_SIZE; | |
| if (This->flProtect == PAGE_READONLY) | |
| desired_access = FILE_MAP_READ; | |
| else | |
| desired_access = FILE_MAP_WRITE; | |
| page->lpBytes = MapViewOfFile(This->hfilemap, desired_access, 0, | |
| lowoffset, numBytesToMap); | |
| } | |
| else | |
| { | |
| page->lpBytes = (LPBYTE)This->pbytearray + lowoffset; | |
| } | |
| TRACE("mapped page %lu to %p\n", page->page_index, page->lpBytes); | |
| return page->lpBytes != NULL; | |
| } | |
| static MappedPage *BIGBLOCKFILE_CreatePage(LPBIGBLOCKFILE This, | |
| ULONG page_index) | |
| { | |
| MappedPage *page; | |
| page = HeapAlloc(GetProcessHeap(), 0, sizeof(MappedPage)); | |
| if (page == NULL) | |
| return NULL; | |
| page->page_index = page_index; | |
| page->refcnt = 1; | |
| page->next = NULL; | |
| page->prev = NULL; | |
| BIGBLOCKFILE_MapPage(This, page); | |
| BIGBLOCKFILE_Zero(&page->readable_blocks); | |
| BIGBLOCKFILE_Zero(&page->writable_blocks); | |
| return page; | |
| } | |
| static void BIGBLOCKFILE_UnmapPage(LPBIGBLOCKFILE This, MappedPage *page) | |
| { | |
| TRACE("%ld at %p\n", page->page_index, page->lpBytes); | |
| if (page->refcnt > 0) | |
| ERR("unmapping inuse page %p\n", page->lpBytes); | |
| if (This->fileBased && page->lpBytes) | |
| UnmapViewOfFile(page->lpBytes); | |
| page->lpBytes = NULL; | |
| } | |
| static void BIGBLOCKFILE_DeletePage(LPBIGBLOCKFILE This, MappedPage *page) | |
| { | |
| BIGBLOCKFILE_UnmapPage(This, page); | |
| HeapFree(GetProcessHeap(), 0, page); | |
| } | |
| /****************************************************************************** | |
| * BIGBLOCKFILE_ReleaseMappedPage [PRIVATE] | |
| * | |
| * Decrements the reference count of the mapped page. | |
| */ | |
| static void BIGBLOCKFILE_ReleaseMappedPage( | |
| LPBIGBLOCKFILE This, | |
| MappedPage *page) | |
| { | |
| assert(This != NULL); | |
| assert(page != NULL); | |
| /* If the page is no longer refenced, move it to the victim list. | |
| * If the victim list is too long, kick somebody off. */ | |
| if (!InterlockedDecrement(&page->refcnt)) | |
| { | |
| if (This->maplist == page) This->maplist = page->next; | |
| BIGBLOCKFILE_UnlinkPage(page); | |
| if (MAX_VICTIM_PAGES > 0) | |
| { | |
| if (This->num_victim_pages >= MAX_VICTIM_PAGES) | |
| { | |
| MappedPage *victim = This->victimtail; | |
| if (victim) | |
| { | |
| This->victimtail = victim->prev; | |
| if (This->victimhead == victim) | |
| This->victimhead = victim->next; | |
| BIGBLOCKFILE_UnlinkPage(victim); | |
| BIGBLOCKFILE_DeletePage(This, victim); | |
| } | |
| } | |
| else This->num_victim_pages++; | |
| BIGBLOCKFILE_LinkHeadPage(&This->victimhead, page); | |
| if (This->victimtail == NULL) This->victimtail = page; | |
| } | |
| else | |
| BIGBLOCKFILE_DeletePage(This, page); | |
| } | |
| } | |
| static void BIGBLOCKFILE_DeleteList(LPBIGBLOCKFILE This, MappedPage *list) | |
| { | |
| while (list != NULL) | |
| { | |
| MappedPage *next = list->next; | |
| BIGBLOCKFILE_DeletePage(This, list); | |
| list = next; | |
| } | |
| } | |
| /****************************************************************************** | |
| * BIGBLOCKFILE_FreeAllMappedPages [PRIVATE] | |
| * | |
| * Unmap all currently mapped pages. | |
| * Empty mapped pages list. | |
| */ | |
| static void BIGBLOCKFILE_FreeAllMappedPages( | |
| LPBIGBLOCKFILE This) | |
| { | |
| BIGBLOCKFILE_DeleteList(This, This->maplist); | |
| BIGBLOCKFILE_DeleteList(This, This->victimhead); | |
| This->maplist = NULL; | |
| This->victimhead = NULL; | |
| This->victimtail = NULL; | |
| This->num_victim_pages = 0; | |
| } | |
| static void BIGBLOCKFILE_UnmapList(LPBIGBLOCKFILE This, MappedPage *list) | |
| { | |
| for (; list != NULL; list = list->next) | |
| { | |
| BIGBLOCKFILE_UnmapPage(This, list); | |
| } | |
| } | |
| static void BIGBLOCKFILE_UnmapAllMappedPages(LPBIGBLOCKFILE This) | |
| { | |
| BIGBLOCKFILE_UnmapList(This, This->maplist); | |
| BIGBLOCKFILE_UnmapList(This, This->victimhead); | |
| } | |
| static void BIGBLOCKFILE_RemapList(LPBIGBLOCKFILE This, MappedPage *list) | |
| { | |
| while (list != NULL) | |
| { | |
| MappedPage *next = list->next; | |
| if (list->page_index * PAGE_SIZE > This->filesize.s.LowPart) | |
| { | |
| TRACE("discarding %lu\n", list->page_index); | |
| /* page is entirely outside of the file, delete it */ | |
| BIGBLOCKFILE_UnlinkPage(list); | |
| BIGBLOCKFILE_DeletePage(This, list); | |
| } | |
| else | |
| { | |
| /* otherwise, remap it */ | |
| BIGBLOCKFILE_MapPage(This, list); | |
| } | |
| list = next; | |
| } | |
| } | |
| static void BIGBLOCKFILE_RemapAllMappedPages(LPBIGBLOCKFILE This) | |
| { | |
| BIGBLOCKFILE_RemapList(This, This->maplist); | |
| BIGBLOCKFILE_RemapList(This, This->victimhead); | |
| } | |
| /**************************************************************************** | |
| * BIGBLOCKFILE_GetProtectMode | |
| * | |
| * This function will return a protection mode flag for a file-mapping object | |
| * from the open flags of a file. | |
| */ | |
| static DWORD BIGBLOCKFILE_GetProtectMode(DWORD openFlags) | |
| { | |
| if (openFlags & (STGM_WRITE | STGM_READWRITE)) | |
| return PAGE_READWRITE; | |
| else | |
| return PAGE_READONLY; | |
| } |