mirror of
git://source.winehq.org/git/wine.git
synced 2024-10-11 19:19:01 +00:00
b0c61294ba
casts of all methods put in the jump table. I find it too risky to use such macros because nothing guarantees that you actually put the right method at the right place or that the prototype is right.
4946 lines
127 KiB
C
4946 lines
127 KiB
C
/*
|
|
* Compound Storage (32 bit version)
|
|
* Storage implementation
|
|
*
|
|
* This file contains the compound file implementation
|
|
* of the storage interface.
|
|
*
|
|
* Copyright 1999 Francis Beaudet
|
|
* Copyright 1999 Sylvain St-Germain
|
|
* Copyright 1999 Thuy Nguyen
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "windows.h"
|
|
#include "winerror.h"
|
|
#include "file.h"
|
|
#include "ole.h"
|
|
#include "ole2.h"
|
|
#include "objbase.h"
|
|
#include "crtdll.h"
|
|
#include "tchar.h"
|
|
#include "heap.h"
|
|
|
|
#include "winnt.h"
|
|
#include "winbase.h"
|
|
#include "debug.h"
|
|
|
|
#include "storage32.h"
|
|
|
|
#define FILE_BEGIN 0
|
|
|
|
static const char rootPropertyName[] = "Root Entry";
|
|
|
|
/***********************************************************************
|
|
* Forward declaration of internal functions used by the method DestroyElement
|
|
*/
|
|
static HRESULT deleteStorageProperty(
|
|
Storage32Impl *parentStorage,
|
|
OLECHAR32 *propertyToDeleteName);
|
|
|
|
static HRESULT deleteStreamProperty(
|
|
Storage32Impl *parentStorage,
|
|
ULONG foundPropertyIndexToDelete,
|
|
StgProperty propertyToDelete);
|
|
|
|
static HRESULT findPlaceholder(
|
|
Storage32Impl *storage,
|
|
ULONG propertyIndexToStore,
|
|
ULONG storagePropertyIndex,
|
|
INT32 typeOfRelation);
|
|
|
|
static HRESULT adjustPropertyChain(
|
|
Storage32Impl *This,
|
|
StgProperty propertyToDelete,
|
|
StgProperty parentProperty,
|
|
ULONG parentPropertyId,
|
|
INT32 typeOfRelation);
|
|
|
|
/***********************************************************************
|
|
* Declaration of the functions used to manipulate StgProperty
|
|
*/
|
|
|
|
static ULONG getFreeProperty(
|
|
Storage32Impl *storage);
|
|
|
|
static void updatePropertyChain(
|
|
Storage32Impl *storage,
|
|
ULONG newPropertyIndex,
|
|
StgProperty newProperty);
|
|
|
|
static LONG propertyNameCmp(
|
|
OLECHAR32 *newProperty,
|
|
OLECHAR32 *currentProperty);
|
|
|
|
|
|
/***********************************************************************
|
|
* Declaration of miscellaneous functions...
|
|
*/
|
|
static HRESULT validateSTGM(DWORD stgmValue);
|
|
|
|
static DWORD GetShareModeFromSTGM(DWORD stgm);
|
|
static DWORD GetAccessModeFromSTGM(DWORD stgm);
|
|
static DWORD GetCreationModeFromSTGM(DWORD stgm);
|
|
|
|
/*
|
|
* Virtual function table for the IStorage32Impl class.
|
|
*/
|
|
static ICOM_VTABLE(IStorage32) Storage32Impl_Vtbl =
|
|
{
|
|
Storage32BaseImpl_QueryInterface,
|
|
Storage32BaseImpl_AddRef,
|
|
Storage32BaseImpl_Release,
|
|
Storage32BaseImpl_CreateStream,
|
|
Storage32BaseImpl_OpenStream,
|
|
Storage32Impl_CreateStorage,
|
|
Storage32BaseImpl_OpenStorage,
|
|
Storage32Impl_CopyTo,
|
|
Storage32Impl_MoveElementTo,
|
|
Storage32Impl_Commit,
|
|
Storage32Impl_Revert,
|
|
Storage32BaseImpl_EnumElements,
|
|
Storage32Impl_DestroyElement,
|
|
Storage32BaseImpl_RenameElement,
|
|
Storage32Impl_SetElementTimes,
|
|
Storage32BaseImpl_SetClass,
|
|
Storage32Impl_SetStateBits,
|
|
Storage32BaseImpl_Stat
|
|
};
|
|
|
|
/*
|
|
* Virtual function table for the Storage32InternalImpl class.
|
|
*/
|
|
static ICOM_VTABLE(IStorage32) Storage32InternalImpl_Vtbl =
|
|
{
|
|
Storage32BaseImpl_QueryInterface,
|
|
Storage32BaseImpl_AddRef,
|
|
Storage32BaseImpl_Release,
|
|
Storage32BaseImpl_CreateStream,
|
|
Storage32BaseImpl_OpenStream,
|
|
Storage32Impl_CreateStorage,
|
|
Storage32BaseImpl_OpenStorage,
|
|
Storage32Impl_CopyTo,
|
|
Storage32Impl_MoveElementTo,
|
|
Storage32InternalImpl_Commit,
|
|
Storage32InternalImpl_Revert,
|
|
Storage32BaseImpl_EnumElements,
|
|
Storage32Impl_DestroyElement,
|
|
Storage32BaseImpl_RenameElement,
|
|
Storage32Impl_SetElementTimes,
|
|
Storage32BaseImpl_SetClass,
|
|
Storage32Impl_SetStateBits,
|
|
Storage32BaseImpl_Stat
|
|
};
|
|
|
|
/*
|
|
* Virtual function table for the IEnumSTATSTGImpl class.
|
|
*/
|
|
static ICOM_VTABLE(IEnumSTATSTG) IEnumSTATSTGImpl_Vtbl =
|
|
{
|
|
IEnumSTATSTGImpl_QueryInterface,
|
|
IEnumSTATSTGImpl_AddRef,
|
|
IEnumSTATSTGImpl_Release,
|
|
IEnumSTATSTGImpl_Next,
|
|
IEnumSTATSTGImpl_Skip,
|
|
IEnumSTATSTGImpl_Reset,
|
|
IEnumSTATSTGImpl_Clone
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/************************************************************************
|
|
** Storage32BaseImpl implementatiion
|
|
*/
|
|
|
|
/************************************************************************
|
|
* Storage32BaseImpl_QueryInterface (IUnknown)
|
|
*
|
|
* This method implements the common QueryInterface for all IStorage32
|
|
* implementations contained in this file.
|
|
*
|
|
* See Windows documentation for more details on IUnknown methods.
|
|
*/
|
|
HRESULT WINAPI Storage32BaseImpl_QueryInterface(
|
|
IStorage32* iface,
|
|
REFIID riid,
|
|
void** ppvObject)
|
|
{
|
|
ICOM_THIS(Storage32BaseImpl,iface);
|
|
/*
|
|
* Perform a sanity check on the parameters.
|
|
*/
|
|
if ( (This==0) || (ppvObject==0) )
|
|
return E_INVALIDARG;
|
|
|
|
/*
|
|
* Initialize the return parameter.
|
|
*/
|
|
*ppvObject = 0;
|
|
|
|
/*
|
|
* Compare the riid with the interface IDs implemented by this object.
|
|
*/
|
|
if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
|
|
{
|
|
*ppvObject = (IStorage32*)This;
|
|
}
|
|
else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
|
|
{
|
|
*ppvObject = (IStorage32*)This;
|
|
}
|
|
|
|
/*
|
|
* Check that we obtained an interface.
|
|
*/
|
|
if ((*ppvObject)==0)
|
|
return E_NOINTERFACE;
|
|
|
|
/*
|
|
* Query Interface always increases the reference count by one when it is
|
|
* successful
|
|
*/
|
|
Storage32BaseImpl_AddRef(iface);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Storage32BaseImpl_AddRef (IUnknown)
|
|
*
|
|
* This method implements the common AddRef for all IStorage32
|
|
* implementations contained in this file.
|
|
*
|
|
* See Windows documentation for more details on IUnknown methods.
|
|
*/
|
|
ULONG WINAPI Storage32BaseImpl_AddRef(
|
|
IStorage32* iface)
|
|
{
|
|
ICOM_THIS(Storage32BaseImpl,iface);
|
|
This->ref++;
|
|
|
|
return This->ref;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Storage32BaseImpl_Release (IUnknown)
|
|
*
|
|
* This method implements the common Release for all IStorage32
|
|
* implementations contained in this file.
|
|
*
|
|
* See Windows documentation for more details on IUnknown methods.
|
|
*/
|
|
ULONG WINAPI Storage32BaseImpl_Release(
|
|
IStorage32* iface)
|
|
{
|
|
ICOM_THIS(Storage32BaseImpl,iface);
|
|
/*
|
|
* Decrease the reference count on this object.
|
|
*/
|
|
This->ref--;
|
|
|
|
/*
|
|
* If the reference count goes down to 0, perform suicide.
|
|
*/
|
|
if (This->ref==0)
|
|
{
|
|
/*
|
|
* Since we are using a system of base-classes, we want to call the
|
|
* destructor of the appropriate derived class. To do this, we are
|
|
* using virtual functions to implement the destructor.
|
|
*/
|
|
This->v_destructor(This);
|
|
|
|
return 0;
|
|
}
|
|
|
|
return This->ref;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Storage32BaseImpl_OpenStream (IStorage)
|
|
*
|
|
* This method will open the specified stream object from the current storage.
|
|
*
|
|
* See Windows documentation for more details on IStorage methods.
|
|
*/
|
|
HRESULT WINAPI Storage32BaseImpl_OpenStream(
|
|
IStorage32* iface,
|
|
const OLECHAR32* pwcsName, /* [string][in] */
|
|
void* reserved1, /* [unique][in] */
|
|
DWORD grfMode, /* [in] */
|
|
DWORD reserved2, /* [in] */
|
|
IStream32** ppstm) /* [out] */
|
|
{
|
|
ICOM_THIS(Storage32BaseImpl,iface);
|
|
IEnumSTATSTGImpl* propertyEnumeration;
|
|
StgStreamImpl* newStream;
|
|
StgProperty currentProperty;
|
|
ULONG foundPropertyIndex;
|
|
|
|
/*
|
|
* Perform a sanity check on the parameters.
|
|
*/
|
|
if ( (pwcsName==NULL) || (ppstm==0) )
|
|
return E_INVALIDARG;
|
|
|
|
/*
|
|
* Initialize the out parameter
|
|
*/
|
|
*ppstm = 0;
|
|
|
|
/*
|
|
* Validate the STGM flags
|
|
*/
|
|
if ( FAILED( validateSTGM(grfMode) ))
|
|
return STG_E_INVALIDFLAG;
|
|
|
|
/*
|
|
* As documented.
|
|
*/
|
|
if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
|
|
(grfMode & STGM_DELETEONRELEASE) ||
|
|
(grfMode & STGM_TRANSACTED) )
|
|
return STG_E_INVALIDFUNCTION;
|
|
|
|
/*
|
|
* Create a property enumeration to search the properties
|
|
*/
|
|
propertyEnumeration = IEnumSTATSTGImpl_Construct(
|
|
This->ancestorStorage,
|
|
This->rootPropertySetIndex);
|
|
|
|
/*
|
|
* Search the enumeration for the property with the given name
|
|
*/
|
|
foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
|
|
propertyEnumeration,
|
|
pwcsName,
|
|
¤tProperty);
|
|
|
|
/*
|
|
* Delete the property enumeration since we don't need it anymore
|
|
*/
|
|
IEnumSTATSTGImpl_Destroy(propertyEnumeration);
|
|
|
|
/*
|
|
* If it was found, construct the stream object and return a pointer to it.
|
|
*/
|
|
if ( (foundPropertyIndex!=PROPERTY_NULL) &&
|
|
(currentProperty.propertyType==PROPTYPE_STREAM) )
|
|
{
|
|
newStream = StgStreamImpl_Construct(This, foundPropertyIndex);
|
|
|
|
if (newStream!=0)
|
|
{
|
|
*ppstm = (IStream32*)newStream;
|
|
|
|
/*
|
|
* Since we are returning a pointer to the interface, we have to
|
|
* nail down the reference.
|
|
*/
|
|
StgStreamImpl_AddRef(*ppstm);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
return STG_E_FILENOTFOUND;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Storage32BaseImpl_OpenStorage (IStorage)
|
|
*
|
|
* This method will open a new storage object from the current storage.
|
|
*
|
|
* See Windows documentation for more details on IStorage methods.
|
|
*/
|
|
HRESULT WINAPI Storage32BaseImpl_OpenStorage(
|
|
IStorage32* iface,
|
|
const OLECHAR32* pwcsName, /* [string][unique][in] */
|
|
IStorage32* pstgPriority, /* [unique][in] */
|
|
DWORD grfMode, /* [in] */
|
|
SNB32 snbExclude, /* [unique][in] */
|
|
DWORD reserved, /* [in] */
|
|
IStorage32** ppstg) /* [out] */
|
|
{
|
|
ICOM_THIS(Storage32BaseImpl,iface);
|
|
Storage32InternalImpl* newStorage;
|
|
IEnumSTATSTGImpl* propertyEnumeration;
|
|
StgProperty currentProperty;
|
|
ULONG foundPropertyIndex;
|
|
|
|
/*
|
|
* Perform a sanity check on the parameters.
|
|
*/
|
|
if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
|
|
return E_INVALIDARG;
|
|
|
|
/*
|
|
* Validate the STGM flags
|
|
*/
|
|
if ( FAILED( validateSTGM(grfMode) ))
|
|
return STG_E_INVALIDFLAG;
|
|
|
|
/*
|
|
* As documented.
|
|
*/
|
|
if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
|
|
(grfMode & STGM_DELETEONRELEASE) ||
|
|
(grfMode & STGM_PRIORITY) )
|
|
return STG_E_INVALIDFUNCTION;
|
|
|
|
/*
|
|
* Initialize the out parameter
|
|
*/
|
|
*ppstg = 0;
|
|
|
|
/*
|
|
* Create a property enumeration to search the properties
|
|
*/
|
|
propertyEnumeration = IEnumSTATSTGImpl_Construct(
|
|
This->ancestorStorage,
|
|
This->rootPropertySetIndex);
|
|
|
|
/*
|
|
* Search the enumeration for the property with the given name
|
|
*/
|
|
foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
|
|
propertyEnumeration,
|
|
pwcsName,
|
|
¤tProperty);
|
|
|
|
/*
|
|
* Delete the property enumeration since we don't need it anymore
|
|
*/
|
|
IEnumSTATSTGImpl_Destroy(propertyEnumeration);
|
|
|
|
/*
|
|
* If it was found, construct the stream object and return a pointer to it.
|
|
*/
|
|
if ( (foundPropertyIndex!=PROPERTY_NULL) &&
|
|
(currentProperty.propertyType==PROPTYPE_STORAGE) )
|
|
{
|
|
/*
|
|
* Construct a new Storage object
|
|
*/
|
|
newStorage = Storage32InternalImpl_Construct(
|
|
This->ancestorStorage,
|
|
foundPropertyIndex);
|
|
|
|
if (newStorage != 0)
|
|
{
|
|
*ppstg = (IStorage32*)newStorage;
|
|
|
|
/*
|
|
* Since we are returning a pointer to the interface,
|
|
* we have to nail down the reference.
|
|
*/
|
|
Storage32BaseImpl_AddRef(*ppstg);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
return STG_E_INSUFFICIENTMEMORY;
|
|
}
|
|
|
|
return STG_E_FILENOTFOUND;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Storage32BaseImpl_EnumElements (IStorage)
|
|
*
|
|
* This method will create an enumerator object that can be used to
|
|
* retrieve informatino about all the properties in the storage object.
|
|
*
|
|
* See Windows documentation for more details on IStorage methods.
|
|
*/
|
|
HRESULT WINAPI Storage32BaseImpl_EnumElements(
|
|
IStorage32* iface,
|
|
DWORD reserved1, /* [in] */
|
|
void* reserved2, /* [size_is][unique][in] */
|
|
DWORD reserved3, /* [in] */
|
|
IEnumSTATSTG** ppenum) /* [out] */
|
|
{
|
|
ICOM_THIS(Storage32BaseImpl,iface);
|
|
IEnumSTATSTGImpl* newEnum;
|
|
|
|
/*
|
|
* Perform a sanity check on the parameters.
|
|
*/
|
|
if ( (This==0) || (ppenum==0))
|
|
return E_INVALIDARG;
|
|
|
|
/*
|
|
* Construct the enumerator.
|
|
*/
|
|
newEnum = IEnumSTATSTGImpl_Construct(
|
|
This->ancestorStorage,
|
|
This->rootPropertySetIndex);
|
|
|
|
if (newEnum!=0)
|
|
{
|
|
*ppenum = (IEnumSTATSTG*)newEnum;
|
|
|
|
/*
|
|
* Don't forget to nail down a reference to the new object before
|
|
* returning it.
|
|
*/
|
|
IEnumSTATSTGImpl_AddRef(*ppenum);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Storage32BaseImpl_Stat (IStorage)
|
|
*
|
|
* This method will retrieve information about this storage object.
|
|
*
|
|
* See Windows documentation for more details on IStorage methods.
|
|
*/
|
|
HRESULT WINAPI Storage32BaseImpl_Stat(
|
|
IStorage32* iface,
|
|
STATSTG* pstatstg, /* [out] */
|
|
DWORD grfStatFlag) /* [in] */
|
|
{
|
|
ICOM_THIS(Storage32BaseImpl,iface);
|
|
StgProperty curProperty;
|
|
BOOL32 readSucessful;
|
|
|
|
/*
|
|
* Perform a sanity check on the parameters.
|
|
*/
|
|
if ( (This==0) || (pstatstg==0))
|
|
return E_INVALIDARG;
|
|
|
|
/*
|
|
* Read the information from the property.
|
|
*/
|
|
readSucessful = Storage32Impl_ReadProperty(
|
|
This->ancestorStorage,
|
|
This->rootPropertySetIndex,
|
|
&curProperty);
|
|
|
|
if (readSucessful)
|
|
{
|
|
StorageUtl_CopyPropertyToSTATSTG(
|
|
pstatstg,
|
|
&curProperty,
|
|
grfStatFlag);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Storage32BaseImpl_RenameElement (IStorage)
|
|
*
|
|
* This method will rename the specified element.
|
|
*
|
|
* See Windows documentation for more details on IStorage methods.
|
|
*
|
|
* Implementation notes: The method used to rename consists of creating a clone
|
|
* of the deleted StgProperty object setting it with the new name and to
|
|
* perform a DestroyElement of the old StgProperty.
|
|
*/
|
|
HRESULT WINAPI Storage32BaseImpl_RenameElement(
|
|
IStorage32* iface,
|
|
const OLECHAR32* pwcsOldName, /* [in] */
|
|
const OLECHAR32* pwcsNewName) /* [in] */
|
|
{
|
|
ICOM_THIS(Storage32BaseImpl,iface);
|
|
IEnumSTATSTGImpl* propertyEnumeration;
|
|
StgProperty currentProperty;
|
|
ULONG foundPropertyIndex;
|
|
|
|
/*
|
|
* Create a property enumeration to search the properties
|
|
*/
|
|
propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
|
|
This->rootPropertySetIndex);
|
|
|
|
/*
|
|
* Search the enumeration for the new property name
|
|
*/
|
|
foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
|
|
pwcsNewName,
|
|
¤tProperty);
|
|
|
|
if (foundPropertyIndex != PROPERTY_NULL)
|
|
{
|
|
/*
|
|
* There is already a property with the new name
|
|
*/
|
|
IEnumSTATSTGImpl_Destroy(propertyEnumeration);
|
|
return STG_E_FILEALREADYEXISTS;
|
|
}
|
|
|
|
IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)propertyEnumeration);
|
|
|
|
/*
|
|
* Search the enumeration for the old property name
|
|
*/
|
|
foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
|
|
pwcsOldName,
|
|
¤tProperty);
|
|
|
|
/*
|
|
* Delete the property enumeration since we don't need it anymore
|
|
*/
|
|
IEnumSTATSTGImpl_Destroy(propertyEnumeration);
|
|
|
|
if (foundPropertyIndex != PROPERTY_NULL)
|
|
{
|
|
StgProperty renamedProperty;
|
|
ULONG renamedPropertyIndex;
|
|
|
|
/*
|
|
* Setup a new property for the renamed property
|
|
*/
|
|
renamedProperty.sizeOfNameString =
|
|
( lstrlen32W(pwcsNewName)+1 ) * sizeof(WCHAR);
|
|
|
|
if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
|
|
return STG_E_INVALIDNAME;
|
|
|
|
lstrcpy32W(renamedProperty.name, pwcsNewName);
|
|
|
|
renamedProperty.propertyType = currentProperty.propertyType;
|
|
renamedProperty.startingBlock = currentProperty.startingBlock;
|
|
renamedProperty.size.LowPart = currentProperty.size.LowPart;
|
|
renamedProperty.size.HighPart = currentProperty.size.HighPart;
|
|
|
|
renamedProperty.previousProperty = PROPERTY_NULL;
|
|
renamedProperty.nextProperty = PROPERTY_NULL;
|
|
|
|
/*
|
|
* Bring the dirProperty link in case it is a storage and in which
|
|
* case the renamed storage elements don't require to be reorganized.
|
|
*/
|
|
renamedProperty.dirProperty = currentProperty.dirProperty;
|
|
|
|
/* call CoFileTime to get the current time
|
|
renamedProperty.timeStampS1
|
|
renamedProperty.timeStampD1
|
|
renamedProperty.timeStampS2
|
|
renamedProperty.timeStampD2
|
|
renamedProperty.propertyUniqueID
|
|
*/
|
|
|
|
/*
|
|
* Obtain a free property in the property chain
|
|
*/
|
|
renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
|
|
|
|
/*
|
|
* Save the new property into the new property spot
|
|
*/
|
|
Storage32Impl_WriteProperty(
|
|
This->ancestorStorage,
|
|
renamedPropertyIndex,
|
|
&renamedProperty);
|
|
|
|
/*
|
|
* Find a spot in the property chain for our newly created property.
|
|
*/
|
|
updatePropertyChain(
|
|
(Storage32Impl*)This,
|
|
renamedPropertyIndex,
|
|
renamedProperty);
|
|
|
|
/*
|
|
* At this point the renamed property has been inserted in the tree,
|
|
* now, before to Destroy the old property we must zeroed it's dirProperty
|
|
* otherwise the DestroyProperty below will zap it all and we do not want
|
|
* this to happen.
|
|
* Also, we fake that the old property is a storage so the DestroyProperty
|
|
* will not do a SetSize(0) on the stream data.
|
|
*
|
|
* This means that we need to tweek the StgProperty if it is a stream or a
|
|
* non empty storage.
|
|
*/
|
|
currentProperty.dirProperty = PROPERTY_NULL;
|
|
currentProperty.propertyType = PROPTYPE_STORAGE;
|
|
Storage32Impl_WriteProperty(
|
|
This->ancestorStorage,
|
|
foundPropertyIndex,
|
|
¤tProperty);
|
|
|
|
/*
|
|
* Invoke Destroy to get rid of the ole property and automatically redo
|
|
* the linking of it's previous and next members...
|
|
*/
|
|
Storage32Impl_DestroyElement((IStorage32*)This->ancestorStorage, pwcsOldName);
|
|
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* There is no property with the old name
|
|
*/
|
|
return STG_E_FILENOTFOUND;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Storage32BaseImpl_CreateStream (IStorage)
|
|
*
|
|
* This method will create a stream object within this storage
|
|
*
|
|
* See Windows documentation for more details on IStorage methods.
|
|
*/
|
|
HRESULT WINAPI Storage32BaseImpl_CreateStream(
|
|
IStorage32* iface,
|
|
const OLECHAR32* pwcsName, /* [string][in] */
|
|
DWORD grfMode, /* [in] */
|
|
DWORD reserved1, /* [in] */
|
|
DWORD reserved2, /* [in] */
|
|
IStream32** ppstm) /* [out] */
|
|
{
|
|
ICOM_THIS(Storage32BaseImpl,iface);
|
|
IEnumSTATSTGImpl* propertyEnumeration;
|
|
StgStreamImpl* newStream;
|
|
StgProperty currentProperty, newStreamProperty;
|
|
ULONG foundPropertyIndex, newPropertyIndex;
|
|
|
|
/*
|
|
* Validate parameters
|
|
*/
|
|
if (ppstm == 0)
|
|
return STG_E_INVALIDPOINTER;
|
|
|
|
if (pwcsName == 0)
|
|
return STG_E_INVALIDNAME;
|
|
|
|
/*
|
|
* Validate the STGM flags
|
|
*/
|
|
if ( FAILED( validateSTGM(grfMode) ))
|
|
return STG_E_INVALIDFLAG;
|
|
|
|
/*
|
|
* As documented.
|
|
*/
|
|
if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
|
|
(grfMode & STGM_DELETEONRELEASE) ||
|
|
(grfMode & STGM_TRANSACTED) )
|
|
return STG_E_INVALIDFUNCTION;
|
|
|
|
/*
|
|
* Initialize the out parameter
|
|
*/
|
|
*ppstm = 0;
|
|
|
|
/*
|
|
* Create a property enumeration to search the properties
|
|
*/
|
|
propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
|
|
This->rootPropertySetIndex);
|
|
|
|
foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
|
|
pwcsName,
|
|
¤tProperty);
|
|
|
|
IEnumSTATSTGImpl_Destroy(propertyEnumeration);
|
|
|
|
if (foundPropertyIndex != PROPERTY_NULL)
|
|
{
|
|
/*
|
|
* An element with this name already exists
|
|
*/
|
|
if (grfMode & STGM_CREATE)
|
|
Storage32Impl_DestroyElement((IStorage32*)This->ancestorStorage, pwcsName);
|
|
else
|
|
return STG_E_FILEALREADYEXISTS;
|
|
}
|
|
|
|
/*
|
|
* memset the empty property
|
|
*/
|
|
memset(&newStreamProperty, 0, sizeof(StgProperty));
|
|
|
|
newStreamProperty.sizeOfNameString =
|
|
( lstrlen32W(pwcsName)+1 ) * sizeof(WCHAR);
|
|
|
|
if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
|
|
return STG_E_INVALIDNAME;
|
|
|
|
lstrcpy32W(newStreamProperty.name, pwcsName);
|
|
|
|
newStreamProperty.propertyType = PROPTYPE_STREAM;
|
|
newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
|
|
newStreamProperty.size.LowPart = 0;
|
|
newStreamProperty.size.HighPart = 0;
|
|
|
|
newStreamProperty.previousProperty = PROPERTY_NULL;
|
|
newStreamProperty.nextProperty = PROPERTY_NULL;
|
|
newStreamProperty.dirProperty = PROPERTY_NULL;
|
|
|
|
/* call CoFileTime to get the current time
|
|
newStreamProperty.timeStampS1
|
|
newStreamProperty.timeStampD1
|
|
newStreamProperty.timeStampS2
|
|
newStreamProperty.timeStampD2
|
|
*/
|
|
|
|
/* newStreamProperty.propertyUniqueID */
|
|
|
|
/*
|
|
* Get a free property or create a new one
|
|
*/
|
|
newPropertyIndex = getFreeProperty(This->ancestorStorage);
|
|
|
|
/*
|
|
* Save the new property into the new property spot
|
|
*/
|
|
Storage32Impl_WriteProperty(
|
|
This->ancestorStorage,
|
|
newPropertyIndex,
|
|
&newStreamProperty);
|
|
|
|
/*
|
|
* Find a spot in the property chain for our newly created property.
|
|
*/
|
|
updatePropertyChain(
|
|
(Storage32Impl*)This,
|
|
newPropertyIndex,
|
|
newStreamProperty);
|
|
|
|
/*
|
|
* Open the stream to return it.
|
|
*/
|
|
newStream = StgStreamImpl_Construct(This, newPropertyIndex);
|
|
|
|
if (newStream != 0)
|
|
{
|
|
*ppstm = (IStream32*)newStream;
|
|
|
|
/*
|
|
* Since we are returning a pointer to the interface, we have to nail down
|
|
* the reference.
|
|
*/
|
|
StgStreamImpl_AddRef(*ppstm);
|
|
}
|
|
else
|
|
{
|
|
return STG_E_INSUFFICIENTMEMORY;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Storage32BaseImpl_SetClass (IStorage)
|
|
*
|
|
* This method will write the specified CLSID in the property of this
|
|
* storage.
|
|
*
|
|
* See Windows documentation for more details on IStorage methods.
|
|
*/
|
|
HRESULT WINAPI Storage32BaseImpl_SetClass(
|
|
IStorage32* iface,
|
|
REFCLSID clsid) /* [in] */
|
|
{
|
|
ICOM_THIS(Storage32BaseImpl,iface);
|
|
HRESULT hRes = E_FAIL;
|
|
StgProperty curProperty;
|
|
BOOL32 success;
|
|
|
|
success = Storage32Impl_ReadProperty(This->ancestorStorage,
|
|
This->rootPropertySetIndex,
|
|
&curProperty);
|
|
if (success)
|
|
{
|
|
curProperty.propertyUniqueID = *clsid;
|
|
|
|
success = Storage32Impl_WriteProperty(This->ancestorStorage,
|
|
This->rootPropertySetIndex,
|
|
&curProperty);
|
|
if (success)
|
|
hRes = S_OK;
|
|
}
|
|
|
|
return hRes;
|
|
}
|
|
|
|
/************************************************************************
|
|
** Storage32Impl implementation
|
|
*/
|
|
|
|
/************************************************************************
|
|
* Storage32Impl_CreateStorage (IStorage)
|
|
*
|
|
* This method will create the storage object within the provided storage.
|
|
*
|
|
* See Windows documentation for more details on IStorage methods.
|
|
*/
|
|
HRESULT WINAPI Storage32Impl_CreateStorage(
|
|
IStorage32* iface,
|
|
const OLECHAR32 *pwcsName, /* [string][in] */
|
|
DWORD grfMode, /* [in] */
|
|
DWORD reserved1, /* [in] */
|
|
DWORD reserved2, /* [in] */
|
|
IStorage32 **ppstg) /* [out] */
|
|
{
|
|
Storage32Impl* const This=(Storage32Impl*)iface;
|
|
|
|
IEnumSTATSTGImpl *propertyEnumeration;
|
|
StgProperty currentProperty;
|
|
StgProperty newProperty;
|
|
ULONG foundPropertyIndex;
|
|
ULONG newPropertyIndex;
|
|
HRESULT hr;
|
|
|
|
/*
|
|
* Validate parameters
|
|
*/
|
|
if (ppstg == 0)
|
|
return STG_E_INVALIDPOINTER;
|
|
|
|
if (pwcsName == 0)
|
|
return STG_E_INVALIDNAME;
|
|
|
|
/*
|
|
* Validate the STGM flags
|
|
*/
|
|
if ( FAILED( validateSTGM(grfMode) ) ||
|
|
(grfMode & STGM_DELETEONRELEASE) )
|
|
return STG_E_INVALIDFLAG;
|
|
|
|
/*
|
|
* Initialize the out parameter
|
|
*/
|
|
*ppstg = 0;
|
|
|
|
/*
|
|
* Create a property enumeration and search the properties
|
|
*/
|
|
propertyEnumeration = IEnumSTATSTGImpl_Construct( This->ancestorStorage,
|
|
This->rootPropertySetIndex);
|
|
|
|
foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
|
|
pwcsName,
|
|
¤tProperty);
|
|
IEnumSTATSTGImpl_Destroy(propertyEnumeration);
|
|
|
|
if (foundPropertyIndex != PROPERTY_NULL)
|
|
{
|
|
/*
|
|
* An element with this name already exists
|
|
*/
|
|
if (grfMode & STGM_CREATE)
|
|
Storage32Impl_DestroyElement((IStorage32*)This->ancestorStorage, pwcsName);
|
|
else
|
|
return STG_E_FILEALREADYEXISTS;
|
|
}
|
|
|
|
/*
|
|
* memset the empty property
|
|
*/
|
|
memset(&newProperty, 0, sizeof(StgProperty));
|
|
|
|
newProperty.sizeOfNameString = (lstrlen32W(pwcsName)+1)*sizeof(WCHAR);
|
|
|
|
if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
|
|
return STG_E_INVALIDNAME;
|
|
|
|
lstrcpy32W(newProperty.name, pwcsName);
|
|
|
|
newProperty.propertyType = PROPTYPE_STORAGE;
|
|
newProperty.startingBlock = BLOCK_END_OF_CHAIN;
|
|
newProperty.size.LowPart = 0;
|
|
newProperty.size.HighPart = 0;
|
|
|
|
newProperty.previousProperty = PROPERTY_NULL;
|
|
newProperty.nextProperty = PROPERTY_NULL;
|
|
newProperty.dirProperty = PROPERTY_NULL;
|
|
|
|
/* call CoFileTime to get the current time
|
|
newProperty.timeStampS1
|
|
newProperty.timeStampD1
|
|
newProperty.timeStampS2
|
|
newProperty.timeStampD2
|
|
*/
|
|
|
|
/* newStorageProperty.propertyUniqueID */
|
|
|
|
/*
|
|
* Obtain a free property in the property chain
|
|
*/
|
|
newPropertyIndex = getFreeProperty(This->ancestorStorage);
|
|
|
|
/*
|
|
* Save the new property into the new property spot
|
|
*/
|
|
Storage32Impl_WriteProperty(
|
|
This->ancestorStorage,
|
|
newPropertyIndex,
|
|
&newProperty);
|
|
|
|
/*
|
|
* Find a spot in the property chain for our newly created property.
|
|
*/
|
|
updatePropertyChain(
|
|
This,
|
|
newPropertyIndex,
|
|
newProperty);
|
|
|
|
/*
|
|
* Open it to get a pointer to return.
|
|
*/
|
|
hr = Storage32BaseImpl_OpenStorage(
|
|
iface,
|
|
(OLECHAR32*)pwcsName,
|
|
0,
|
|
grfMode,
|
|
0,
|
|
0,
|
|
ppstg);
|
|
|
|
if( (hr != S_OK) || (*ppstg == NULL))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
*
|
|
* Internal Method
|
|
*
|
|
* Get a free property or create a new one.
|
|
*/
|
|
static ULONG getFreeProperty(
|
|
Storage32Impl *storage)
|
|
{
|
|
ULONG currentPropertyIndex = 0;
|
|
ULONG newPropertyIndex = PROPERTY_NULL;
|
|
BOOL32 readSucessful = TRUE;
|
|
StgProperty currentProperty;
|
|
|
|
do
|
|
{
|
|
/*
|
|
* Start by reading the root property
|
|
*/
|
|
readSucessful = Storage32Impl_ReadProperty(storage->ancestorStorage,
|
|
currentPropertyIndex,
|
|
¤tProperty);
|
|
if (readSucessful)
|
|
{
|
|
if (currentProperty.sizeOfNameString == 0)
|
|
{
|
|
/*
|
|
* The property existis and is available, we found it.
|
|
*/
|
|
newPropertyIndex = currentPropertyIndex;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* We exhausted the property list, we will create more space below
|
|
*/
|
|
newPropertyIndex = currentPropertyIndex;
|
|
}
|
|
currentPropertyIndex++;
|
|
|
|
} while (newPropertyIndex == PROPERTY_NULL);
|
|
|
|
/*
|
|
* grow the property chain
|
|
*/
|
|
if (! readSucessful)
|
|
{
|
|
StgProperty emptyProperty;
|
|
ULARGE_INTEGER newSize;
|
|
ULONG propertyIndex;
|
|
ULONG lastProperty = 0;
|
|
ULONG blockCount = 0;
|
|
|
|
/*
|
|
* obtain the new count of property blocks
|
|
*/
|
|
blockCount = BlockChainStream_GetCount(
|
|
storage->ancestorStorage->rootBlockChain)+1;
|
|
|
|
/*
|
|
* initialize the size used by the property stream
|
|
*/
|
|
newSize.HighPart = 0;
|
|
newSize.LowPart = storage->bigBlockSize * blockCount;
|
|
|
|
/*
|
|
* add a property block to the property chain
|
|
*/
|
|
BlockChainStream_SetSize(storage->ancestorStorage->rootBlockChain, newSize);
|
|
|
|
/*
|
|
* memset the empty property in order to initialize the unused newly
|
|
* created property
|
|
*/
|
|
memset(&emptyProperty, 0, sizeof(StgProperty));
|
|
|
|
/*
|
|
* initialize them
|
|
*/
|
|
lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
|
|
|
|
for(
|
|
propertyIndex = newPropertyIndex;
|
|
propertyIndex < lastProperty;
|
|
propertyIndex++)
|
|
{
|
|
Storage32Impl_WriteProperty(
|
|
storage->ancestorStorage,
|
|
propertyIndex,
|
|
&emptyProperty);
|
|
}
|
|
}
|
|
|
|
return newPropertyIndex;
|
|
}
|
|
|
|
/****************************************************************************
|
|
*
|
|
* Internal Method
|
|
*
|
|
* Case insensitive comparaison of StgProperty.name by first considering
|
|
* their size.
|
|
*
|
|
* Returns <0 when newPrpoerty < currentProperty
|
|
* >0 when newPrpoerty > currentProperty
|
|
* 0 when newPrpoerty == currentProperty
|
|
*/
|
|
static LONG propertyNameCmp(
|
|
OLECHAR32 *newProperty,
|
|
OLECHAR32 *currentProperty)
|
|
{
|
|
LONG sizeOfNew = (lstrlen32W(newProperty) +1) * sizeof(WCHAR);
|
|
LONG sizeOfCur = (lstrlen32W(currentProperty)+1) * sizeof(WCHAR);
|
|
LONG diff = sizeOfNew - sizeOfCur;
|
|
|
|
if (diff == 0)
|
|
{
|
|
/*
|
|
* We compare the string themselves only when they are of the same lenght
|
|
*/
|
|
WCHAR wsnew[PROPERTY_NAME_MAX_LEN];
|
|
WCHAR wscur[PROPERTY_NAME_MAX_LEN];
|
|
|
|
diff = lstrcmp32W( (LPCWSTR)CRTDLL__wcsupr(
|
|
lstrcpyn32W(wsnew, newProperty, sizeOfNew)),
|
|
(LPCWSTR)CRTDLL__wcsupr(
|
|
lstrcpyn32W(wscur, currentProperty, sizeOfCur)));
|
|
}
|
|
|
|
return diff;
|
|
}
|
|
|
|
/****************************************************************************
|
|
*
|
|
* Internal Method
|
|
*
|
|
* Properly link this new element in the property chain.
|
|
*/
|
|
static void updatePropertyChain(
|
|
Storage32Impl *storage,
|
|
ULONG newPropertyIndex,
|
|
StgProperty newProperty)
|
|
{
|
|
StgProperty currentProperty;
|
|
|
|
/*
|
|
* Read the root property
|
|
*/
|
|
Storage32Impl_ReadProperty(storage->ancestorStorage,
|
|
storage->rootPropertySetIndex,
|
|
¤tProperty);
|
|
|
|
if (currentProperty.dirProperty != PROPERTY_NULL)
|
|
{
|
|
/*
|
|
* The root storage contains some element, therefore, start the research
|
|
* for the appropriate location.
|
|
*/
|
|
BOOL32 found = 0;
|
|
ULONG current, next, previous, currentPropertyId;
|
|
|
|
/*
|
|
* Keep the StgProperty sequence number of the storage first property
|
|
*/
|
|
currentPropertyId = currentProperty.dirProperty;
|
|
|
|
/*
|
|
* Read
|
|
*/
|
|
Storage32Impl_ReadProperty(storage->ancestorStorage,
|
|
currentProperty.dirProperty,
|
|
¤tProperty);
|
|
|
|
previous = currentProperty.previousProperty;
|
|
next = currentProperty.nextProperty;
|
|
current = currentPropertyId;
|
|
|
|
while (found == 0)
|
|
{
|
|
LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
|
|
|
|
if (diff < 0)
|
|
{
|
|
if (previous != PROPERTY_NULL)
|
|
{
|
|
Storage32Impl_ReadProperty(storage->ancestorStorage,
|
|
previous,
|
|
¤tProperty);
|
|
current = previous;
|
|
}
|
|
else
|
|
{
|
|
currentProperty.previousProperty = newPropertyIndex;
|
|
Storage32Impl_WriteProperty(storage->ancestorStorage,
|
|
current,
|
|
¤tProperty);
|
|
found = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (next != PROPERTY_NULL)
|
|
{
|
|
Storage32Impl_ReadProperty(storage->ancestorStorage,
|
|
next,
|
|
¤tProperty);
|
|
current = next;
|
|
}
|
|
else
|
|
{
|
|
currentProperty.nextProperty = newPropertyIndex;
|
|
Storage32Impl_WriteProperty(storage->ancestorStorage,
|
|
current,
|
|
¤tProperty);
|
|
found = 1;
|
|
}
|
|
}
|
|
|
|
previous = currentProperty.previousProperty;
|
|
next = currentProperty.nextProperty;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The root storage is empty, link the new property to it's dir property
|
|
*/
|
|
currentProperty.dirProperty = newPropertyIndex;
|
|
Storage32Impl_WriteProperty(storage->ancestorStorage,
|
|
storage->rootPropertySetIndex,
|
|
¤tProperty);
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
* CopyTo (IStorage)
|
|
*/
|
|
HRESULT WINAPI Storage32Impl_CopyTo(
|
|
IStorage32* iface,
|
|
DWORD ciidExclude, /* [in] */
|
|
const IID *rgiidExclude,/* [size_is][unique][in] */
|
|
SNB32 snbExclude, /* [unique][in] */
|
|
IStorage32 *pstgDest) /* [unique][in] */
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* MoveElementTo (IStorage)
|
|
*/
|
|
HRESULT WINAPI Storage32Impl_MoveElementTo(
|
|
IStorage32* iface,
|
|
const OLECHAR32 *pwcsName, /* [string][in] */
|
|
IStorage32 *pstgDest, /* [unique][in] */
|
|
const OLECHAR32 *pwcsNewName,/* [string][in] */
|
|
DWORD grfFlags) /* [in] */
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* Commit (IStorage)
|
|
*/
|
|
HRESULT WINAPI Storage32Impl_Commit(
|
|
IStorage32* iface,
|
|
DWORD grfCommitFlags)/* [in] */
|
|
{
|
|
FIXME(ole, "(%ld): stub!\n", grfCommitFlags);
|
|
return S_OK;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* Revert (IStorage)
|
|
*/
|
|
HRESULT WINAPI Storage32Impl_Revert(
|
|
IStorage32* iface)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* DestroyElement (IStorage)
|
|
*
|
|
* Stategy: This implementation is build this way for simplicity not for speed.
|
|
* I always delete the top most element of the enumeration and adjust
|
|
* the deleted element pointer all the time. This takes longer to
|
|
* do but allow to reinvoke DestroyElement whenever we encounter a
|
|
* storage object. The optimisation reside in the usage of another
|
|
* enumeration stategy that would give all the leaves of a storage
|
|
* first. (postfix order)
|
|
*/
|
|
HRESULT WINAPI Storage32Impl_DestroyElement(
|
|
IStorage32* iface,
|
|
const OLECHAR32 *pwcsName)/* [string][in] */
|
|
{
|
|
Storage32Impl* const This=(Storage32Impl*)iface;
|
|
|
|
IEnumSTATSTGImpl* propertyEnumeration;
|
|
HRESULT hr = S_OK;
|
|
BOOL32 res;
|
|
StgProperty propertyToDelete;
|
|
StgProperty parentProperty;
|
|
ULONG foundPropertyIndexToDelete;
|
|
ULONG typeOfRelation;
|
|
ULONG parentPropertyId;
|
|
|
|
/*
|
|
* Perform a sanity check on the parameters.
|
|
*/
|
|
if (pwcsName==NULL)
|
|
return STG_E_INVALIDPOINTER;
|
|
|
|
/*
|
|
* Create a property enumeration to search the property with the given name
|
|
*/
|
|
propertyEnumeration = IEnumSTATSTGImpl_Construct(
|
|
This->ancestorStorage,
|
|
This->rootPropertySetIndex);
|
|
|
|
foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
|
|
propertyEnumeration,
|
|
pwcsName,
|
|
&propertyToDelete);
|
|
|
|
IEnumSTATSTGImpl_Destroy(propertyEnumeration);
|
|
|
|
if ( foundPropertyIndexToDelete == PROPERTY_NULL )
|
|
{
|
|
return STG_E_FILENOTFOUND;
|
|
}
|
|
|
|
/*
|
|
* Find the parent property of the property to delete (the one that
|
|
* link to it). If This->dirProperty == foundPropertyIndexToDelete,
|
|
* the parent is This. Otherwise, the parent is one of it's sibling...
|
|
*/
|
|
|
|
/*
|
|
* First, read This's StgProperty..
|
|
*/
|
|
res = Storage32Impl_ReadProperty(
|
|
This->ancestorStorage,
|
|
This->rootPropertySetIndex,
|
|
&parentProperty);
|
|
|
|
assert(res==TRUE);
|
|
|
|
/*
|
|
* Second, check to see if by any chance the actual storage (This) is not
|
|
* the parent of the property to delete... We never know...
|
|
*/
|
|
if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
|
|
{
|
|
/*
|
|
* Set data as it would have been done in the else part...
|
|
*/
|
|
typeOfRelation = PROPERTY_RELATION_DIR;
|
|
parentPropertyId = This->rootPropertySetIndex;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Create a property enumeration to search the parent properties, and
|
|
* delete it once done.
|
|
*/
|
|
IEnumSTATSTGImpl* propertyEnumeration2;
|
|
|
|
propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
|
|
This->ancestorStorage,
|
|
This->rootPropertySetIndex);
|
|
|
|
typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
|
|
propertyEnumeration2,
|
|
foundPropertyIndexToDelete,
|
|
&parentProperty,
|
|
&parentPropertyId);
|
|
|
|
IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
|
|
}
|
|
|
|
if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
|
|
{
|
|
hr = deleteStorageProperty(
|
|
This,
|
|
propertyToDelete.name);
|
|
}
|
|
else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
|
|
{
|
|
hr = deleteStreamProperty(
|
|
This,
|
|
foundPropertyIndexToDelete,
|
|
propertyToDelete);
|
|
}
|
|
|
|
if (hr!=S_OK)
|
|
return hr;
|
|
|
|
/*
|
|
* Adjust the property chain
|
|
*/
|
|
hr = adjustPropertyChain(
|
|
This,
|
|
propertyToDelete,
|
|
parentProperty,
|
|
parentPropertyId,
|
|
typeOfRelation);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/*********************************************************************
|
|
*
|
|
* Internal Method
|
|
*
|
|
* Perform the deletion of a complete storage node
|
|
*
|
|
*/
|
|
static HRESULT deleteStorageProperty(
|
|
Storage32Impl *parentStorage,
|
|
OLECHAR32 *propertyToDeleteName)
|
|
{
|
|
IEnumSTATSTG *elements = 0;
|
|
IStorage32 *childStorage = 0;
|
|
STATSTG currentElement;
|
|
HRESULT hr;
|
|
HRESULT destroyHr = S_OK;
|
|
|
|
/*
|
|
* Open the storage and enumerate it
|
|
*/
|
|
hr = Storage32BaseImpl_OpenStorage(
|
|
(IStorage32*)parentStorage,
|
|
propertyToDeleteName,
|
|
0,
|
|
STGM_SHARE_EXCLUSIVE,
|
|
0,
|
|
0,
|
|
&childStorage);
|
|
|
|
if (hr != S_OK)
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* Enumerate the elements
|
|
*/
|
|
IStorage32_EnumElements( childStorage, 0, 0, 0, &elements);
|
|
|
|
do
|
|
{
|
|
/*
|
|
* Obtain the next element
|
|
*/
|
|
hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
|
|
if (hr==S_OK)
|
|
{
|
|
destroyHr = Storage32Impl_DestroyElement(
|
|
(IStorage32*)childStorage,
|
|
(OLECHAR32*)currentElement.pwcsName);
|
|
|
|
CoTaskMemFree(currentElement.pwcsName);
|
|
}
|
|
|
|
/*
|
|
* We need to Reset the enumeration every time because we delete elements
|
|
* and the enumeration could be invalid
|
|
*/
|
|
IEnumSTATSTG_Reset(elements);
|
|
|
|
} while ((hr == S_OK) && (destroyHr == S_OK));
|
|
|
|
IStorage32_Release(childStorage);
|
|
IEnumSTATSTG_Release(elements);
|
|
|
|
return destroyHr;
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* Internal Method
|
|
*
|
|
* Perform the deletion of a stream node
|
|
*
|
|
*/
|
|
static HRESULT deleteStreamProperty(
|
|
Storage32Impl *parentStorage,
|
|
ULONG indexOfPropertyToDelete,
|
|
StgProperty propertyToDelete)
|
|
{
|
|
IStream32 *pis;
|
|
HRESULT hr;
|
|
ULARGE_INTEGER size;
|
|
|
|
size.HighPart = 0;
|
|
size.LowPart = 0;
|
|
|
|
hr = Storage32BaseImpl_OpenStream(
|
|
(IStorage32*)parentStorage,
|
|
(OLECHAR32*)propertyToDelete.name,
|
|
NULL,
|
|
STGM_SHARE_EXCLUSIVE,
|
|
0,
|
|
&pis);
|
|
|
|
if (hr!=S_OK)
|
|
{
|
|
return(hr);
|
|
}
|
|
|
|
/*
|
|
* Zap the stream
|
|
*/
|
|
hr = IStream32_SetSize(pis, size);
|
|
|
|
if(hr != S_OK)
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* Invalidate the property by zeroing it's name member.
|
|
*/
|
|
propertyToDelete.sizeOfNameString = 0;
|
|
|
|
/*
|
|
* Here we should re-read the property so we get the updated pointer
|
|
* but since we are here to zap it, I don't do it...
|
|
*/
|
|
|
|
Storage32Impl_WriteProperty(
|
|
parentStorage->ancestorStorage,
|
|
indexOfPropertyToDelete,
|
|
&propertyToDelete);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* Internal Method
|
|
*
|
|
* Finds a placeholder for the StgProperty within the Storage
|
|
*
|
|
*/
|
|
static HRESULT findPlaceholder(
|
|
Storage32Impl *storage,
|
|
ULONG propertyIndexToStore,
|
|
ULONG storePropertyIndex,
|
|
INT32 typeOfRelation)
|
|
{
|
|
StgProperty storeProperty;
|
|
HRESULT hr = S_OK;
|
|
BOOL32 res = TRUE;
|
|
|
|
/*
|
|
* Read the storage property
|
|
*/
|
|
res = Storage32Impl_ReadProperty(
|
|
storage->ancestorStorage,
|
|
storePropertyIndex,
|
|
&storeProperty);
|
|
|
|
if(! res)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
|
|
{
|
|
if (storeProperty.previousProperty != PROPERTY_NULL)
|
|
{
|
|
return findPlaceholder(
|
|
storage,
|
|
propertyIndexToStore,
|
|
storeProperty.previousProperty,
|
|
typeOfRelation);
|
|
}
|
|
else
|
|
{
|
|
storeProperty.previousProperty = propertyIndexToStore;
|
|
}
|
|
}
|
|
else if (typeOfRelation == PROPERTY_RELATION_NEXT)
|
|
{
|
|
if (storeProperty.nextProperty != PROPERTY_NULL)
|
|
{
|
|
return findPlaceholder(
|
|
storage,
|
|
propertyIndexToStore,
|
|
storeProperty.nextProperty,
|
|
typeOfRelation);
|
|
}
|
|
else
|
|
{
|
|
storeProperty.nextProperty = propertyIndexToStore;
|
|
}
|
|
}
|
|
else if (typeOfRelation == PROPERTY_RELATION_DIR)
|
|
{
|
|
if (storeProperty.dirProperty != PROPERTY_NULL)
|
|
{
|
|
return findPlaceholder(
|
|
storage,
|
|
propertyIndexToStore,
|
|
storeProperty.dirProperty,
|
|
typeOfRelation);
|
|
}
|
|
else
|
|
{
|
|
storeProperty.dirProperty = propertyIndexToStore;
|
|
}
|
|
}
|
|
|
|
hr = Storage32Impl_WriteProperty(
|
|
storage->ancestorStorage,
|
|
storePropertyIndex,
|
|
&storeProperty);
|
|
|
|
if(! hr)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Internal Method
|
|
*
|
|
* This method takes the previous and the next property link of a property
|
|
* to be deleted and find them a place in the Storage.
|
|
*/
|
|
static HRESULT adjustPropertyChain(
|
|
Storage32Impl *This,
|
|
StgProperty propertyToDelete,
|
|
StgProperty parentProperty,
|
|
ULONG parentPropertyId,
|
|
INT32 typeOfRelation)
|
|
{
|
|
ULONG newLinkProperty = PROPERTY_NULL;
|
|
BOOL32 needToFindAPlaceholder = FALSE;
|
|
ULONG storeNode = PROPERTY_NULL;
|
|
ULONG toStoreNode = PROPERTY_NULL;
|
|
INT32 relationType = 0;
|
|
HRESULT hr = S_OK;
|
|
BOOL32 res = TRUE;
|
|
|
|
if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
|
|
{
|
|
if (propertyToDelete.previousProperty != PROPERTY_NULL)
|
|
{
|
|
/*
|
|
* Set the parent previous to the property to delete previous
|
|
*/
|
|
newLinkProperty = propertyToDelete.previousProperty;
|
|
|
|
if (propertyToDelete.nextProperty != PROPERTY_NULL)
|
|
{
|
|
/*
|
|
* We also need to find a storage for the other link, setup variables
|
|
* to do this at the end...
|
|
*/
|
|
needToFindAPlaceholder = TRUE;
|
|
storeNode = propertyToDelete.previousProperty;
|
|
toStoreNode = propertyToDelete.nextProperty;
|
|
relationType = PROPERTY_RELATION_NEXT;
|
|
}
|
|
}
|
|
else if (propertyToDelete.nextProperty != PROPERTY_NULL)
|
|
{
|
|
/*
|
|
* Set the parent previous to the property to delete next
|
|
*/
|
|
newLinkProperty = propertyToDelete.nextProperty;
|
|
}
|
|
|
|
/*
|
|
* Link it for real...
|
|
*/
|
|
parentProperty.previousProperty = newLinkProperty;
|
|
|
|
}
|
|
else if (typeOfRelation == PROPERTY_RELATION_NEXT)
|
|
{
|
|
if (propertyToDelete.previousProperty != PROPERTY_NULL)
|
|
{
|
|
/*
|
|
* Set the parent next to the property to delete next previous
|
|
*/
|
|
newLinkProperty = propertyToDelete.previousProperty;
|
|
|
|
if (propertyToDelete.nextProperty != PROPERTY_NULL)
|
|
{
|
|
/*
|
|
* We also need to find a storage for the other link, setup variables
|
|
* to do this at the end...
|
|
*/
|
|
needToFindAPlaceholder = TRUE;
|
|
storeNode = propertyToDelete.previousProperty;
|
|
toStoreNode = propertyToDelete.nextProperty;
|
|
relationType = PROPERTY_RELATION_NEXT;
|
|
}
|
|
}
|
|
else if (propertyToDelete.nextProperty != PROPERTY_NULL)
|
|
{
|
|
/*
|
|
* Set the parent next to the property to delete next
|
|
*/
|
|
newLinkProperty = propertyToDelete.nextProperty;
|
|
}
|
|
|
|
/*
|
|
* Link it for real...
|
|
*/
|
|
parentProperty.nextProperty = newLinkProperty;
|
|
}
|
|
else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
|
|
{
|
|
if (propertyToDelete.previousProperty != PROPERTY_NULL)
|
|
{
|
|
/*
|
|
* Set the parent dir to the property to delete previous
|
|
*/
|
|
newLinkProperty = propertyToDelete.previousProperty;
|
|
|
|
if (propertyToDelete.nextProperty != PROPERTY_NULL)
|
|
{
|
|
/*
|
|
* We also need to find a storage for the other link, setup variables
|
|
* to do this at the end...
|
|
*/
|
|
needToFindAPlaceholder = TRUE;
|
|
storeNode = propertyToDelete.previousProperty;
|
|
toStoreNode = propertyToDelete.nextProperty;
|
|
relationType = PROPERTY_RELATION_NEXT;
|
|
}
|
|
}
|
|
else if (propertyToDelete.nextProperty != PROPERTY_NULL)
|
|
{
|
|
/*
|
|
* Set the parent dir to the property to delete next
|
|
*/
|
|
newLinkProperty = propertyToDelete.nextProperty;
|
|
}
|
|
|
|
/*
|
|
* Link it for real...
|
|
*/
|
|
parentProperty.dirProperty = newLinkProperty;
|
|
}
|
|
|
|
/*
|
|
* Write back the parent property
|
|
*/
|
|
res = Storage32Impl_WriteProperty(
|
|
This->ancestorStorage,
|
|
parentPropertyId,
|
|
&parentProperty);
|
|
if(! res)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
/*
|
|
* If a placeholder is required for the other link, then, find one and
|
|
* get out of here...
|
|
*/
|
|
if (needToFindAPlaceholder)
|
|
{
|
|
hr = findPlaceholder(
|
|
This,
|
|
toStoreNode,
|
|
storeNode,
|
|
relationType);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
* SetElementTimes (IStorage)
|
|
*/
|
|
HRESULT WINAPI Storage32Impl_SetElementTimes(
|
|
IStorage32* iface,
|
|
const OLECHAR32 *pwcsName,/* [string][in] */
|
|
const FILETIME *pctime, /* [in] */
|
|
const FILETIME *patime, /* [in] */
|
|
const FILETIME *pmtime) /* [in] */
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* SetStateBits (IStorage)
|
|
*/
|
|
HRESULT WINAPI Storage32Impl_SetStateBits(
|
|
IStorage32* iface,
|
|
DWORD grfStateBits,/* [in] */
|
|
DWORD grfMask) /* [in] */
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT Storage32Impl_Construct(
|
|
Storage32Impl* This,
|
|
HANDLE32 hFile,
|
|
DWORD openFlags)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
StgProperty currentProperty;
|
|
BOOL32 readSucessful;
|
|
ULONG currentPropertyIndex;
|
|
|
|
if ( FAILED( validateSTGM(openFlags) ))
|
|
return STG_E_INVALIDFLAG;
|
|
|
|
memset(This, 0, sizeof(Storage32Impl));
|
|
|
|
/*
|
|
* Initialize the virtual fgunction table.
|
|
*/
|
|
This->lpvtbl = &Storage32Impl_Vtbl;
|
|
This->v_destructor = &Storage32Impl_Destroy;
|
|
|
|
/*
|
|
* This is the top-level storage so initialize the ancester pointer
|
|
* to this.
|
|
*/
|
|
This->ancestorStorage = This;
|
|
|
|
/*
|
|
* Initialize the physical support of the storage.
|
|
*/
|
|
This->hFile = hFile;
|
|
|
|
/*
|
|
* Initialize the big block cache.
|
|
*/
|
|
This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
|
|
This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
|
|
This->bigBlockFile = BIGBLOCKFILE_Construct(hFile, This->bigBlockSize);
|
|
|
|
if (openFlags & STGM_CREATE)
|
|
{
|
|
ULARGE_INTEGER size;
|
|
BYTE* bigBlockBuffer;
|
|
|
|
/*
|
|
* Initialize all header variables:
|
|
* - The big block depot consists of one block and it is at block 0
|
|
* - The properties start at block 1
|
|
* - There is no small block depot
|
|
*/
|
|
memset( This->bigBlockDepotStart,
|
|
BLOCK_UNUSED,
|
|
sizeof(This->bigBlockDepotStart));
|
|
|
|
This->bigBlockDepotCount = 1;
|
|
This->bigBlockDepotStart[0] = 0;
|
|
This->rootStartBlock = 1;
|
|
This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
|
|
This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
|
|
This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
|
|
This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
|
|
This->extBigBlockDepotCount = 0;
|
|
|
|
Storage32Impl_SaveFileHeader(This);
|
|
|
|
/*
|
|
* Add one block for the big block depot and one block for the properties
|
|
*/
|
|
size.HighPart = 0;
|
|
size.LowPart = This->bigBlockSize * 3;
|
|
BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
|
|
|
|
/*
|
|
* Initialize the big block depot
|
|
*/
|
|
bigBlockBuffer = Storage32Impl_GetBigBlock(This, 0);
|
|
memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
|
|
StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
|
|
StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
|
|
Storage32Impl_ReleaseBigBlock(This, bigBlockBuffer);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Load the header for the file.
|
|
*/
|
|
Storage32Impl_LoadFileHeader(This);
|
|
}
|
|
|
|
/*
|
|
* Create the block chain abstractions.
|
|
*/
|
|
This->rootBlockChain =
|
|
BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL);
|
|
|
|
This->smallBlockDepotChain = BlockChainStream_Construct(
|
|
This,
|
|
&This->smallBlockDepotStart,
|
|
PROPERTY_NULL);
|
|
|
|
/*
|
|
* Write the root property
|
|
*/
|
|
if (openFlags & STGM_CREATE)
|
|
{
|
|
StgProperty rootProp;
|
|
/*
|
|
* Initialize the property chain
|
|
*/
|
|
memset(&rootProp, 0, sizeof(rootProp));
|
|
lstrcpyAtoW(rootProp.name, rootPropertyName);
|
|
|
|
rootProp.sizeOfNameString = (lstrlen32W(rootProp.name)+1) * sizeof(WCHAR);
|
|
rootProp.propertyType = PROPTYPE_ROOT;
|
|
rootProp.previousProperty = PROPERTY_NULL;
|
|
rootProp.nextProperty = PROPERTY_NULL;
|
|
rootProp.dirProperty = PROPERTY_NULL;
|
|
rootProp.startingBlock = BLOCK_END_OF_CHAIN;
|
|
rootProp.size.HighPart = 0;
|
|
rootProp.size.LowPart = 0;
|
|
|
|
Storage32Impl_WriteProperty(This, 0, &rootProp);
|
|
}
|
|
|
|
/*
|
|
* Find the ID of the root int he property sets.
|
|
*/
|
|
currentPropertyIndex = 0;
|
|
|
|
do
|
|
{
|
|
readSucessful = Storage32Impl_ReadProperty(
|
|
This,
|
|
currentPropertyIndex,
|
|
¤tProperty);
|
|
|
|
if (readSucessful)
|
|
{
|
|
if ( (currentProperty.sizeOfNameString != 0 ) &&
|
|
(currentProperty.propertyType == PROPTYPE_ROOT) )
|
|
{
|
|
This->rootPropertySetIndex = currentPropertyIndex;
|
|
}
|
|
}
|
|
|
|
currentPropertyIndex++;
|
|
|
|
} while (readSucessful && (This->rootPropertySetIndex == PROPERTY_NULL) );
|
|
|
|
if (!readSucessful)
|
|
{
|
|
/* TODO CLEANUP */
|
|
return E_FAIL;
|
|
}
|
|
|
|
/*
|
|
* Create the block chain abstraction for the small block root chain.
|
|
*/
|
|
This->smallBlockRootChain = BlockChainStream_Construct(
|
|
This,
|
|
NULL,
|
|
This->rootPropertySetIndex);
|
|
|
|
return hr;
|
|
}
|
|
|
|
void Storage32Impl_Destroy(
|
|
Storage32Impl* This)
|
|
{
|
|
BlockChainStream_Destroy(This->smallBlockRootChain);
|
|
BlockChainStream_Destroy(This->rootBlockChain);
|
|
BlockChainStream_Destroy(This->smallBlockDepotChain);
|
|
|
|
BIGBLOCKFILE_Destructor(This->bigBlockFile);
|
|
return;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Storage32Impl_GetNextFreeBigBlock
|
|
*
|
|
* Returns the index of the next free big block.
|
|
* If the big block depot is filled, this method will enlarge it.
|
|
*
|
|
* TODO: Handle the case when the big block depot becomes bigger
|
|
* than COUNT_BBDEPOTINHEADER.
|
|
*/
|
|
ULONG Storage32Impl_GetNextFreeBigBlock(
|
|
Storage32Impl* This)
|
|
{
|
|
ULONG depotBlockIndexPos;
|
|
void *depotBuffer;
|
|
ULONG depotBlockOffset;
|
|
ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
|
|
ULONG nextBlockIndex = BLOCK_SPECIAL;
|
|
int depotIndex = 0;
|
|
ULONG blockNoInSequence = 0;
|
|
|
|
/*
|
|
* Scan the entire big block depot until we find a block marked free
|
|
*/
|
|
while ( (depotIndex < COUNT_BBDEPOTINHEADER) &&
|
|
(nextBlockIndex != BLOCK_UNUSED))
|
|
{
|
|
depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
|
|
|
|
if (depotBlockIndexPos == BLOCK_UNUSED)
|
|
{
|
|
/*
|
|
* No more space in the big block depot, we have to enlarge it
|
|
*/
|
|
depotBlockIndexPos = depotIndex*blocksPerDepot;
|
|
depotBuffer = Storage32Impl_GetBigBlock(This, depotBlockIndexPos);
|
|
|
|
depotBlockOffset = 0;
|
|
|
|
/* mark this block as being part of the big block depot
|
|
*/
|
|
StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, BLOCK_SPECIAL);
|
|
depotBlockOffset += sizeof(ULONG);
|
|
|
|
/* initialize blocks as free
|
|
*/
|
|
while ((depotBlockOffset < blocksPerDepot))
|
|
{
|
|
StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, BLOCK_UNUSED);
|
|
depotBlockOffset += sizeof(ULONG);
|
|
}
|
|
|
|
Storage32Impl_ReleaseBigBlock(This, depotBuffer);
|
|
|
|
/* Save the information to the file header
|
|
*/
|
|
This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
|
|
This->bigBlockDepotCount++;
|
|
Storage32Impl_SaveFileHeader(This);
|
|
}
|
|
|
|
depotBuffer = Storage32Impl_GetROBigBlock(This, depotBlockIndexPos);
|
|
|
|
if (depotBuffer != 0)
|
|
{
|
|
depotBlockOffset = 0;
|
|
|
|
while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
|
|
( nextBlockIndex != BLOCK_UNUSED))
|
|
{
|
|
StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
|
|
|
|
if (nextBlockIndex != BLOCK_UNUSED)
|
|
blockNoInSequence++;
|
|
|
|
depotBlockOffset += sizeof(ULONG);
|
|
}
|
|
|
|
Storage32Impl_ReleaseBigBlock(This, depotBuffer);
|
|
}
|
|
|
|
depotIndex++;
|
|
}
|
|
|
|
return blockNoInSequence;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Storage32Impl_FreeBigBlock
|
|
*
|
|
* This method will flag the specified block as free in the big block depot.
|
|
*/
|
|
void Storage32Impl_FreeBigBlock(
|
|
Storage32Impl* This,
|
|
ULONG blockIndex)
|
|
{
|
|
Storage32Impl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
|
|
}
|
|
|
|
/************************************************************************
|
|
* Storage32Impl_GetNextBlockInChain
|
|
*
|
|
* This method will retrieve the block index of the next big block in
|
|
* in the chain.
|
|
*
|
|
* Params: This - Pointer to the Storage object.
|
|
* blockIndex - Index of the block to retrieve the chain
|
|
* for.
|
|
*
|
|
* Returns: This method returns the index of the next block in the chain.
|
|
* It will return the constants:
|
|
* BLOCK_SPECIAL - If the block given was not part of a
|
|
* chain.
|
|
* BLOCK_END_OF_CHAIN - If the block given was the last in
|
|
* a chain.
|
|
* BLOCK_UNUSED - If the block given was not past of a chain
|
|
* and is available.
|
|
*
|
|
* See Windows documentation for more details on IStorage methods.
|
|
*/
|
|
ULONG Storage32Impl_GetNextBlockInChain(
|
|
Storage32Impl* This,
|
|
ULONG blockIndex)
|
|
{
|
|
ULONG offsetInDepot = blockIndex * sizeof (ULONG);
|
|
ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
|
|
ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
|
|
ULONG nextBlockIndex = BLOCK_SPECIAL;
|
|
void* depotBuffer;
|
|
ULONG depotBlockIndexPos;
|
|
|
|
assert(depotBlockCount < This->bigBlockDepotCount);
|
|
assert(depotBlockCount < COUNT_BBDEPOTINHEADER);
|
|
|
|
depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
|
|
|
|
depotBuffer = Storage32Impl_GetROBigBlock(This, depotBlockIndexPos);
|
|
|
|
if (depotBuffer!=0)
|
|
{
|
|
StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
|
|
|
|
Storage32Impl_ReleaseBigBlock(This, depotBuffer);
|
|
}
|
|
|
|
return nextBlockIndex;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Storage32Impl_SetNextBlockInChain
|
|
*
|
|
* This method will write the index of the specified block's next block
|
|
* in the big block depot.
|
|
*
|
|
* For example: to create the chain 3 -> 1 -> 7 -> End of Chain
|
|
* do the following
|
|
*
|
|
* Storage32Impl_SetNextBlockInChain(This, 3, 1);
|
|
* Storage32Impl_SetNextBlockInChain(This, 1, 7);
|
|
* Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
|
|
*
|
|
*/
|
|
void Storage32Impl_SetNextBlockInChain(
|
|
Storage32Impl* This,
|
|
ULONG blockIndex,
|
|
ULONG nextBlock)
|
|
{
|
|
ULONG offsetInDepot = blockIndex * sizeof (ULONG);
|
|
ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
|
|
ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
|
|
ULONG depotBlockIndexPos;
|
|
void* depotBuffer;
|
|
|
|
assert(depotBlockCount < This->bigBlockDepotCount);
|
|
assert(depotBlockCount < COUNT_BBDEPOTINHEADER);
|
|
|
|
depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
|
|
|
|
depotBuffer = Storage32Impl_GetBigBlock(This, depotBlockIndexPos);
|
|
|
|
if (depotBuffer!=0)
|
|
{
|
|
StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
|
|
Storage32Impl_ReleaseBigBlock(This, depotBuffer);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Storage32Impl_LoadFileHeader
|
|
*
|
|
* This method will read in the file header, i.e. big block index -1.
|
|
*/
|
|
HRESULT Storage32Impl_LoadFileHeader(
|
|
Storage32Impl* This)
|
|
{
|
|
HRESULT hr = STG_E_FILENOTFOUND;
|
|
void* headerBigBlock = NULL;
|
|
int index;
|
|
|
|
/*
|
|
* Get a pointer to the big block of data containing the header.
|
|
*/
|
|
headerBigBlock = Storage32Impl_GetROBigBlock(This, -1);
|
|
|
|
/*
|
|
* Extract the information from the header.
|
|
*/
|
|
if (headerBigBlock!=0)
|
|
{
|
|
/*
|
|
* Check for the "magic number" signature and return an error if it is not
|
|
* found.
|
|
*/
|
|
if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
|
|
{
|
|
Storage32Impl_ReleaseBigBlock(This, headerBigBlock);
|
|
return STG_E_OLDFORMAT;
|
|
}
|
|
|
|
if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
|
|
{
|
|
Storage32Impl_ReleaseBigBlock(This, headerBigBlock);
|
|
return STG_E_INVALIDHEADER;
|
|
}
|
|
|
|
StorageUtl_ReadWord(
|
|
headerBigBlock,
|
|
OFFSET_BIGBLOCKSIZEBITS,
|
|
&This->bigBlockSizeBits);
|
|
|
|
StorageUtl_ReadWord(
|
|
headerBigBlock,
|
|
OFFSET_SMALLBLOCKSIZEBITS,
|
|
&This->smallBlockSizeBits);
|
|
|
|
StorageUtl_ReadDWord(
|
|
headerBigBlock,
|
|
OFFSET_BBDEPOTCOUNT,
|
|
&This->bigBlockDepotCount);
|
|
|
|
StorageUtl_ReadDWord(
|
|
headerBigBlock,
|
|
OFFSET_ROOTSTARTBLOCK,
|
|
&This->rootStartBlock);
|
|
|
|
StorageUtl_ReadDWord(
|
|
headerBigBlock,
|
|
OFFSET_SBDEPOTSTART,
|
|
&This->smallBlockDepotStart);
|
|
|
|
StorageUtl_ReadDWord(
|
|
headerBigBlock,
|
|
OFFSET_EXTBBDEPOTSTART,
|
|
&This->extBigBlockDepotStart);
|
|
|
|
StorageUtl_ReadDWord(
|
|
headerBigBlock,
|
|
OFFSET_EXTBBDEPOTCOUNT,
|
|
&This->extBigBlockDepotCount);
|
|
|
|
for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
|
|
{
|
|
StorageUtl_ReadDWord(
|
|
headerBigBlock,
|
|
OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
|
|
&(This->bigBlockDepotStart[index]));
|
|
}
|
|
|
|
/*
|
|
* Make the bitwise arithmetic to get the size of the blocks in bytes.
|
|
*/
|
|
if ((1 << 2) == 4)
|
|
{
|
|
This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
|
|
This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
|
|
}
|
|
else
|
|
{
|
|
This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
|
|
This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
|
|
}
|
|
|
|
/*
|
|
* Right now, the code is making some assumptions about the size of the
|
|
* blocks, just make sure they are what we're expecting.
|
|
*/
|
|
assert( (This->bigBlockSize==DEF_BIG_BLOCK_SIZE) &&
|
|
(This->smallBlockSize==DEF_SMALL_BLOCK_SIZE));
|
|
|
|
/*
|
|
* Release the block.
|
|
*/
|
|
Storage32Impl_ReleaseBigBlock(This, headerBigBlock);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Storage32Impl_SaveFileHeader
|
|
*
|
|
* This method will save to the file the header, i.e. big block -1.
|
|
*/
|
|
void Storage32Impl_SaveFileHeader(
|
|
Storage32Impl* This)
|
|
{
|
|
BYTE headerBigBlock[BIG_BLOCK_SIZE];
|
|
int index;
|
|
BOOL32 success;
|
|
|
|
/*
|
|
* Get a pointer to the big block of data containing the header.
|
|
*/
|
|
success = Storage32Impl_ReadBigBlock(This, -1, headerBigBlock);
|
|
|
|
/*
|
|
* If the block read failed, the file is probably new.
|
|
*/
|
|
if (!success)
|
|
{
|
|
/*
|
|
* Initialize for all unknown fields.
|
|
*/
|
|
memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
|
|
|
|
/*
|
|
* Initialize the magic number.
|
|
*/
|
|
memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
|
|
|
|
/*
|
|
* And a bunch of things we don't know what they mean
|
|
*/
|
|
StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
|
|
StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
|
|
StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
|
|
StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
|
|
StorageUtl_WriteDWord(headerBigBlock, 0x40, (DWORD)0x0001);
|
|
}
|
|
|
|
/*
|
|
* Write the information to the header.
|
|
*/
|
|
if (headerBigBlock!=0)
|
|
{
|
|
StorageUtl_WriteWord(
|
|
headerBigBlock,
|
|
OFFSET_BIGBLOCKSIZEBITS,
|
|
This->bigBlockSizeBits);
|
|
|
|
StorageUtl_WriteWord(
|
|
headerBigBlock,
|
|
OFFSET_SMALLBLOCKSIZEBITS,
|
|
This->smallBlockSizeBits);
|
|
|
|
StorageUtl_WriteDWord(
|
|
headerBigBlock,
|
|
OFFSET_BBDEPOTCOUNT,
|
|
This->bigBlockDepotCount);
|
|
|
|
StorageUtl_WriteDWord(
|
|
headerBigBlock,
|
|
OFFSET_ROOTSTARTBLOCK,
|
|
This->rootStartBlock);
|
|
|
|
StorageUtl_WriteDWord(
|
|
headerBigBlock,
|
|
OFFSET_SBDEPOTSTART,
|
|
This->smallBlockDepotStart);
|
|
|
|
StorageUtl_WriteDWord(
|
|
headerBigBlock,
|
|
OFFSET_EXTBBDEPOTSTART,
|
|
This->extBigBlockDepotStart);
|
|
|
|
StorageUtl_WriteDWord(
|
|
headerBigBlock,
|
|
OFFSET_EXTBBDEPOTCOUNT,
|
|
This->extBigBlockDepotCount);
|
|
|
|
for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
|
|
{
|
|
StorageUtl_WriteDWord(
|
|
headerBigBlock,
|
|
OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
|
|
(This->bigBlockDepotStart[index]));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Write the big block back to the file.
|
|
*/
|
|
Storage32Impl_WriteBigBlock(This, -1, headerBigBlock);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Storage32Impl_ReadProperty
|
|
*
|
|
* This method will read the specified property from the property chain.
|
|
*/
|
|
BOOL32 Storage32Impl_ReadProperty(
|
|
Storage32Impl* This,
|
|
ULONG index,
|
|
StgProperty* buffer)
|
|
{
|
|
BYTE currentProperty[PROPSET_BLOCK_SIZE];
|
|
ULARGE_INTEGER offsetInPropSet;
|
|
BOOL32 readSucessful;
|
|
ULONG bytesRead;
|
|
|
|
offsetInPropSet.HighPart = 0;
|
|
offsetInPropSet.LowPart = index * PROPSET_BLOCK_SIZE;
|
|
|
|
readSucessful = BlockChainStream_ReadAt(
|
|
This->rootBlockChain,
|
|
offsetInPropSet,
|
|
PROPSET_BLOCK_SIZE,
|
|
currentProperty,
|
|
&bytesRead);
|
|
|
|
if (readSucessful)
|
|
{
|
|
memset(buffer->name, 0, sizeof(buffer->name));
|
|
memcpy(
|
|
buffer->name,
|
|
currentProperty+OFFSET_PS_NAME,
|
|
PROPERTY_NAME_BUFFER_LEN );
|
|
|
|
memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
|
|
|
|
StorageUtl_ReadWord(
|
|
currentProperty,
|
|
OFFSET_PS_NAMELENGTH,
|
|
&buffer->sizeOfNameString);
|
|
|
|
StorageUtl_ReadDWord(
|
|
currentProperty,
|
|
OFFSET_PS_PREVIOUSPROP,
|
|
&buffer->previousProperty);
|
|
|
|
StorageUtl_ReadDWord(
|
|
currentProperty,
|
|
OFFSET_PS_NEXTPROP,
|
|
&buffer->nextProperty);
|
|
|
|
StorageUtl_ReadDWord(
|
|
currentProperty,
|
|
OFFSET_PS_DIRPROP,
|
|
&buffer->dirProperty);
|
|
|
|
StorageUtl_ReadGUID(
|
|
currentProperty,
|
|
OFFSET_PS_GUID,
|
|
&buffer->propertyUniqueID);
|
|
|
|
StorageUtl_ReadDWord(
|
|
currentProperty,
|
|
OFFSET_PS_TSS1,
|
|
&buffer->timeStampS1);
|
|
|
|
StorageUtl_ReadDWord(
|
|
currentProperty,
|
|
OFFSET_PS_TSD1,
|
|
&buffer->timeStampD1);
|
|
|
|
StorageUtl_ReadDWord(
|
|
currentProperty,
|
|
OFFSET_PS_TSS2,
|
|
&buffer->timeStampS2);
|
|
|
|
StorageUtl_ReadDWord(
|
|
currentProperty,
|
|
OFFSET_PS_TSD2,
|
|
&buffer->timeStampD2);
|
|
|
|
StorageUtl_ReadDWord(
|
|
currentProperty,
|
|
OFFSET_PS_STARTBLOCK,
|
|
&buffer->startingBlock);
|
|
|
|
StorageUtl_ReadDWord(
|
|
currentProperty,
|
|
OFFSET_PS_SIZE,
|
|
&buffer->size.LowPart);
|
|
|
|
buffer->size.HighPart = 0;
|
|
}
|
|
|
|
return readSucessful;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* Write the specified property into the property chain
|
|
*/
|
|
BOOL32 Storage32Impl_WriteProperty(
|
|
Storage32Impl* This,
|
|
ULONG index,
|
|
StgProperty* buffer)
|
|
{
|
|
BYTE currentProperty[PROPSET_BLOCK_SIZE];
|
|
ULARGE_INTEGER offsetInPropSet;
|
|
BOOL32 writeSucessful;
|
|
ULONG bytesWritten;
|
|
|
|
offsetInPropSet.HighPart = 0;
|
|
offsetInPropSet.LowPart = index * PROPSET_BLOCK_SIZE;
|
|
|
|
memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
|
|
|
|
memcpy(
|
|
currentProperty + OFFSET_PS_NAME,
|
|
buffer->name,
|
|
PROPERTY_NAME_BUFFER_LEN );
|
|
|
|
memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
|
|
|
|
/*
|
|
* Reassign the size in case of mistake....
|
|
*/
|
|
buffer->sizeOfNameString = (lstrlen32W(buffer->name)+1) * sizeof(WCHAR);
|
|
|
|
StorageUtl_WriteWord(
|
|
currentProperty,
|
|
OFFSET_PS_NAMELENGTH,
|
|
buffer->sizeOfNameString);
|
|
|
|
StorageUtl_WriteDWord(
|
|
currentProperty,
|
|
OFFSET_PS_PREVIOUSPROP,
|
|
buffer->previousProperty);
|
|
|
|
StorageUtl_WriteDWord(
|
|
currentProperty,
|
|
OFFSET_PS_NEXTPROP,
|
|
buffer->nextProperty);
|
|
|
|
StorageUtl_WriteDWord(
|
|
currentProperty,
|
|
OFFSET_PS_DIRPROP,
|
|
buffer->dirProperty);
|
|
|
|
StorageUtl_WriteGUID(
|
|
currentProperty,
|
|
OFFSET_PS_GUID,
|
|
&buffer->propertyUniqueID);
|
|
|
|
StorageUtl_WriteDWord(
|
|
currentProperty,
|
|
OFFSET_PS_TSS1,
|
|
buffer->timeStampS1);
|
|
|
|
StorageUtl_WriteDWord(
|
|
currentProperty,
|
|
OFFSET_PS_TSD1,
|
|
buffer->timeStampD1);
|
|
|
|
StorageUtl_WriteDWord(
|
|
currentProperty,
|
|
OFFSET_PS_TSS2,
|
|
buffer->timeStampS2);
|
|
|
|
StorageUtl_WriteDWord(
|
|
currentProperty,
|
|
OFFSET_PS_TSD2,
|
|
buffer->timeStampD2);
|
|
|
|
StorageUtl_WriteDWord(
|
|
currentProperty,
|
|
OFFSET_PS_STARTBLOCK,
|
|
buffer->startingBlock);
|
|
|
|
StorageUtl_WriteDWord(
|
|
currentProperty,
|
|
OFFSET_PS_SIZE,
|
|
buffer->size.LowPart);
|
|
|
|
writeSucessful = BlockChainStream_WriteAt(This->rootBlockChain,
|
|
offsetInPropSet,
|
|
PROPSET_BLOCK_SIZE,
|
|
currentProperty,
|
|
&bytesWritten);
|
|
return writeSucessful;
|
|
}
|
|
|
|
BOOL32 Storage32Impl_ReadBigBlock(
|
|
Storage32Impl* This,
|
|
ULONG blockIndex,
|
|
void* buffer)
|
|
{
|
|
void* bigBlockBuffer;
|
|
|
|
bigBlockBuffer = Storage32Impl_GetROBigBlock(This, blockIndex);
|
|
|
|
if (bigBlockBuffer!=0)
|
|
{
|
|
memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
|
|
|
|
Storage32Impl_ReleaseBigBlock(This, bigBlockBuffer);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL32 Storage32Impl_WriteBigBlock(
|
|
Storage32Impl* This,
|
|
ULONG blockIndex,
|
|
void* buffer)
|
|
{
|
|
void* bigBlockBuffer;
|
|
|
|
bigBlockBuffer = Storage32Impl_GetBigBlock(This, blockIndex);
|
|
|
|
if (bigBlockBuffer!=0)
|
|
{
|
|
memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
|
|
|
|
Storage32Impl_ReleaseBigBlock(This, bigBlockBuffer);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void* Storage32Impl_GetROBigBlock(
|
|
Storage32Impl* This,
|
|
ULONG blockIndex)
|
|
{
|
|
return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
|
|
}
|
|
|
|
void* Storage32Impl_GetBigBlock(
|
|
Storage32Impl* This,
|
|
ULONG blockIndex)
|
|
{
|
|
return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
|
|
}
|
|
|
|
void Storage32Impl_ReleaseBigBlock(
|
|
Storage32Impl* This,
|
|
void* pBigBlock)
|
|
{
|
|
BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Storage32Impl_SmallBlocksToBigBlocks
|
|
*
|
|
* This method will convert a small block chain to a big block chain.
|
|
* The small block chain will be destroyed.
|
|
*/
|
|
BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
|
|
Storage32Impl* This,
|
|
SmallBlockChainStream** ppsbChain)
|
|
{
|
|
ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
|
|
ULARGE_INTEGER size, offset;
|
|
ULONG cbRead, cbWritten;
|
|
ULONG propertyIndex;
|
|
BOOL32 successRead, successWrite;
|
|
StgProperty chainProperty;
|
|
BYTE buffer[DEF_SMALL_BLOCK_SIZE];
|
|
BlockChainStream *bbTempChain = NULL;
|
|
BlockChainStream *bigBlockChain = NULL;
|
|
|
|
/*
|
|
* Create a temporary big block chain that doesn't have
|
|
* an associated property. This temporary chain will be
|
|
* used to copy data from small blocks to big blocks.
|
|
*/
|
|
bbTempChain = BlockChainStream_Construct(This,
|
|
&bbHeadOfChain,
|
|
PROPERTY_NULL);
|
|
|
|
/*
|
|
* Grow the big block chain.
|
|
*/
|
|
size = SmallBlockChainStream_GetSize(*ppsbChain);
|
|
BlockChainStream_SetSize(bbTempChain, size);
|
|
|
|
/*
|
|
* Copy the contents of the small block chain to the big block chain
|
|
* by small block size increments.
|
|
*/
|
|
offset.LowPart = 0;
|
|
offset.HighPart = 0;
|
|
|
|
do
|
|
{
|
|
successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
|
|
offset,
|
|
sizeof(buffer),
|
|
buffer,
|
|
&cbRead);
|
|
|
|
successWrite = BlockChainStream_WriteAt(bbTempChain,
|
|
offset,
|
|
sizeof(buffer),
|
|
buffer,
|
|
&cbWritten);
|
|
offset.LowPart += This->smallBlockSize;
|
|
|
|
} while (successRead && successWrite);
|
|
|
|
assert(cbRead == cbWritten);
|
|
|
|
/*
|
|
* Destroy the small block chain.
|
|
*/
|
|
propertyIndex = (*ppsbChain)->ownerPropertyIndex;
|
|
size.HighPart = 0;
|
|
size.LowPart = 0;
|
|
SmallBlockChainStream_SetSize(*ppsbChain, size);
|
|
SmallBlockChainStream_Destroy(*ppsbChain);
|
|
*ppsbChain = 0;
|
|
|
|
/*
|
|
* Change the property information. This chain is now a big block chain
|
|
* and it doesn't reside in the small blocks chain anymore.
|
|
*/
|
|
Storage32Impl_ReadProperty(This, propertyIndex, &chainProperty);
|
|
|
|
chainProperty.startingBlock = bbHeadOfChain;
|
|
|
|
Storage32Impl_WriteProperty(This, propertyIndex, &chainProperty);
|
|
|
|
/*
|
|
* Destroy the temporary propertyless big block chain.
|
|
* Create a new big block chain associated with this property.
|
|
*/
|
|
BlockChainStream_Destroy(bbTempChain);
|
|
bigBlockChain = BlockChainStream_Construct(This,
|
|
NULL,
|
|
propertyIndex);
|
|
|
|
return bigBlockChain;
|
|
}
|
|
|
|
/******************************************************************************
|
|
** Storage32InternalImpl implementation
|
|
*/
|
|
|
|
Storage32InternalImpl* Storage32InternalImpl_Construct(
|
|
Storage32Impl* ancestorStorage,
|
|
ULONG rootPropertyIndex)
|
|
{
|
|
Storage32InternalImpl* newStorage;
|
|
|
|
/*
|
|
* Allocate space for the new storage object
|
|
*/
|
|
newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(Storage32InternalImpl));
|
|
|
|
if (newStorage!=0)
|
|
{
|
|
memset(newStorage, 0, sizeof(Storage32InternalImpl));
|
|
|
|
/*
|
|
* Initialize the virtual function table.
|
|
*/
|
|
newStorage->lpvtbl = &Storage32InternalImpl_Vtbl;
|
|
newStorage->v_destructor = &Storage32InternalImpl_Destroy;
|
|
|
|
/*
|
|
* Keep the ancestor storage pointer and nail a reference to it.
|
|
*/
|
|
newStorage->ancestorStorage = ancestorStorage;
|
|
Storage32BaseImpl_AddRef((IStorage32*)(newStorage->ancestorStorage));
|
|
|
|
/*
|
|
* Keep the index of the root property set for this storage,
|
|
*/
|
|
newStorage->rootPropertySetIndex = rootPropertyIndex;
|
|
|
|
return newStorage;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void Storage32InternalImpl_Destroy(
|
|
Storage32InternalImpl* This)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, This);
|
|
}
|
|
|
|
/******************************************************************************
|
|
**
|
|
** Storage32InternalImpl_Commit
|
|
**
|
|
** The non-root storages cannot be opened in transacted mode thus this function
|
|
** does nothing.
|
|
*/
|
|
HRESULT WINAPI Storage32InternalImpl_Commit(
|
|
IStorage32* iface,
|
|
DWORD grfCommitFlags) /* [in] */
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
/******************************************************************************
|
|
**
|
|
** Storage32InternalImpl_Revert
|
|
**
|
|
** The non-root storages cannot be opened in transacted mode thus this function
|
|
** does nothing.
|
|
*/
|
|
HRESULT WINAPI Storage32InternalImpl_Revert(
|
|
IStorage32* iface)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
/******************************************************************************
|
|
** IEnumSTATSTGImpl implementation
|
|
*/
|
|
|
|
IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
|
|
Storage32Impl* parentStorage,
|
|
ULONG firstPropertyNode)
|
|
{
|
|
IEnumSTATSTGImpl* newEnumeration;
|
|
|
|
newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
|
|
|
|
if (newEnumeration!=0)
|
|
{
|
|
/*
|
|
* Set-up the virtual function table and reference count.
|
|
*/
|
|
newEnumeration->lpvtbl = &IEnumSTATSTGImpl_Vtbl;
|
|
newEnumeration->ref = 0;
|
|
|
|
/*
|
|
* We want to nail-down the reference to the storage in case the
|
|
* enumeration out-lives the storage in the client application.
|
|
*/
|
|
newEnumeration->parentStorage = parentStorage;
|
|
IStorage32_AddRef((IStorage32*)newEnumeration->parentStorage);
|
|
|
|
newEnumeration->firstPropertyNode = firstPropertyNode;
|
|
|
|
/*
|
|
* Initialize the search stack
|
|
*/
|
|
newEnumeration->stackSize = 0;
|
|
newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
|
|
newEnumeration->stackToVisit =
|
|
HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
|
|
|
|
/*
|
|
* Make sure the current node of the iterator is the first one.
|
|
*/
|
|
IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
|
|
}
|
|
|
|
return newEnumeration;
|
|
}
|
|
|
|
void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
|
|
{
|
|
IStorage32_Release((IStorage32*)This->parentStorage);
|
|
HeapFree(GetProcessHeap(), 0, This->stackToVisit);
|
|
HeapFree(GetProcessHeap(), 0, This);
|
|
}
|
|
|
|
HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
|
|
IEnumSTATSTG* iface,
|
|
REFIID riid,
|
|
void** ppvObject)
|
|
{
|
|
IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
|
|
|
|
/*
|
|
* Perform a sanity check on the parameters.
|
|
*/
|
|
if (ppvObject==0)
|
|
return E_INVALIDARG;
|
|
|
|
/*
|
|
* Initialize the return parameter.
|
|
*/
|
|
*ppvObject = 0;
|
|
|
|
/*
|
|
* Compare the riid with the interface IDs implemented by this object.
|
|
*/
|
|
if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
|
|
{
|
|
*ppvObject = (IEnumSTATSTG*)This;
|
|
}
|
|
else if (memcmp(&IID_IStorage, riid, sizeof(IID_IEnumSTATSTG)) == 0)
|
|
{
|
|
*ppvObject = (IEnumSTATSTG*)This;
|
|
}
|
|
|
|
/*
|
|
* Check that we obtained an interface.
|
|
*/
|
|
if ((*ppvObject)==0)
|
|
return E_NOINTERFACE;
|
|
|
|
/*
|
|
* Query Interface always increases the reference count by one when it is
|
|
* successful
|
|
*/
|
|
IEnumSTATSTGImpl_AddRef((IEnumSTATSTG*)This);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
ULONG WINAPI IEnumSTATSTGImpl_AddRef(
|
|
IEnumSTATSTG* iface)
|
|
{
|
|
IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
|
|
|
|
This->ref++;
|
|
return This->ref;
|
|
}
|
|
|
|
ULONG WINAPI IEnumSTATSTGImpl_Release(
|
|
IEnumSTATSTG* iface)
|
|
{
|
|
IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
|
|
|
|
ULONG newRef;
|
|
|
|
This->ref--;
|
|
newRef = This->ref;
|
|
|
|
/*
|
|
* If the reference count goes down to 0, perform suicide.
|
|
*/
|
|
if (newRef==0)
|
|
{
|
|
IEnumSTATSTGImpl_Destroy(This);
|
|
}
|
|
|
|
return newRef;;
|
|
}
|
|
|
|
HRESULT WINAPI IEnumSTATSTGImpl_Next(
|
|
IEnumSTATSTG* iface,
|
|
ULONG celt,
|
|
STATSTG* rgelt,
|
|
ULONG* pceltFetched)
|
|
{
|
|
IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
|
|
|
|
StgProperty currentProperty;
|
|
STATSTG* currentReturnStruct = rgelt;
|
|
ULONG objectFetched = 0;
|
|
ULONG currentSearchNode;
|
|
|
|
/*
|
|
* Perform a sanity check on the parameters.
|
|
*/
|
|
if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
|
|
return E_INVALIDARG;
|
|
|
|
/*
|
|
* To avoid the special case, get another pointer to a ULONG value if
|
|
* the caller didn't supply one.
|
|
*/
|
|
if (pceltFetched==0)
|
|
pceltFetched = &objectFetched;
|
|
|
|
/*
|
|
* Start the iteration, we will iterate until we hit the end of the
|
|
* linked list or until we hit the number of items to iterate through
|
|
*/
|
|
*pceltFetched = 0;
|
|
|
|
/*
|
|
* Start with the node at the top of the stack.
|
|
*/
|
|
currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
|
|
|
|
while ( ( *pceltFetched < celt) &&
|
|
( currentSearchNode!=PROPERTY_NULL) )
|
|
{
|
|
/*
|
|
* Remove the top node from the stack
|
|
*/
|
|
IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
|
|
|
|
/*
|
|
* Read the property from the storage.
|
|
*/
|
|
Storage32Impl_ReadProperty(This->parentStorage,
|
|
currentSearchNode,
|
|
¤tProperty);
|
|
|
|
/*
|
|
* Copy the information to the return buffer.
|
|
*/
|
|
StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
|
|
¤tProperty,
|
|
STATFLAG_DEFAULT);
|
|
|
|
/*
|
|
* Step to the next item in the iteration
|
|
*/
|
|
(*pceltFetched)++;
|
|
currentReturnStruct++;
|
|
|
|
/*
|
|
* Push the next search node in the search stack.
|
|
*/
|
|
IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
|
|
|
|
/*
|
|
* continue the iteration.
|
|
*/
|
|
currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
|
|
}
|
|
|
|
if (*pceltFetched == celt)
|
|
return S_OK;
|
|
|
|
return S_FALSE;
|
|
}
|
|
|
|
|
|
HRESULT WINAPI IEnumSTATSTGImpl_Skip(
|
|
IEnumSTATSTG* iface,
|
|
ULONG celt)
|
|
{
|
|
IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
|
|
|
|
StgProperty currentProperty;
|
|
ULONG objectFetched = 0;
|
|
ULONG currentSearchNode;
|
|
|
|
/*
|
|
* Start with the node at the top of the stack.
|
|
*/
|
|
currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
|
|
|
|
while ( (objectFetched < celt) &&
|
|
(currentSearchNode!=PROPERTY_NULL) )
|
|
{
|
|
/*
|
|
* Remove the top node from the stack
|
|
*/
|
|
IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
|
|
|
|
/*
|
|
* Read the property from the storage.
|
|
*/
|
|
Storage32Impl_ReadProperty(This->parentStorage,
|
|
currentSearchNode,
|
|
¤tProperty);
|
|
|
|
/*
|
|
* Step to the next item in the iteration
|
|
*/
|
|
objectFetched++;
|
|
|
|
/*
|
|
* Push the next search node in the search stack.
|
|
*/
|
|
IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
|
|
|
|
/*
|
|
* continue the iteration.
|
|
*/
|
|
currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
|
|
}
|
|
|
|
if (objectFetched == celt)
|
|
return S_OK;
|
|
|
|
return S_FALSE;
|
|
}
|
|
|
|
HRESULT WINAPI IEnumSTATSTGImpl_Reset(
|
|
IEnumSTATSTG* iface)
|
|
{
|
|
IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
|
|
|
|
StgProperty rootProperty;
|
|
BOOL32 readSucessful;
|
|
|
|
/*
|
|
* Re-initialize the search stack to an empty stack
|
|
*/
|
|
This->stackSize = 0;
|
|
|
|
/*
|
|
* Read the root property from the storage.
|
|
*/
|
|
readSucessful = Storage32Impl_ReadProperty(
|
|
This->parentStorage,
|
|
This->firstPropertyNode,
|
|
&rootProperty);
|
|
|
|
if (readSucessful)
|
|
{
|
|
assert(rootProperty.sizeOfNameString!=0);
|
|
|
|
/*
|
|
* Push the search node in the search stack.
|
|
*/
|
|
IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT WINAPI IEnumSTATSTGImpl_Clone(
|
|
IEnumSTATSTG* iface,
|
|
IEnumSTATSTG** ppenum)
|
|
{
|
|
IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
|
|
|
|
IEnumSTATSTGImpl* newClone;
|
|
|
|
/*
|
|
* Perform a sanity check on the parameters.
|
|
*/
|
|
if (ppenum==0)
|
|
return E_INVALIDARG;
|
|
|
|
newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
|
|
This->firstPropertyNode);
|
|
|
|
|
|
/*
|
|
* The new clone enumeration must point to the same current node as
|
|
* the ole one.
|
|
*/
|
|
newClone->stackSize = This->stackSize ;
|
|
newClone->stackMaxSize = This->stackMaxSize ;
|
|
newClone->stackToVisit =
|
|
HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
|
|
|
|
memcpy(
|
|
newClone->stackToVisit,
|
|
This->stackToVisit,
|
|
sizeof(ULONG) * newClone->stackSize);
|
|
|
|
*ppenum = (IEnumSTATSTG*)newClone;
|
|
|
|
/*
|
|
* Don't forget to nail down a reference to the clone before
|
|
* returning it.
|
|
*/
|
|
IEnumSTATSTGImpl_AddRef(*ppenum);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
INT32 IEnumSTATSTGImpl_FindParentProperty(
|
|
IEnumSTATSTGImpl *This,
|
|
ULONG childProperty,
|
|
StgProperty *currentProperty,
|
|
ULONG *thisNodeId)
|
|
{
|
|
ULONG currentSearchNode;
|
|
ULONG foundNode;
|
|
|
|
/*
|
|
* To avoid the special case, get another pointer to a ULONG value if
|
|
* the caller didn't supply one.
|
|
*/
|
|
if (thisNodeId==0)
|
|
thisNodeId = &foundNode;
|
|
|
|
/*
|
|
* Start with the node at the top of the stack.
|
|
*/
|
|
currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
|
|
|
|
|
|
while (currentSearchNode!=PROPERTY_NULL)
|
|
{
|
|
/*
|
|
* Store the current node in the returned parameters
|
|
*/
|
|
*thisNodeId = currentSearchNode;
|
|
|
|
/*
|
|
* Remove the top node from the stack
|
|
*/
|
|
IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
|
|
|
|
/*
|
|
* Read the property from the storage.
|
|
*/
|
|
Storage32Impl_ReadProperty(
|
|
This->parentStorage,
|
|
currentSearchNode,
|
|
currentProperty);
|
|
|
|
if (currentProperty->previousProperty == childProperty)
|
|
return PROPERTY_RELATION_PREVIOUS;
|
|
|
|
else if (currentProperty->nextProperty == childProperty)
|
|
return PROPERTY_RELATION_NEXT;
|
|
|
|
else if (currentProperty->dirProperty == childProperty)
|
|
return PROPERTY_RELATION_DIR;
|
|
|
|
/*
|
|
* Push the next search node in the search stack.
|
|
*/
|
|
IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
|
|
|
|
/*
|
|
* continue the iteration.
|
|
*/
|
|
currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
|
|
}
|
|
|
|
return PROPERTY_NULL;
|
|
}
|
|
|
|
ULONG IEnumSTATSTGImpl_FindProperty(
|
|
IEnumSTATSTGImpl* This,
|
|
const OLECHAR32* lpszPropName,
|
|
StgProperty* currentProperty)
|
|
{
|
|
ULONG currentSearchNode;
|
|
|
|
/*
|
|
* Start with the node at the top of the stack.
|
|
*/
|
|
currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
|
|
|
|
while (currentSearchNode!=PROPERTY_NULL)
|
|
{
|
|
/*
|
|
* Remove the top node from the stack
|
|
*/
|
|
IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
|
|
|
|
/*
|
|
* Read the property from the storage.
|
|
*/
|
|
Storage32Impl_ReadProperty(This->parentStorage,
|
|
currentSearchNode,
|
|
currentProperty);
|
|
|
|
if ( propertyNameCmp(
|
|
(OLECHAR32*)currentProperty->name,
|
|
(OLECHAR32*)lpszPropName) == 0)
|
|
return currentSearchNode;
|
|
|
|
/*
|
|
* Push the next search node in the search stack.
|
|
*/
|
|
IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
|
|
|
|
/*
|
|
* continue the iteration.
|
|
*/
|
|
currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
|
|
}
|
|
|
|
return PROPERTY_NULL;
|
|
}
|
|
|
|
void IEnumSTATSTGImpl_PushSearchNode(
|
|
IEnumSTATSTGImpl* This,
|
|
ULONG nodeToPush)
|
|
{
|
|
StgProperty rootProperty;
|
|
BOOL32 readSucessful;
|
|
|
|
/*
|
|
* First, make sure we're not trying to push an unexisting node.
|
|
*/
|
|
if (nodeToPush==PROPERTY_NULL)
|
|
return;
|
|
|
|
/*
|
|
* First push the node to the stack
|
|
*/
|
|
if (This->stackSize == This->stackMaxSize)
|
|
{
|
|
This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
|
|
|
|
This->stackToVisit = HeapReAlloc(
|
|
GetProcessHeap(),
|
|
0,
|
|
This->stackToVisit,
|
|
sizeof(ULONG) * This->stackMaxSize);
|
|
}
|
|
|
|
This->stackToVisit[This->stackSize] = nodeToPush;
|
|
This->stackSize++;
|
|
|
|
/*
|
|
* Read the root property from the storage.
|
|
*/
|
|
readSucessful = Storage32Impl_ReadProperty(
|
|
This->parentStorage,
|
|
nodeToPush,
|
|
&rootProperty);
|
|
|
|
if (readSucessful)
|
|
{
|
|
assert(rootProperty.sizeOfNameString!=0);
|
|
|
|
/*
|
|
* Push the previous search node in the search stack.
|
|
*/
|
|
IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
|
|
}
|
|
}
|
|
|
|
ULONG IEnumSTATSTGImpl_PopSearchNode(
|
|
IEnumSTATSTGImpl* This,
|
|
BOOL32 remove)
|
|
{
|
|
ULONG topNode;
|
|
|
|
if (This->stackSize == 0)
|
|
return PROPERTY_NULL;
|
|
|
|
topNode = This->stackToVisit[This->stackSize-1];
|
|
|
|
if (remove)
|
|
This->stackSize--;
|
|
|
|
return topNode;
|
|
}
|
|
|
|
/******************************************************************************
|
|
** StorageUtl implementation
|
|
*/
|
|
|
|
void StorageUtl_ReadWord(void* buffer, ULONG offset, WORD* value)
|
|
{
|
|
memcpy(value, (BYTE*)buffer+offset, sizeof(WORD));
|
|
}
|
|
|
|
void StorageUtl_WriteWord(void* buffer, ULONG offset, WORD value)
|
|
{
|
|
memcpy((BYTE*)buffer+offset, &value, sizeof(WORD));
|
|
}
|
|
|
|
void StorageUtl_ReadDWord(void* buffer, ULONG offset, DWORD* value)
|
|
{
|
|
memcpy(value, (BYTE*)buffer+offset, sizeof(DWORD));
|
|
}
|
|
|
|
void StorageUtl_WriteDWord(void* buffer, ULONG offset, DWORD value)
|
|
{
|
|
memcpy((BYTE*)buffer+offset, &value, sizeof(DWORD));
|
|
}
|
|
|
|
void StorageUtl_ReadGUID(void* buffer, ULONG offset, GUID* value)
|
|
{
|
|
StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
|
|
StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
|
|
StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
|
|
|
|
memcpy(value->Data4, (BYTE*)buffer+offset+8, sizeof(value->Data4));
|
|
}
|
|
|
|
void StorageUtl_WriteGUID(void* buffer, ULONG offset, GUID* value)
|
|
{
|
|
StorageUtl_WriteDWord(buffer, offset, value->Data1);
|
|
StorageUtl_WriteWord(buffer, offset+4, value->Data2);
|
|
StorageUtl_WriteWord(buffer, offset+6, value->Data3);
|
|
|
|
memcpy((BYTE*)buffer+offset+8, value->Data4, sizeof(value->Data4));
|
|
}
|
|
|
|
void StorageUtl_CopyPropertyToSTATSTG(
|
|
STATSTG* destination,
|
|
StgProperty* source,
|
|
int statFlags)
|
|
{
|
|
/*
|
|
* The copy of the string occurs only when the flag is not set
|
|
*/
|
|
if ((statFlags & STATFLAG_NONAME) != 0)
|
|
{
|
|
destination->pwcsName = 0;
|
|
}
|
|
else
|
|
{
|
|
destination->pwcsName =
|
|
CoTaskMemAlloc((lstrlen32W(source->name)+1)*sizeof(WCHAR));
|
|
|
|
lstrcpy32W((LPWSTR)destination->pwcsName, source->name);
|
|
}
|
|
|
|
switch (source->propertyType)
|
|
{
|
|
case PROPTYPE_STORAGE:
|
|
case PROPTYPE_ROOT:
|
|
destination->type = STGTY_STORAGE;
|
|
break;
|
|
case PROPTYPE_STREAM:
|
|
destination->type = STGTY_STREAM;
|
|
break;
|
|
default:
|
|
destination->type = STGTY_STREAM;
|
|
break;
|
|
}
|
|
|
|
destination->cbSize = source->size;
|
|
/*
|
|
currentReturnStruct->mtime = {0}; TODO
|
|
currentReturnStruct->ctime = {0};
|
|
currentReturnStruct->atime = {0};
|
|
*/
|
|
destination->grfMode = 0;
|
|
destination->grfLocksSupported = 0;
|
|
destination->clsid = source->propertyUniqueID;
|
|
destination->grfStateBits = 0;
|
|
destination->reserved = 0;
|
|
}
|
|
|
|
/******************************************************************************
|
|
** BlockChainStream implementation
|
|
*/
|
|
|
|
BlockChainStream* BlockChainStream_Construct(
|
|
Storage32Impl* parentStorage,
|
|
ULONG* headOfStreamPlaceHolder,
|
|
ULONG propertyIndex)
|
|
{
|
|
BlockChainStream* newStream;
|
|
|
|
newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
|
|
|
|
newStream->parentStorage = parentStorage;
|
|
newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
|
|
newStream->ownerPropertyIndex = propertyIndex;
|
|
|
|
return newStream;
|
|
}
|
|
|
|
void BlockChainStream_Destroy(BlockChainStream* This)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, This);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* BlockChainStream_GetHeadOfChain
|
|
*
|
|
* Returns the head of this stream chain.
|
|
* Some special chains don't have properties, their heads are kept in
|
|
* This->headOfStreamPlaceHolder.
|
|
*
|
|
*/
|
|
ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
|
|
{
|
|
StgProperty chainProperty;
|
|
BOOL32 readSucessful;
|
|
|
|
if (This->headOfStreamPlaceHolder != 0)
|
|
return *(This->headOfStreamPlaceHolder);
|
|
|
|
if (This->ownerPropertyIndex != PROPERTY_NULL)
|
|
{
|
|
readSucessful = Storage32Impl_ReadProperty(
|
|
This->parentStorage,
|
|
This->ownerPropertyIndex,
|
|
&chainProperty);
|
|
|
|
if (readSucessful)
|
|
{
|
|
return chainProperty.startingBlock;
|
|
}
|
|
}
|
|
|
|
return BLOCK_END_OF_CHAIN;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* BlockChainStream_GetCount
|
|
*
|
|
* Returns the number of blocks that comprises this chain.
|
|
* This is not the size of the stream as the last block may not be full!
|
|
*
|
|
*/
|
|
ULONG BlockChainStream_GetCount(BlockChainStream* This)
|
|
{
|
|
ULONG blockIndex;
|
|
ULONG count = 0;
|
|
|
|
blockIndex = BlockChainStream_GetHeadOfChain(This);
|
|
|
|
while (blockIndex != BLOCK_END_OF_CHAIN)
|
|
{
|
|
count++;
|
|
|
|
blockIndex = Storage32Impl_GetNextBlockInChain(
|
|
This->parentStorage,
|
|
blockIndex);
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* BlockChainStream_ReadAt
|
|
*
|
|
* Reads a specified number of bytes from this chain at the specified offset.
|
|
* bytesRead may be NULL.
|
|
* Failure will be returned if the specified number of bytes has not been read.
|
|
*/
|
|
BOOL32 BlockChainStream_ReadAt(BlockChainStream* This,
|
|
ULARGE_INTEGER offset,
|
|
ULONG size,
|
|
void* buffer,
|
|
ULONG* bytesRead)
|
|
{
|
|
ULONG blockNoInSequence = offset.LowPart / This->parentStorage->bigBlockSize;
|
|
ULONG offsetInBlock = offset.LowPart % This->parentStorage->bigBlockSize;
|
|
ULONG bytesToReadInBuffer;
|
|
ULONG blockIndex;
|
|
BYTE* bufferWalker;
|
|
BYTE* bigBlockBuffer;
|
|
|
|
/*
|
|
* Find the first block in the stream that contains part of the buffer.
|
|
*/
|
|
blockIndex = BlockChainStream_GetHeadOfChain(This);
|
|
|
|
while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
|
|
{
|
|
blockIndex =
|
|
Storage32Impl_GetNextBlockInChain(This->parentStorage, blockIndex);
|
|
|
|
blockNoInSequence--;
|
|
}
|
|
|
|
/*
|
|
* Start reading the buffer.
|
|
*/
|
|
*bytesRead = 0;
|
|
bufferWalker = buffer;
|
|
|
|
while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
|
|
{
|
|
/*
|
|
* Calculate how many bytes we can copy from this big block.
|
|
*/
|
|
bytesToReadInBuffer =
|
|
MIN(This->parentStorage->bigBlockSize - offsetInBlock, size);
|
|
|
|
/*
|
|
* Copy those bytes to the buffer
|
|
*/
|
|
bigBlockBuffer =
|
|
Storage32Impl_GetROBigBlock(This->parentStorage, blockIndex);
|
|
|
|
memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
|
|
|
|
Storage32Impl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
|
|
|
|
/*
|
|
* Step to the next big block.
|
|
*/
|
|
blockIndex =
|
|
Storage32Impl_GetNextBlockInChain(This->parentStorage, blockIndex);
|
|
|
|
bufferWalker += bytesToReadInBuffer;
|
|
size -= bytesToReadInBuffer;
|
|
*bytesRead += bytesToReadInBuffer;
|
|
offsetInBlock = 0; /* There is no offset on the next block */
|
|
|
|
}
|
|
|
|
return (size == 0);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* BlockChainStream_WriteAt
|
|
*
|
|
* Writes the specified number of bytes to this chain at the specified offset.
|
|
* bytesWritten may be NULL.
|
|
* Will fail if not all specified number of bytes have been written.
|
|
*/
|
|
BOOL32 BlockChainStream_WriteAt(BlockChainStream* This,
|
|
ULARGE_INTEGER offset,
|
|
ULONG size,
|
|
const void* buffer,
|
|
ULONG* bytesWritten)
|
|
{
|
|
ULONG blockNoInSequence = offset.LowPart / This->parentStorage->bigBlockSize;
|
|
ULONG offsetInBlock = offset.LowPart % This->parentStorage->bigBlockSize;
|
|
ULONG bytesToWrite;
|
|
ULONG blockIndex;
|
|
BYTE* bufferWalker;
|
|
BYTE* bigBlockBuffer;
|
|
|
|
/*
|
|
* Find the first block in the stream that contains part of the buffer.
|
|
*/
|
|
blockIndex = BlockChainStream_GetHeadOfChain(This);
|
|
|
|
while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
|
|
{
|
|
blockIndex =
|
|
Storage32Impl_GetNextBlockInChain(This->parentStorage, blockIndex);
|
|
|
|
blockNoInSequence--;
|
|
}
|
|
|
|
/*
|
|
* Here, I'm casting away the constness on the buffer variable
|
|
* This is OK since we don't intend to modify that buffer.
|
|
*/
|
|
*bytesWritten = 0;
|
|
bufferWalker = (BYTE*)buffer;
|
|
|
|
while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
|
|
{
|
|
/*
|
|
* Calculate how many bytes we can copy from this big block.
|
|
*/
|
|
bytesToWrite =
|
|
MIN(This->parentStorage->bigBlockSize - offsetInBlock, size);
|
|
|
|
/*
|
|
* Copy those bytes to the buffer
|
|
*/
|
|
bigBlockBuffer = Storage32Impl_GetBigBlock(This->parentStorage, blockIndex);
|
|
|
|
memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
|
|
|
|
Storage32Impl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
|
|
|
|
/*
|
|
* Step to the next big block.
|
|
*/
|
|
blockIndex =
|
|
Storage32Impl_GetNextBlockInChain(This->parentStorage, blockIndex);
|
|
|
|
bufferWalker += bytesToWrite;
|
|
size -= bytesToWrite;
|
|
*bytesWritten += bytesToWrite;
|
|
offsetInBlock = 0; /* There is no offset on the next block */
|
|
}
|
|
|
|
return (size == 0);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* BlockChainStream_Shrink
|
|
*
|
|
* Shrinks this chain in the big block depot.
|
|
*/
|
|
BOOL32 BlockChainStream_Shrink(BlockChainStream* This,
|
|
ULARGE_INTEGER newSize)
|
|
{
|
|
ULONG blockIndex, extraBlock;
|
|
ULONG numBlocks;
|
|
ULONG count = 1;
|
|
|
|
/*
|
|
* Figure out how many blocks are needed to contain the new size
|
|
*/
|
|
numBlocks = newSize.LowPart / This->parentStorage->bigBlockSize;
|
|
|
|
if ((newSize.LowPart % This->parentStorage->bigBlockSize) != 0)
|
|
numBlocks++;
|
|
|
|
blockIndex = BlockChainStream_GetHeadOfChain(This);
|
|
|
|
/*
|
|
* Go to the new end of chain
|
|
*/
|
|
while (count < numBlocks)
|
|
{
|
|
blockIndex =
|
|
Storage32Impl_GetNextBlockInChain(This->parentStorage, blockIndex);
|
|
|
|
count++;
|
|
}
|
|
|
|
/* Get the next block before marking the new end */
|
|
extraBlock =
|
|
Storage32Impl_GetNextBlockInChain(This->parentStorage, blockIndex);
|
|
|
|
/* Mark the new end of chain */
|
|
Storage32Impl_SetNextBlockInChain(
|
|
This->parentStorage,
|
|
blockIndex,
|
|
BLOCK_END_OF_CHAIN);
|
|
|
|
/*
|
|
* Mark the extra blocks as free
|
|
*/
|
|
while (extraBlock != BLOCK_END_OF_CHAIN)
|
|
{
|
|
blockIndex =
|
|
Storage32Impl_GetNextBlockInChain(This->parentStorage, extraBlock);
|
|
|
|
Storage32Impl_FreeBigBlock(This->parentStorage, extraBlock);
|
|
extraBlock = blockIndex;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* BlockChainStream_Enlarge
|
|
*
|
|
* Grows this chain in the big block depot.
|
|
*/
|
|
BOOL32 BlockChainStream_Enlarge(BlockChainStream* This,
|
|
ULARGE_INTEGER newSize)
|
|
{
|
|
ULONG blockIndex, currentBlock;
|
|
ULONG newNumBlocks;
|
|
ULONG oldNumBlocks = 0;
|
|
|
|
blockIndex = BlockChainStream_GetHeadOfChain(This);
|
|
|
|
/*
|
|
* Empty chain. Create the head.
|
|
*/
|
|
if (blockIndex == BLOCK_END_OF_CHAIN)
|
|
{
|
|
blockIndex = Storage32Impl_GetNextFreeBigBlock(This->parentStorage);
|
|
Storage32Impl_SetNextBlockInChain(This->parentStorage,
|
|
blockIndex,
|
|
BLOCK_END_OF_CHAIN);
|
|
|
|
if (This->headOfStreamPlaceHolder != 0)
|
|
{
|
|
*(This->headOfStreamPlaceHolder) = blockIndex;
|
|
}
|
|
else
|
|
{
|
|
StgProperty chainProp;
|
|
assert(This->ownerPropertyIndex != PROPERTY_NULL);
|
|
|
|
Storage32Impl_ReadProperty(
|
|
This->parentStorage,
|
|
This->ownerPropertyIndex,
|
|
&chainProp);
|
|
|
|
chainProp.startingBlock = blockIndex;
|
|
|
|
Storage32Impl_WriteProperty(
|
|
This->parentStorage,
|
|
This->ownerPropertyIndex,
|
|
&chainProp);
|
|
}
|
|
}
|
|
|
|
currentBlock = blockIndex;
|
|
|
|
/*
|
|
* Figure out how many blocks are needed to contain this stream
|
|
*/
|
|
newNumBlocks = newSize.LowPart / This->parentStorage->bigBlockSize;
|
|
|
|
if ((newSize.LowPart % This->parentStorage->bigBlockSize) != 0)
|
|
newNumBlocks++;
|
|
|
|
/*
|
|
* Go to the current end of chain
|
|
*/
|
|
while (blockIndex != BLOCK_END_OF_CHAIN)
|
|
{
|
|
oldNumBlocks++;
|
|
currentBlock = blockIndex;
|
|
|
|
blockIndex =
|
|
Storage32Impl_GetNextBlockInChain(This->parentStorage, currentBlock);
|
|
}
|
|
|
|
/*
|
|
* Add new blocks to the chain
|
|
*/
|
|
while (oldNumBlocks < newNumBlocks)
|
|
{
|
|
blockIndex = Storage32Impl_GetNextFreeBigBlock(This->parentStorage);
|
|
|
|
Storage32Impl_SetNextBlockInChain(
|
|
This->parentStorage,
|
|
currentBlock,
|
|
blockIndex);
|
|
|
|
Storage32Impl_SetNextBlockInChain(
|
|
This->parentStorage,
|
|
blockIndex,
|
|
BLOCK_END_OF_CHAIN);
|
|
|
|
currentBlock = blockIndex;
|
|
oldNumBlocks++;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* BlockChainStream_SetSize
|
|
*
|
|
* Sets the size of this stream. The big block depot will be updated.
|
|
* The file will grow if we grow the chain.
|
|
*
|
|
* TODO: Free the actual blocks in the file when we shrink the chain.
|
|
* Currently, the blocks are still in the file. So the file size
|
|
* doesn't shrink even if we shrink streams.
|
|
*/
|
|
BOOL32 BlockChainStream_SetSize(
|
|
BlockChainStream* This,
|
|
ULARGE_INTEGER newSize)
|
|
{
|
|
ULARGE_INTEGER size = BlockChainStream_GetSize(This);
|
|
|
|
if (newSize.LowPart == size.LowPart)
|
|
return TRUE;
|
|
|
|
if (newSize.LowPart < size.LowPart)
|
|
{
|
|
BlockChainStream_Shrink(This, newSize);
|
|
}
|
|
else
|
|
{
|
|
ULARGE_INTEGER fileSize =
|
|
BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
|
|
|
|
ULONG diff = newSize.LowPart - size.LowPart;
|
|
|
|
/*
|
|
* Make sure the file stays a multiple of blocksize
|
|
*/
|
|
if ((diff % This->parentStorage->bigBlockSize) != 0)
|
|
diff += (This->parentStorage->bigBlockSize -
|
|
(diff % This->parentStorage->bigBlockSize) );
|
|
|
|
fileSize.LowPart += diff;
|
|
BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
|
|
|
|
BlockChainStream_Enlarge(This, newSize);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* BlockChainStream_GetSize
|
|
*
|
|
* Returns the size of this chain.
|
|
* Will return the block count if this chain doesn't have a property.
|
|
*/
|
|
ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
|
|
{
|
|
StgProperty chainProperty;
|
|
|
|
if(This->headOfStreamPlaceHolder == NULL)
|
|
{
|
|
/*
|
|
* This chain is a data stream read the property and return
|
|
* the appropriate size
|
|
*/
|
|
Storage32Impl_ReadProperty(
|
|
This->parentStorage,
|
|
This->ownerPropertyIndex,
|
|
&chainProperty);
|
|
|
|
return chainProperty.size;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* this chain is a chain that does not have a property, figure out the
|
|
* size by making the product number of used blocks times the
|
|
* size of them
|
|
*/
|
|
ULARGE_INTEGER result;
|
|
result.HighPart = 0;
|
|
|
|
result.LowPart =
|
|
BlockChainStream_GetCount(This) *
|
|
This->parentStorage->bigBlockSize;
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
** SmallBlockChainStream implementation
|
|
*/
|
|
|
|
SmallBlockChainStream* SmallBlockChainStream_Construct(
|
|
Storage32Impl* parentStorage,
|
|
ULONG propertyIndex)
|
|
{
|
|
SmallBlockChainStream* newStream;
|
|
|
|
newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
|
|
|
|
newStream->parentStorage = parentStorage;
|
|
newStream->ownerPropertyIndex = propertyIndex;
|
|
|
|
return newStream;
|
|
}
|
|
|
|
void SmallBlockChainStream_Destroy(
|
|
SmallBlockChainStream* This)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, This);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* SmallBlockChainStream_GetHeadOfChain
|
|
*
|
|
* Returns the head of this chain of small blocks.
|
|
*/
|
|
ULONG SmallBlockChainStream_GetHeadOfChain(
|
|
SmallBlockChainStream* This)
|
|
{
|
|
StgProperty chainProperty;
|
|
BOOL32 readSucessful;
|
|
|
|
if (This->ownerPropertyIndex)
|
|
{
|
|
readSucessful = Storage32Impl_ReadProperty(
|
|
This->parentStorage,
|
|
This->ownerPropertyIndex,
|
|
&chainProperty);
|
|
|
|
if (readSucessful)
|
|
{
|
|
return chainProperty.startingBlock;
|
|
}
|
|
|
|
}
|
|
|
|
return BLOCK_END_OF_CHAIN;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* SmallBlockChainStream_GetNextBlockInChain
|
|
*
|
|
* Returns the index of the next small block in this chain.
|
|
*
|
|
* Return Values:
|
|
* - BLOCK_END_OF_CHAIN: end of this chain
|
|
* - BLOCK_UNUSED: small block 'blockIndex' is free
|
|
*/
|
|
ULONG SmallBlockChainStream_GetNextBlockInChain(
|
|
SmallBlockChainStream* This,
|
|
ULONG blockIndex)
|
|
{
|
|
ULARGE_INTEGER offsetOfBlockInDepot;
|
|
DWORD buffer;
|
|
ULONG nextBlockInChain = BLOCK_END_OF_CHAIN;
|
|
ULONG bytesRead;
|
|
BOOL32 success;
|
|
|
|
offsetOfBlockInDepot.HighPart = 0;
|
|
offsetOfBlockInDepot.LowPart = blockIndex * sizeof(ULONG);
|
|
|
|
/*
|
|
* Read those bytes in the buffer from the small block file.
|
|
*/
|
|
success = BlockChainStream_ReadAt(
|
|
This->parentStorage->smallBlockDepotChain,
|
|
offsetOfBlockInDepot,
|
|
sizeof(DWORD),
|
|
&buffer,
|
|
&bytesRead);
|
|
|
|
if (success)
|
|
{
|
|
StorageUtl_ReadDWord(&buffer, 0, &nextBlockInChain);
|
|
}
|
|
|
|
return nextBlockInChain;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* SmallBlockChainStream_SetNextBlockInChain
|
|
*
|
|
* Writes the index of the next block of the specified block in the small
|
|
* block depot.
|
|
* To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
|
|
* To flag a block as free use BLOCK_UNUSED as nextBlock.
|
|
*/
|
|
void SmallBlockChainStream_SetNextBlockInChain(
|
|
SmallBlockChainStream* This,
|
|
ULONG blockIndex,
|
|
ULONG nextBlock)
|
|
{
|
|
ULARGE_INTEGER offsetOfBlockInDepot;
|
|
DWORD buffer;
|
|
ULONG bytesWritten;
|
|
|
|
offsetOfBlockInDepot.HighPart = 0;
|
|
offsetOfBlockInDepot.LowPart = blockIndex * sizeof(ULONG);
|
|
|
|
StorageUtl_WriteDWord(&buffer, 0, nextBlock);
|
|
|
|
/*
|
|
* Read those bytes in the buffer from the small block file.
|
|
*/
|
|
BlockChainStream_WriteAt(
|
|
This->parentStorage->smallBlockDepotChain,
|
|
offsetOfBlockInDepot,
|
|
sizeof(DWORD),
|
|
&buffer,
|
|
&bytesWritten);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* SmallBlockChainStream_FreeBlock
|
|
*
|
|
* Flag small block 'blockIndex' as free in the small block depot.
|
|
*/
|
|
void SmallBlockChainStream_FreeBlock(
|
|
SmallBlockChainStream* This,
|
|
ULONG blockIndex)
|
|
{
|
|
SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* SmallBlockChainStream_GetNextFreeBlock
|
|
*
|
|
* Returns the index of a free small block. The small block depot will be
|
|
* enlarged if necessary. The small block chain will also be enlarged if
|
|
* necessary.
|
|
*/
|
|
ULONG SmallBlockChainStream_GetNextFreeBlock(
|
|
SmallBlockChainStream* This)
|
|
{
|
|
ULARGE_INTEGER offsetOfBlockInDepot;
|
|
DWORD buffer;
|
|
ULONG bytesRead;
|
|
ULONG blockIndex = 0;
|
|
ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
|
|
BOOL32 success = TRUE;
|
|
ULONG smallBlocksPerBigBlock;
|
|
|
|
offsetOfBlockInDepot.HighPart = 0;
|
|
|
|
/*
|
|
* Scan the small block depot for a free block
|
|
*/
|
|
while (nextBlockIndex != BLOCK_UNUSED)
|
|
{
|
|
offsetOfBlockInDepot.LowPart = blockIndex * sizeof(ULONG);
|
|
|
|
success = BlockChainStream_ReadAt(
|
|
This->parentStorage->smallBlockDepotChain,
|
|
offsetOfBlockInDepot,
|
|
sizeof(DWORD),
|
|
&buffer,
|
|
&bytesRead);
|
|
|
|
/*
|
|
* If we run out of space for the small block depot, enlarge it
|
|
*/
|
|
if (success)
|
|
{
|
|
StorageUtl_ReadDWord(&buffer, 0, &nextBlockIndex);
|
|
|
|
if (nextBlockIndex != BLOCK_UNUSED)
|
|
blockIndex++;
|
|
}
|
|
else
|
|
{
|
|
ULONG count =
|
|
BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
|
|
|
|
ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
|
|
ULONG nextBlock, newsbdIndex;
|
|
BYTE* smallBlockDepot;
|
|
|
|
nextBlock = sbdIndex;
|
|
while (nextBlock != BLOCK_END_OF_CHAIN)
|
|
{
|
|
sbdIndex = nextBlock;
|
|
nextBlock =
|
|
Storage32Impl_GetNextBlockInChain(This->parentStorage, sbdIndex);
|
|
}
|
|
|
|
newsbdIndex = Storage32Impl_GetNextFreeBigBlock(This->parentStorage);
|
|
if (sbdIndex != BLOCK_END_OF_CHAIN)
|
|
Storage32Impl_SetNextBlockInChain(
|
|
This->parentStorage,
|
|
sbdIndex,
|
|
newsbdIndex);
|
|
|
|
Storage32Impl_SetNextBlockInChain(
|
|
This->parentStorage,
|
|
newsbdIndex,
|
|
BLOCK_END_OF_CHAIN);
|
|
|
|
/*
|
|
* Initialize all the small blocks to free
|
|
*/
|
|
smallBlockDepot =
|
|
Storage32Impl_GetBigBlock(This->parentStorage, newsbdIndex);
|
|
|
|
memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
|
|
Storage32Impl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
|
|
|
|
if (count == 0)
|
|
{
|
|
/*
|
|
* We have just created the small block depot.
|
|
*/
|
|
StgProperty rootProp;
|
|
ULONG sbStartIndex;
|
|
|
|
/*
|
|
* Save it in the header
|
|
*/
|
|
This->parentStorage->smallBlockDepotStart = newsbdIndex;
|
|
Storage32Impl_SaveFileHeader(This->parentStorage);
|
|
|
|
/*
|
|
* And allocate the first big block that will contain small blocks
|
|
*/
|
|
sbStartIndex =
|
|
Storage32Impl_GetNextFreeBigBlock(This->parentStorage);
|
|
|
|
Storage32Impl_SetNextBlockInChain(
|
|
This->parentStorage,
|
|
sbStartIndex,
|
|
BLOCK_END_OF_CHAIN);
|
|
|
|
Storage32Impl_ReadProperty(
|
|
This->parentStorage,
|
|
This->parentStorage->rootPropertySetIndex,
|
|
&rootProp);
|
|
|
|
rootProp.startingBlock = sbStartIndex;
|
|
rootProp.size.HighPart = 0;
|
|
rootProp.size.LowPart = This->parentStorage->bigBlockSize;
|
|
|
|
Storage32Impl_WriteProperty(
|
|
This->parentStorage,
|
|
This->parentStorage->rootPropertySetIndex,
|
|
&rootProp);
|
|
}
|
|
}
|
|
}
|
|
|
|
smallBlocksPerBigBlock =
|
|
This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
|
|
|
|
/*
|
|
* Verify if we have to allocate big blocks to contain small blocks
|
|
*/
|
|
if (blockIndex % smallBlocksPerBigBlock == 0)
|
|
{
|
|
StgProperty rootProp;
|
|
ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
|
|
|
|
Storage32Impl_ReadProperty(
|
|
This->parentStorage,
|
|
This->parentStorage->rootPropertySetIndex,
|
|
&rootProp);
|
|
|
|
if (rootProp.size.LowPart <
|
|
(blocksRequired * This->parentStorage->bigBlockSize))
|
|
{
|
|
rootProp.size.LowPart += This->parentStorage->bigBlockSize;
|
|
|
|
BlockChainStream_SetSize(
|
|
This->parentStorage->smallBlockRootChain,
|
|
rootProp.size);
|
|
|
|
Storage32Impl_WriteProperty(
|
|
This->parentStorage,
|
|
This->parentStorage->rootPropertySetIndex,
|
|
&rootProp);
|
|
}
|
|
}
|
|
|
|
return blockIndex;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* SmallBlockChainStream_ReadAt
|
|
*
|
|
* Reads a specified number of bytes from this chain at the specified offset.
|
|
* bytesRead may be NULL.
|
|
* Failure will be returned if the specified number of bytes has not been read.
|
|
*/
|
|
BOOL32 SmallBlockChainStream_ReadAt(
|
|
SmallBlockChainStream* This,
|
|
ULARGE_INTEGER offset,
|
|
ULONG size,
|
|
void* buffer,
|
|
ULONG* bytesRead)
|
|
{
|
|
ULARGE_INTEGER offsetInBigBlockFile;
|
|
ULONG blockNoInSequence =
|
|
offset.LowPart / This->parentStorage->smallBlockSize;
|
|
|
|
ULONG offsetInBlock = offset.LowPart % This->parentStorage->smallBlockSize;
|
|
ULONG bytesToReadInBuffer;
|
|
ULONG blockIndex;
|
|
ULONG bytesReadFromBigBlockFile;
|
|
BYTE* bufferWalker;
|
|
|
|
/*
|
|
* This should never happen on a small block file.
|
|
*/
|
|
assert(offset.HighPart==0);
|
|
|
|
/*
|
|
* Find the first block in the stream that contains part of the buffer.
|
|
*/
|
|
blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
|
|
|
|
while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
|
|
{
|
|
blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
|
|
|
|
blockNoInSequence--;
|
|
}
|
|
|
|
/*
|
|
* Start reading the buffer.
|
|
*/
|
|
*bytesRead = 0;
|
|
bufferWalker = buffer;
|
|
|
|
while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
|
|
{
|
|
/*
|
|
* Calculate how many bytes we can copy from this small block.
|
|
*/
|
|
bytesToReadInBuffer =
|
|
MIN(This->parentStorage->smallBlockSize - offsetInBlock, size);
|
|
|
|
/*
|
|
* Calculate the offset of the small block in the small block file.
|
|
*/
|
|
offsetInBigBlockFile.HighPart = 0;
|
|
offsetInBigBlockFile.LowPart =
|
|
blockIndex * This->parentStorage->smallBlockSize;
|
|
|
|
offsetInBigBlockFile.LowPart += offsetInBlock;
|
|
|
|
/*
|
|
* Read those bytes in the buffer from the small block file.
|
|
*/
|
|
BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
|
|
offsetInBigBlockFile,
|
|
bytesToReadInBuffer,
|
|
bufferWalker,
|
|
&bytesReadFromBigBlockFile);
|
|
|
|
assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
|
|
|
|
/*
|
|
* Step to the next big block.
|
|
*/
|
|
blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
|
|
bufferWalker += bytesToReadInBuffer;
|
|
size -= bytesToReadInBuffer;
|
|
*bytesRead += bytesToReadInBuffer;
|
|
offsetInBlock = 0; /* There is no offset on the next block */
|
|
}
|
|
|
|
return (size == 0);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* SmallBlockChainStream_WriteAt
|
|
*
|
|
* Writes the specified number of bytes to this chain at the specified offset.
|
|
* bytesWritten may be NULL.
|
|
* Will fail if not all specified number of bytes have been written.
|
|
*/
|
|
BOOL32 SmallBlockChainStream_WriteAt(
|
|
SmallBlockChainStream* This,
|
|
ULARGE_INTEGER offset,
|
|
ULONG size,
|
|
const void* buffer,
|
|
ULONG* bytesWritten)
|
|
{
|
|
ULARGE_INTEGER offsetInBigBlockFile;
|
|
ULONG blockNoInSequence =
|
|
offset.LowPart / This->parentStorage->smallBlockSize;
|
|
|
|
ULONG offsetInBlock = offset.LowPart % This->parentStorage->smallBlockSize;
|
|
ULONG bytesToWriteInBuffer;
|
|
ULONG blockIndex;
|
|
ULONG bytesWrittenFromBigBlockFile;
|
|
BYTE* bufferWalker;
|
|
|
|
/*
|
|
* This should never happen on a small block file.
|
|
*/
|
|
assert(offset.HighPart==0);
|
|
|
|
/*
|
|
* Find the first block in the stream that contains part of the buffer.
|
|
*/
|
|
blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
|
|
|
|
while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
|
|
{
|
|
blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
|
|
|
|
blockNoInSequence--;
|
|
}
|
|
|
|
/*
|
|
* Start writing the buffer.
|
|
*
|
|
* Here, I'm casting away the constness on the buffer variable
|
|
* This is OK since we don't intend to modify that buffer.
|
|
*/
|
|
*bytesWritten = 0;
|
|
bufferWalker = (BYTE*)buffer;
|
|
while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
|
|
{
|
|
/*
|
|
* Calculate how many bytes we can copy to this small block.
|
|
*/
|
|
bytesToWriteInBuffer =
|
|
MIN(This->parentStorage->smallBlockSize - offsetInBlock, size);
|
|
|
|
/*
|
|
* Calculate the offset of the small block in the small block file.
|
|
*/
|
|
offsetInBigBlockFile.HighPart = 0;
|
|
offsetInBigBlockFile.LowPart =
|
|
blockIndex * This->parentStorage->smallBlockSize;
|
|
|
|
offsetInBigBlockFile.LowPart += offsetInBlock;
|
|
|
|
/*
|
|
* Write those bytes in the buffer to the small block file.
|
|
*/
|
|
BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
|
|
offsetInBigBlockFile,
|
|
bytesToWriteInBuffer,
|
|
bufferWalker,
|
|
&bytesWrittenFromBigBlockFile);
|
|
|
|
assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
|
|
|
|
/*
|
|
* Step to the next big block.
|
|
*/
|
|
blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
|
|
bufferWalker += bytesToWriteInBuffer;
|
|
size -= bytesToWriteInBuffer;
|
|
*bytesWritten += bytesToWriteInBuffer;
|
|
offsetInBlock = 0; /* There is no offset on the next block */
|
|
}
|
|
|
|
return (size == 0);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* SmallBlockChainStream_Shrink
|
|
*
|
|
* Shrinks this chain in the small block depot.
|
|
*/
|
|
BOOL32 SmallBlockChainStream_Shrink(
|
|
SmallBlockChainStream* This,
|
|
ULARGE_INTEGER newSize)
|
|
{
|
|
ULONG blockIndex, extraBlock;
|
|
ULONG numBlocks;
|
|
ULONG count = 1;
|
|
|
|
numBlocks = newSize.LowPart / This->parentStorage->smallBlockSize;
|
|
|
|
if ((newSize.LowPart % This->parentStorage->smallBlockSize) != 0)
|
|
numBlocks++;
|
|
|
|
blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
|
|
|
|
/*
|
|
* Go to the new end of chain
|
|
*/
|
|
while (count < numBlocks)
|
|
{
|
|
blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
|
|
count++;
|
|
}
|
|
|
|
/* Get the next block before marking the new end */
|
|
extraBlock = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
|
|
|
|
/* Mark the new end of chain */
|
|
SmallBlockChainStream_SetNextBlockInChain(
|
|
This,
|
|
blockIndex,
|
|
BLOCK_END_OF_CHAIN);
|
|
|
|
/*
|
|
* Mark the extra blocks as free
|
|
*/
|
|
while (extraBlock != BLOCK_END_OF_CHAIN)
|
|
{
|
|
blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, extraBlock);
|
|
SmallBlockChainStream_FreeBlock(This, extraBlock);
|
|
extraBlock = blockIndex;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* SmallBlockChainStream_Enlarge
|
|
*
|
|
* Grows this chain in the small block depot.
|
|
*/
|
|
BOOL32 SmallBlockChainStream_Enlarge(
|
|
SmallBlockChainStream* This,
|
|
ULARGE_INTEGER newSize)
|
|
{
|
|
ULONG blockIndex, currentBlock;
|
|
ULONG newNumBlocks;
|
|
ULONG oldNumBlocks = 0;
|
|
|
|
blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
|
|
|
|
/*
|
|
* Empty chain
|
|
*/
|
|
if (blockIndex == BLOCK_END_OF_CHAIN)
|
|
{
|
|
StgProperty chainProp;
|
|
|
|
Storage32Impl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
|
|
&chainProp);
|
|
|
|
chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
|
|
|
|
Storage32Impl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
|
|
&chainProp);
|
|
|
|
blockIndex = chainProp.startingBlock;
|
|
SmallBlockChainStream_SetNextBlockInChain(
|
|
This,
|
|
blockIndex,
|
|
BLOCK_END_OF_CHAIN);
|
|
}
|
|
|
|
currentBlock = blockIndex;
|
|
|
|
/*
|
|
* Figure out how many blocks are needed to contain this stream
|
|
*/
|
|
newNumBlocks = newSize.LowPart / This->parentStorage->smallBlockSize;
|
|
|
|
if ((newSize.LowPart % This->parentStorage->smallBlockSize) != 0)
|
|
newNumBlocks++;
|
|
|
|
/*
|
|
* Go to the current end of chain
|
|
*/
|
|
while (blockIndex != BLOCK_END_OF_CHAIN)
|
|
{
|
|
oldNumBlocks++;
|
|
currentBlock = blockIndex;
|
|
blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, currentBlock);
|
|
}
|
|
|
|
/*
|
|
* Add new blocks to the chain
|
|
*/
|
|
while (oldNumBlocks < newNumBlocks)
|
|
{
|
|
blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
|
|
SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
|
|
|
|
SmallBlockChainStream_SetNextBlockInChain(
|
|
This,
|
|
blockIndex,
|
|
BLOCK_END_OF_CHAIN);
|
|
|
|
currentBlock = blockIndex;
|
|
oldNumBlocks++;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* SmallBlockChainStream_GetCount
|
|
*
|
|
* Returns the number of blocks that comprises this chain.
|
|
* This is not the size of this chain as the last block may not be full!
|
|
*/
|
|
ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
|
|
{
|
|
ULONG blockIndex;
|
|
ULONG count = 0;
|
|
|
|
blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
|
|
|
|
while (blockIndex != BLOCK_END_OF_CHAIN)
|
|
{
|
|
count++;
|
|
|
|
blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* SmallBlockChainStream_SetSize
|
|
*
|
|
* Sets the size of this stream.
|
|
* The file will grow if we grow the chain.
|
|
*
|
|
* TODO: Free the actual blocks in the file when we shrink the chain.
|
|
* Currently, the blocks are still in the file. So the file size
|
|
* doesn't shrink even if we shrink streams.
|
|
*/
|
|
BOOL32 SmallBlockChainStream_SetSize(
|
|
SmallBlockChainStream* This,
|
|
ULARGE_INTEGER newSize)
|
|
{
|
|
ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
|
|
|
|
if (newSize.LowPart == size.LowPart)
|
|
return TRUE;
|
|
|
|
if (newSize.LowPart < size.LowPart)
|
|
{
|
|
SmallBlockChainStream_Shrink(This, newSize);
|
|
}
|
|
else
|
|
{
|
|
SmallBlockChainStream_Enlarge(This, newSize);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* SmallBlockChainStream_GetSize
|
|
*
|
|
* Returns the size of this chain.
|
|
*/
|
|
ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
|
|
{
|
|
StgProperty chainProperty;
|
|
|
|
Storage32Impl_ReadProperty(
|
|
This->parentStorage,
|
|
This->ownerPropertyIndex,
|
|
&chainProperty);
|
|
|
|
return chainProperty.size;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* StgCreateDocfile32 [OLE32.144]
|
|
* TODO Validate grfMode (STGM)
|
|
*/
|
|
HRESULT WINAPI StgCreateDocfile32(
|
|
LPCOLESTR32 pwcsName,
|
|
DWORD grfMode,
|
|
DWORD reserved,
|
|
IStorage32 **ppstgOpen)
|
|
{
|
|
Storage32Impl* newStorage = 0;
|
|
HANDLE32 hFile = INVALID_HANDLE_VALUE32;
|
|
HRESULT hr = S_OK;
|
|
DWORD shareMode;
|
|
DWORD accessMode;
|
|
DWORD creationMode;
|
|
DWORD fileAttributes;
|
|
|
|
/*
|
|
* Validate the parameters
|
|
*/
|
|
if ((ppstgOpen == 0) || (pwcsName == 0))
|
|
return STG_E_INVALIDPOINTER;
|
|
|
|
/*
|
|
* Validate the STGM flags
|
|
*/
|
|
if ( FAILED( validateSTGM(grfMode) ))
|
|
return STG_E_INVALIDFLAG;
|
|
|
|
/*
|
|
* Interpret the STGM value grfMode
|
|
*/
|
|
shareMode = GetShareModeFromSTGM(grfMode);
|
|
accessMode = GetAccessModeFromSTGM(grfMode);
|
|
creationMode = GetCreationModeFromSTGM(grfMode);
|
|
|
|
if (grfMode & STGM_DELETEONRELEASE)
|
|
fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
|
|
else
|
|
fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
|
|
|
|
if (grfMode & STGM_TRANSACTED)
|
|
FIXME(ole, "Transacted mode not implemented.\n");
|
|
|
|
/*
|
|
* Initialize the "out" parameter.
|
|
*/
|
|
*ppstgOpen = 0;
|
|
|
|
hFile = CreateFile32W(pwcsName,
|
|
accessMode,
|
|
shareMode,
|
|
NULL,
|
|
creationMode,
|
|
fileAttributes,
|
|
0);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE32)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
/*
|
|
* Allocate and initialize the new IStorage32object.
|
|
*/
|
|
newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(Storage32Impl));
|
|
|
|
if (newStorage == 0)
|
|
return STG_E_INSUFFICIENTMEMORY;
|
|
|
|
hr = Storage32Impl_Construct(
|
|
newStorage,
|
|
hFile,
|
|
grfMode);
|
|
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
/*
|
|
* Get an "out" pointer for the caller.
|
|
*/
|
|
hr = Storage32BaseImpl_QueryInterface(
|
|
(IStorage32*)newStorage,
|
|
(REFIID)&IID_IStorage,
|
|
(void**)ppstgOpen);
|
|
|
|
return hr;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* StgOpenStorage32 [OLE32.148]
|
|
*/
|
|
HRESULT WINAPI StgOpenStorage32(
|
|
const OLECHAR32 *pwcsName,
|
|
IStorage32 *pstgPriority,
|
|
DWORD grfMode,
|
|
SNB32 snbExclude,
|
|
DWORD reserved,
|
|
IStorage32 **ppstgOpen)
|
|
{
|
|
Storage32Impl* newStorage = 0;
|
|
HRESULT hr = S_OK;
|
|
HANDLE32 hFile = 0;
|
|
DWORD shareMode;
|
|
DWORD accessMode;
|
|
|
|
/*
|
|
* Perform a sanity check
|
|
*/
|
|
if (( pwcsName == 0) || (ppstgOpen == 0) )
|
|
return STG_E_INVALIDPOINTER;
|
|
|
|
/*
|
|
* Validate the STGM flags
|
|
*/
|
|
if ( FAILED( validateSTGM(grfMode) ))
|
|
return STG_E_INVALIDFLAG;
|
|
|
|
/*
|
|
* Interpret the STGM value grfMode
|
|
*/
|
|
shareMode = GetShareModeFromSTGM(grfMode);
|
|
accessMode = GetAccessModeFromSTGM(grfMode);
|
|
|
|
/*
|
|
* Initialize the "out" parameter.
|
|
*/
|
|
*ppstgOpen = 0;
|
|
|
|
hFile = CreateFile32W( pwcsName,
|
|
accessMode,
|
|
shareMode,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
|
|
0);
|
|
|
|
|
|
if (hFile==INVALID_HANDLE_VALUE32)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
/*
|
|
* Allocate and initialize the new IStorage32object.
|
|
*/
|
|
newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(Storage32Impl));
|
|
|
|
if (newStorage == 0)
|
|
return STG_E_INSUFFICIENTMEMORY;
|
|
|
|
hr = Storage32Impl_Construct(
|
|
newStorage,
|
|
hFile,
|
|
grfMode);
|
|
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
/*
|
|
* Get an "out" pointer for the caller.
|
|
*/
|
|
hr = Storage32BaseImpl_QueryInterface(
|
|
(IStorage32*)newStorage,
|
|
(REFIID)&IID_IStorage,
|
|
(void**)ppstgOpen);
|
|
|
|
return hr;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* WriteClassStg32 [OLE32.148]
|
|
*
|
|
* This method will store the specified CLSID in the specified storage object
|
|
*/
|
|
HRESULT WINAPI WriteClassStg32(IStorage32* pStg, REFCLSID rclsid)
|
|
{
|
|
HRESULT hRes;
|
|
|
|
assert(pStg != 0);
|
|
|
|
hRes = IStorage32_SetClass(pStg, rclsid);
|
|
|
|
return hRes;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
* This method validate a STGM parameter that can contain the values below
|
|
*
|
|
* STGM_DIRECT 0x00000000
|
|
* STGM_TRANSACTED 0x00010000
|
|
* STGM_SIMPLE 0x08000000
|
|
*
|
|
* STGM_READ 0x00000000
|
|
* STGM_WRITE 0x00000001
|
|
* STGM_READWRITE 0x00000002
|
|
*
|
|
* STGM_SHARE_DENY_NONE 0x00000040
|
|
* STGM_SHARE_DENY_READ 0x00000030
|
|
* STGM_SHARE_DENY_WRITE 0x00000020
|
|
* STGM_SHARE_EXCLUSIVE 0x00000010
|
|
*
|
|
* STGM_PRIORITY 0x00040000
|
|
* STGM_DELETEONRELEASE 0x04000000
|
|
*
|
|
* STGM_CREATE 0x00001000
|
|
* STGM_CONVERT 0x00020000
|
|
* STGM_FAILIFTHERE 0x00000000
|
|
*
|
|
* STGM_NOSCRATCH 0x00100000
|
|
* STGM_NOSNAPSHOT 0x00200000
|
|
*/
|
|
static HRESULT validateSTGM(DWORD stgm)
|
|
{
|
|
BOOL32 bSTGM_TRANSACTED = ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED);
|
|
BOOL32 bSTGM_SIMPLE = ((stgm & STGM_SIMPLE) == STGM_SIMPLE);
|
|
BOOL32 bSTGM_DIRECT = ! (bSTGM_TRANSACTED || bSTGM_SIMPLE);
|
|
|
|
BOOL32 bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
|
|
BOOL32 bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
|
|
BOOL32 bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
|
|
|
|
BOOL32 bSTGM_SHARE_DENY_NONE =
|
|
((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
|
|
|
|
BOOL32 bSTGM_SHARE_DENY_READ =
|
|
((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
|
|
|
|
BOOL32 bSTGM_SHARE_DENY_WRITE =
|
|
((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
|
|
|
|
BOOL32 bSTGM_SHARE_EXCLUSIVE =
|
|
((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
|
|
|
|
BOOL32 bSTGM_CREATE = ((stgm & STGM_CREATE) == STGM_CREATE);
|
|
BOOL32 bSTGM_CONVERT = ((stgm & STGM_CONVERT) == STGM_CONVERT);
|
|
|
|
BOOL32 bSTGM_NOSCRATCH = ((stgm & STGM_NOSCRATCH) == STGM_NOSCRATCH);
|
|
BOOL32 bSTGM_NOSNAPSHOT = ((stgm & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT);
|
|
|
|
/*
|
|
* STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
|
|
*/
|
|
if ( ! bSTGM_DIRECT )
|
|
if( bSTGM_TRANSACTED && bSTGM_SIMPLE )
|
|
return E_FAIL;
|
|
|
|
/*
|
|
* STGM_WRITE | STGM_READWRITE | STGM_READ
|
|
*/
|
|
if ( ! bSTGM_READ )
|
|
if( bSTGM_WRITE && bSTGM_READWRITE )
|
|
return E_FAIL;
|
|
|
|
/*
|
|
* STGM_SHARE_DENY_NONE | others
|
|
* (I assume here that DENY_READ implies DENY_WRITE)
|
|
*/
|
|
if ( bSTGM_SHARE_DENY_NONE )
|
|
if ( bSTGM_SHARE_DENY_READ ||
|
|
bSTGM_SHARE_DENY_WRITE ||
|
|
bSTGM_SHARE_EXCLUSIVE)
|
|
return E_FAIL;
|
|
|
|
/*
|
|
* STGM_CREATE | STGM_CONVERT
|
|
* if both are false, STGM_FAILIFTHERE is set to TRUE
|
|
*/
|
|
if ( bSTGM_CREATE && bSTGM_CONVERT )
|
|
return E_FAIL;
|
|
|
|
/*
|
|
* STGM_NOSCRATCH requires STGM_TRANSACTED
|
|
*/
|
|
if ( bSTGM_NOSCRATCH && ! bSTGM_TRANSACTED )
|
|
return E_FAIL;
|
|
|
|
/*
|
|
* STGM_NOSNAPSHOT requires STGM_TRANSACTED and
|
|
* not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
|
|
*/
|
|
if (bSTGM_NOSNAPSHOT)
|
|
{
|
|
if ( ! ( bSTGM_TRANSACTED &&
|
|
!(bSTGM_SHARE_EXCLUSIVE || bSTGM_SHARE_DENY_WRITE)) )
|
|
return E_FAIL;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* GetShareModeFromSTGM
|
|
*
|
|
* This method will return a share mode flag from a STGM value.
|
|
* The STGM value is assumed valid.
|
|
*/
|
|
static DWORD GetShareModeFromSTGM(DWORD stgm)
|
|
{
|
|
DWORD dwShareMode = 0;
|
|
BOOL32 bSTGM_SHARE_DENY_NONE =
|
|
((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
|
|
|
|
BOOL32 bSTGM_SHARE_DENY_READ =
|
|
((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
|
|
|
|
BOOL32 bSTGM_SHARE_DENY_WRITE =
|
|
((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
|
|
|
|
BOOL32 bSTGM_SHARE_EXCLUSIVE =
|
|
((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
|
|
|
|
if ((bSTGM_SHARE_EXCLUSIVE) || (bSTGM_SHARE_DENY_READ))
|
|
dwShareMode = 0;
|
|
|
|
if (bSTGM_SHARE_DENY_NONE)
|
|
dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
|
|
|
if (bSTGM_SHARE_DENY_WRITE)
|
|
dwShareMode = FILE_SHARE_READ;
|
|
|
|
return dwShareMode;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* GetAccessModeFromSTGM
|
|
*
|
|
* This method will return an access mode flag from a STGM value.
|
|
* The STGM value is assumed valid.
|
|
*/
|
|
static DWORD GetAccessModeFromSTGM(DWORD stgm)
|
|
{
|
|
DWORD dwDesiredAccess = 0;
|
|
BOOL32 bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
|
|
BOOL32 bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
|
|
BOOL32 bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
|
|
|
|
if (bSTGM_READ)
|
|
dwDesiredAccess = GENERIC_READ;
|
|
|
|
if (bSTGM_WRITE)
|
|
dwDesiredAccess |= GENERIC_WRITE;
|
|
|
|
if (bSTGM_READWRITE)
|
|
dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
|
|
|
|
return dwDesiredAccess;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* GetCreationModeFromSTGM
|
|
*
|
|
* This method will return a creation mode flag from a STGM value.
|
|
* The STGM value is assumed valid.
|
|
*/
|
|
static DWORD GetCreationModeFromSTGM(DWORD stgm)
|
|
{
|
|
DWORD dwCreationDistribution;
|
|
BOOL32 bSTGM_CREATE = ((stgm & STGM_CREATE) == STGM_CREATE);
|
|
BOOL32 bSTGM_CONVERT = ((stgm & STGM_CONVERT) == STGM_CONVERT);
|
|
BOOL32 bSTGM_FAILIFTHERE = ! (bSTGM_CREATE || bSTGM_CONVERT);
|
|
|
|
if (bSTGM_CREATE)
|
|
dwCreationDistribution = CREATE_NEW;
|
|
else if (bSTGM_FAILIFTHERE)
|
|
dwCreationDistribution = CREATE_NEW;
|
|
else if (bSTGM_CONVERT)
|
|
{
|
|
FIXME(ole, "STGM_CONVERT not implemented!\n");
|
|
dwCreationDistribution = CREATE_NEW;
|
|
}
|
|
|
|
return dwCreationDistribution;
|
|
}
|
|
|