📄 stg_bigblockfile.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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;
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 = 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 %lu\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 %lu\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_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.u.LowPart, This->blocksize))
{
TRACE("out of range %lu vs %lu\n", This->blocksize * (index + 1),
This->filesize.u.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.u.LowPart)
{
ULARGE_INTEGER newSize;
newSize.u.HighPart = 0;
newSize.u.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.u.LowPart == newSize.u.LowPart)
return;
TRACE("from %lu to %lu\n", This->filesize.u.LowPart, newSize.u.LowPart);
/*
* unmap all views, must be done before call to SetEndFile
*/
BIGBLOCKFILE_UnmapAllMappedPages(This);
if (This->fileBased)
{
char buf[10];
DWORD w;
/*
* close file-mapping object, must be done before call to SetEndFile
*/
if( This->hfilemap )
CloseHandle(This->hfilemap);
This->hfilemap = 0;
/*
* BEGIN HACK
* This fixes a bug when saving through smbfs.
* smbmount a Windows shared directory, save a structured storage file
* to that dir: crash.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -