| /****************************************************************************** |
| * |
| * 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 |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include <assert.h> |
| #include <stdlib.h> |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <limits.h> |
| |
| #define COBJMACROS |
| #define NONAMELESSUNION |
| #define NONAMELESSSTRUCT |
| |
| #include "windef.h" |
| #include "winbase.h" |
| #include "winuser.h" |
| #include "winerror.h" |
| #include "objbase.h" |
| #include "ole2.h" |
| |
| #include "storage32.h" |
| |
| #include "wine/debug.h" |
| |
| WINE_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.) |
| */ |
| |
| typedef struct MappedPage MappedPage; |
| struct MappedPage |
| { |
| MappedPage *next; |
| MappedPage *prev; |
| |
| DWORD page_index; |
| DWORD mapped_bytes; |
| LPVOID lpBytes; |
| LONG refcnt; |
| |
| BlockBits readable_blocks; |
| BlockBits writable_blocks; |
| }; |
| |
| struct BigBlockFile |
| { |
| BOOL fileBased; |
| ULARGE_INTEGER filesize; |
| ULONG blocksize; |
| HANDLE hfile; |
| HANDLE hfilemap; |
| DWORD flProtect; |
| MappedPage *maplist; |
| MappedPage *victimhead, *victimtail; |
| ULONG num_victim_pages; |
| ILockBytes *pLkbyt; |
| }; |
| |
| /*********************************************************** |
| * Prototypes for private methods |
| */ |
| |
| /* 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_FileInit |
| * |
| * Initialize a big block object supported by a file. |
| */ |
| static BOOL BIGBLOCKFILE_FileInit(LPBIGBLOCKFILE This, HANDLE hFile) |
| { |
| This->pLkbyt = NULL; |
| This->hfile = hFile; |
| |
| if (This->hfile == INVALID_HANDLE_VALUE) |
| return FALSE; |
| |
| This->filesize.u.LowPart = GetFileSize(This->hfile, |
| &This->filesize.u.HighPart); |
| |
| if( This->filesize.u.LowPart || This->filesize.u.HighPart ) |
| { |
| /* create the file mapping object |
| */ |
| This->hfilemap = CreateFileMappingA(This->hfile, |
| NULL, |
| This->flProtect, |
| 0, 0, |
| NULL); |
| |
| if (!This->hfilemap) |
| { |
| CloseHandle(This->hfile); |
| return FALSE; |
| } |
| } |
| else |
| This->hfilemap = NULL; |
| |
| This->maplist = NULL; |
| |
| TRACE("file len %u\n", This->filesize.u.LowPart); |
| |
| return TRUE; |
| } |
| |
| /****************************************************************************** |
| * BIGBLOCKFILE_LockBytesInit |
| * |
| * Initialize a big block object supported by an ILockBytes. |
| */ |
| static BOOL BIGBLOCKFILE_LockBytesInit(LPBIGBLOCKFILE This, ILockBytes* plkbyt) |
| { |
| This->hfile = 0; |
| This->hfilemap = 0; |
| This->pLkbyt = plkbyt; |
| ILockBytes_AddRef(This->pLkbyt); |
| |
| /* We'll get the size directly with ILockBytes_Stat */ |
| This->filesize.QuadPart = 0; |
| |
| TRACE("ILockBytes %p\n", This->pLkbyt); |
| return TRUE; |
| } |
| |
| /****************************************************************************** |
| * 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; |
| } |
| |
| static BOOL BIGBLOCKFILE_MapPage(BigBlockFile *This, MappedPage *page) |
| { |
| DWORD lowoffset = PAGE_SIZE * page->page_index; |
| DWORD numBytesToMap; |
| DWORD desired_access; |
| |
| assert(This->fileBased); |
| |
| if( !This->hfilemap ) |
| return FALSE; |
| |
| if (lowoffset + PAGE_SIZE > This->filesize.u.LowPart) |
| numBytesToMap = This->filesize.u.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); |
| page->mapped_bytes = numBytesToMap; |
| |
| TRACE("mapped page %u to %p\n", page->page_index, page->lpBytes); |
| |
| return page->lpBytes != NULL; |
| } |
| |
| |
| static MappedPage *BIGBLOCKFILE_CreatePage(BigBlockFile *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; |
| |
| if (!BIGBLOCKFILE_MapPage(This, page)) |
| { |
| HeapFree(GetProcessHeap(),0,page); |
| return NULL; |
| } |
| |
| BIGBLOCKFILE_Zero(&page->readable_blocks); |
| BIGBLOCKFILE_Zero(&page->writable_blocks); |
| |
| return 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 void BIGBLOCKFILE_UnmapPage(LPBIGBLOCKFILE This, MappedPage *page) |
| { |
| TRACE("%d at %p\n", page->page_index, page->lpBytes); |
| |
| assert(This->fileBased); |
| |
| if (page->refcnt > 0) |
| ERR("unmapping inuse page %p\n", page->lpBytes); |
| |
| if (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.u.LowPart) |
| { |
| TRACE("discarding %u\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) |
| { |
| switch(STGM_ACCESS_MODE(openFlags)) |
| { |
| case STGM_WRITE: |
| case STGM_READWRITE: |
| return PAGE_READWRITE; |
| } |
| return PAGE_READONLY; |
| } |
| |
| |
| /* ILockByte Interfaces */ |
| |
| /****************************************************************************** |
| * This method is part of the ILockBytes interface. |
| * |
| * It reads a block of information from the byte array at the specified |
| * offset. |
| * |
| * See the documentation of ILockBytes for more info. |
| */ |
| static HRESULT ImplBIGBLOCKFILE_ReadAt( |
| BigBlockFile* const This, |
| ULARGE_INTEGER ulOffset, /* [in] */ |
| void* pv, /* [length_is][size_is][out] */ |
| ULONG cb, /* [in] */ |
| ULONG* pcbRead) /* [out] */ |
| { |
| ULONG first_page = ulOffset.u.LowPart / PAGE_SIZE; |
| ULONG offset_in_page = ulOffset.u.LowPart % PAGE_SIZE; |
| ULONG bytes_left = cb; |
| ULONG page_index = first_page; |
| ULONG bytes_from_page; |
| LPVOID writePtr = pv; |
| |
| HRESULT rc = S_OK; |
| |
| TRACE("(%p)-> %i %p %i %p\n",This, ulOffset.u.LowPart, pv, cb, pcbRead); |
| |
| /* verify a sane environment */ |
| if (!This) return E_FAIL; |
| |
| if (offset_in_page + bytes_left > PAGE_SIZE) |
| bytes_from_page = PAGE_SIZE - offset_in_page; |
| else |
| bytes_from_page = bytes_left; |
| |
| if (pcbRead) |
| *pcbRead = 0; |
| |
| while (bytes_left) |
| { |
| LPBYTE readPtr; |
| BOOL eof = FALSE; |
| MappedPage *page = BIGBLOCKFILE_GetMappedView(This, page_index); |
| |
| if (!page || !page->lpBytes) |
| { |
| rc = STG_E_READFAULT; |
| break; |
| } |
| |
| TRACE("page %i, offset %u, bytes_from_page %u, bytes_left %u\n", |
| page->page_index, offset_in_page, bytes_from_page, bytes_left); |
| |
| if (page->mapped_bytes < bytes_from_page) |
| { |
| eof = TRUE; |
| bytes_from_page = page->mapped_bytes; |
| } |
| |
| readPtr = (BYTE*)page->lpBytes + offset_in_page; |
| memcpy(writePtr,readPtr,bytes_from_page); |
| BIGBLOCKFILE_ReleaseMappedPage(This, page); |
| |
| if (pcbRead) |
| *pcbRead += bytes_from_page; |
| bytes_left -= bytes_from_page; |
| |
| if (bytes_left && !eof) |
| { |
| writePtr = (LPBYTE)writePtr + bytes_from_page; |
| page_index ++; |
| offset_in_page = 0; |
| if (bytes_left > PAGE_SIZE) |
| bytes_from_page = PAGE_SIZE; |
| else |
| bytes_from_page = bytes_left; |
| } |
| if (eof) |
| { |
| rc = STG_E_READFAULT; |
| break; |
| } |
| } |
| |
| TRACE("finished\n"); |
| return rc; |
| } |
| |
| /****************************************************************************** |
| * This method is part of the ILockBytes interface. |
| * |
| * It writes the specified bytes at the specified offset. |
| * position. If the file is too small, it will be resized. |
| * |
| * See the documentation of ILockBytes for more info. |
| */ |
| static HRESULT ImplBIGBLOCKFILE_WriteAt( |
| BigBlockFile* const This, |
| ULARGE_INTEGER ulOffset, /* [in] */ |
| const void* pv, /* [size_is][in] */ |
| ULONG cb, /* [in] */ |
| ULONG* pcbWritten) /* [out] */ |
| { |
| ULONG size_needed = ulOffset.u.LowPart + cb; |
| ULONG first_page = ulOffset.u.LowPart / PAGE_SIZE; |
| ULONG offset_in_page = ulOffset.u.LowPart % PAGE_SIZE; |
| ULONG bytes_left = cb; |
| ULONG page_index = first_page; |
| ULONG bytes_to_page; |
| LPCVOID readPtr = pv; |
| |
| HRESULT rc = S_OK; |
| |
| TRACE("(%p)-> %i %p %i %p\n",This, ulOffset.u.LowPart, pv, cb, pcbWritten); |
| |
| /* verify a sane environment */ |
| if (!This) return E_FAIL; |
| |
| if (This->flProtect != PAGE_READWRITE) |
| return STG_E_ACCESSDENIED; |
| |
| if (size_needed > This->filesize.u.LowPart) |
| { |
| ULARGE_INTEGER newSize; |
| newSize.u.HighPart = 0; |
| newSize.u.LowPart = size_needed; |
| BIGBLOCKFILE_SetSize(This, newSize); |
| } |
| |
| if (offset_in_page + bytes_left > PAGE_SIZE) |
| bytes_to_page = PAGE_SIZE - offset_in_page; |
| else |
| bytes_to_page = bytes_left; |
| |
| if (pcbWritten) |
| *pcbWritten = 0; |
| |
| while (bytes_left) |
| { |
| LPBYTE writePtr; |
| MappedPage *page = BIGBLOCKFILE_GetMappedView(This, page_index); |
| |
| TRACE("page %i, offset %u, bytes_to_page %u, bytes_left %u\n", |
| page ? page->page_index : 0, offset_in_page, bytes_to_page, bytes_left); |
| |
| if (!page) |
| { |
| ERR("Unable to get a page to write. This should never happen\n"); |
| rc = E_FAIL; |
| break; |
| } |
| |
| if (page->mapped_bytes < bytes_to_page) |
| { |
| ERR("Not enough bytes mapped to the page. This should never happen\n"); |
| rc = E_FAIL; |
| break; |
| } |
| |
| writePtr = (BYTE*)page->lpBytes + offset_in_page; |
| memcpy(writePtr,readPtr,bytes_to_page); |
| BIGBLOCKFILE_ReleaseMappedPage(This, page); |
| |
| if (pcbWritten) |
| *pcbWritten += bytes_to_page; |
| bytes_left -= bytes_to_page; |
| |
| if (bytes_left) |
| { |
| readPtr = (const BYTE *)readPtr + bytes_to_page; |
| page_index ++; |
| offset_in_page = 0; |
| if (bytes_left > PAGE_SIZE) |
| bytes_to_page = PAGE_SIZE; |
| else |
| bytes_to_page = bytes_left; |
| } |
| } |
| |
| return rc; |
| } |
| |
| /****************************************************************************** |
| * 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) |
| { |
| BigBlockFile *This; |
| |
| This = 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_LockBytesInit(This, pLkByt)) |
| { |
| HeapFree(GetProcessHeap(), 0, This); |
| return NULL; |
| } |
| } |
| |
| return This; |
| } |
| |
| /****************************************************************************** |
| * BIGBLOCKFILE_Destructor |
| * |
| * Destructor. Clean up, free memory. |
| */ |
| void BIGBLOCKFILE_Destructor(BigBlockFile *This) |
| { |
| BIGBLOCKFILE_FreeAllMappedPages(This); |
| |
| if (This->fileBased) |
| { |
| CloseHandle(This->hfilemap); |
| CloseHandle(This->hfile); |
| } |
| else |
| { |
| ILockBytes_Release(This->pLkbyt); |
| } |
| |
| HeapFree(GetProcessHeap(), 0, This); |
| } |
| |
| /****************************************************************************** |
| * BIGBLOCKFILE_ReadAt |
| */ |
| HRESULT BIGBLOCKFILE_ReadAt(BigBlockFile *This, ULARGE_INTEGER offset, |
| void* buffer, ULONG size, ULONG* bytesRead) |
| { |
| if (This->fileBased) |
| return ImplBIGBLOCKFILE_ReadAt(This,offset,buffer,size,bytesRead); |
| else |
| return ILockBytes_ReadAt(This->pLkbyt,offset,buffer,size,bytesRead); |
| } |
| |
| /****************************************************************************** |
| * BIGBLOCKFILE_WriteAt |
| */ |
| HRESULT BIGBLOCKFILE_WriteAt(BigBlockFile *This, ULARGE_INTEGER offset, |
| const void* buffer, ULONG size, ULONG* bytesRead) |
| { |
| if (This->fileBased) |
| return ImplBIGBLOCKFILE_WriteAt(This,offset,buffer,size,bytesRead); |
| else |
| return ILockBytes_WriteAt(This->pLkbyt,offset,buffer,size,bytesRead); |
| } |
| |
| /****************************************************************************** |
| * BIGBLOCKFILE_SetSize |
| * |
| * Sets the size of the file. |
| * |
| */ |
| HRESULT BIGBLOCKFILE_SetSize(BigBlockFile *This, ULARGE_INTEGER newSize) |
| { |
| HRESULT hr = S_OK; |
| LARGE_INTEGER newpos; |
| |
| if (!This->fileBased) |
| return ILockBytes_SetSize(This->pLkbyt, newSize); |
| |
| if (This->filesize.u.LowPart == newSize.u.LowPart) |
| return hr; |
| |
| TRACE("from %u to %u\n", This->filesize.u.LowPart, newSize.u.LowPart); |
| |
| /* |
| * Unmap all views, must be done before call to SetEndFile. |
| * |
| * Just ditch the victim list because there is no guarantee we will need them |
| * and it is not worth the performance hit to unmap and remap them all. |
| */ |
| BIGBLOCKFILE_DeleteList(This, This->victimhead); |
| This->victimhead = NULL; |
| This->victimtail = NULL; |
| This->num_victim_pages = 0; |
| |
| BIGBLOCKFILE_UnmapAllMappedPages(This); |
| |
| newpos.QuadPart = newSize.QuadPart; |
| if (SetFilePointerEx(This->hfile, newpos, NULL, FILE_BEGIN)) |
| { |
| if( This->hfilemap ) CloseHandle(This->hfilemap); |
| |
| SetEndOfFile(This->hfile); |
| |
| /* re-create the file mapping object */ |
| This->hfilemap = CreateFileMappingA(This->hfile, NULL, This->flProtect, |
| 0, 0, NULL); |
| } |
| |
| This->filesize = newSize; |
| BIGBLOCKFILE_RemapAllMappedPages(This); |
| return hr; |
| } |
| |
| /****************************************************************************** |
| * BIGBLOCKFILE_GetSize |
| * |
| * Gets the size of the file. |
| * |
| */ |
| static HRESULT BIGBLOCKFILE_GetSize(BigBlockFile *This, ULARGE_INTEGER *size) |
| { |
| HRESULT hr = S_OK; |
| if(This->fileBased) |
| *size = This->filesize; |
| else |
| { |
| STATSTG stat; |
| hr = ILockBytes_Stat(This->pLkbyt, &stat, STATFLAG_NONAME); |
| if(SUCCEEDED(hr)) *size = stat.cbSize; |
| } |
| return hr; |
| } |
| |
| /****************************************************************************** |
| * BIGBLOCKFILE_EnsureExists |
| * |
| * Grows the file if necessary to make sure the block is valid. |
| */ |
| HRESULT BIGBLOCKFILE_EnsureExists(BigBlockFile *This, ULONG index) |
| { |
| ULARGE_INTEGER size; |
| HRESULT hr; |
| |
| /* Block index starts at -1 translate to zero based index */ |
| if (index == 0xffffffff) |
| index = 0; |
| else |
| index++; |
| |
| hr = BIGBLOCKFILE_GetSize(This, &size); |
| if(FAILED(hr)) return hr; |
| |
| /* make sure that the block physically exists */ |
| if ((This->blocksize * (index + 1)) > size.QuadPart) |
| { |
| ULARGE_INTEGER newSize; |
| |
| newSize.QuadPart = This->blocksize * (index + 1); |
| hr = BIGBLOCKFILE_SetSize(This, newSize); |
| } |
| return hr; |
| } |