wine/dlls/ole32/stg_bigblockfile.c
Aric Stewart 838eb4037f ole32: Rework storage with blockfiles to properly use ILockBytes interfaces.
Rework storage to make all its interactions with the underlying data go 
though ReadAt and WriteAt to allow for an ILockBytes underlying layer.
Implement ReadAt and WriteAt in stg_bigblockfile.c for blockfiles.
2007-03-20 12:36:45 +01:00

921 lines
24 KiB
C

/******************************************************************************
*
* 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.)
*/
struct MappedPage
{
MappedPage *next;
MappedPage *prev;
DWORD page_index;
DWORD mapped_bytes;
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 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 = 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;
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_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 = 0;
/*
* 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.u.LowPart = GlobalSize(This->hbytearray);
This->filesize.u.HighPart = 0;
This->pbytearray = GlobalLock(This->hbytearray);
TRACE("mem on %p len %u\n", This->pbytearray, This->filesize.u.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_EnsureExists
*
* Grows the file if necessary to make sure the block is valid.
*/
void BIGBLOCKFILE_EnsureExists(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.u.LowPart)
{
ULARGE_INTEGER newSize;
newSize.u.HighPart = 0;
newSize.u.LowPart = This->blocksize * (index + 1);
BIGBLOCKFILE_SetSize(This, newSize);
}
}
/******************************************************************************
* BIGBLOCKFILE_SetSize
*
* Sets the size of the file.
*
*/
void BIGBLOCKFILE_SetSize(LPBIGBLOCKFILE This, ULARGE_INTEGER newSize)
{
if (This->filesize.u.LowPart == newSize.u.LowPart)
return;
TRACE("from %u to %u\n", This->filesize.u.LowPart, newSize.u.LowPart);
/*
* unmap all views, must be done before call to SetEndFile
*/
BIGBLOCKFILE_UnmapAllMappedPages(This);
if (This->fileBased)
{
LARGE_INTEGER newpos;
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);
}
}
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.u.LowPart = newSize.u.LowPart;
This->filesize.u.HighPart = newSize.u.HighPart;
BIGBLOCKFILE_RemapAllMappedPages(This);
}
/******************************************************************************
* BIGBLOCKFILE_GetSize
*
* Returns the size of the file.
*
*/
ULARGE_INTEGER BIGBLOCKFILE_GetSize(LPBIGBLOCKFILE This)
{
return This->filesize;
}
/******************************************************************************
* 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( !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;
}
else
{
page->lpBytes = (LPBYTE)This->pbytearray + lowoffset;
page->mapped_bytes = PAGE_SIZE;
}
TRACE("mapped page %u 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;
if (!BIGBLOCKFILE_MapPage(This, page))
{
HeapFree(GetProcessHeap(),0,page);
return NULL;
}
BIGBLOCKFILE_Zero(&page->readable_blocks);
BIGBLOCKFILE_Zero(&page->writable_blocks);
return page;
}
static void BIGBLOCKFILE_UnmapPage(LPBIGBLOCKFILE This, MappedPage *page)
{
TRACE("%d 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.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 WINAPI 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 enviroment */
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 WINAPI 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 enviroment */
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_index, offset_in_page, bytes_to_page, bytes_left);
if (page->mapped_bytes < bytes_to_page)
{
ERR("Not enough bytes mapped to the page. This should never happen\n");
rc = E_FAIL;
break;
}
if (!page)
{
ERR("Unable to get a page to write. 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 = (LPBYTE)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;
}
HRESULT BIGBLOCKFILE_ReadAt(LPBIGBLOCKFILE 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);
}
HRESULT BIGBLOCKFILE_WriteAt(LPBIGBLOCKFILE This, ULARGE_INTEGER offset,
void* buffer, const 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);
}