Sweden-Number/dlls/ole32/storage32.c

7932 lines
212 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
* Copyright 2005 Mike McCormack
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
* NOTES
* The compound file implementation of IStorage used for create
* and manage substorages and streams within a storage object
* residing in a compound file object.
*
* MSDN
* http://msdn.microsoft.com/library/default.asp?url=/library/en-us/stg/stg/istorage_compound_file_implementation.asp
*/
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define COBJMACROS
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
#include "windef.h"
#include "winbase.h"
#include "winnls.h"
#include "winuser.h"
#include "wine/unicode.h"
#include "wine/debug.h"
#include "storage32.h"
#include "ole2.h" /* For Write/ReadClassStm */
#include "winreg.h"
#include "wine/wingdi16.h"
WINE_DEFAULT_DEBUG_CHANNEL(storage);
/* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
#define OLESTREAM_ID 0x501
#define OLESTREAM_MAX_STR_LEN 255
/*
* These are signatures to detect the type of Document file.
*/
static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
static const char rootPropertyName[] = "Root Entry";
/****************************************************************************
* Storage32InternalImpl definitions.
*
* Definition of the implementation structure for the IStorage32 interface.
* This one implements the IStorage32 interface for storage that are
* inside another storage.
*/
struct StorageInternalImpl
{
struct StorageBaseImpl base;
/*
* There is no specific data for this class.
*/
};
typedef struct StorageInternalImpl StorageInternalImpl;
/* Method definitions for the Storage32InternalImpl class. */
static StorageInternalImpl* StorageInternalImpl_Construct(StorageImpl* ancestorStorage,
DWORD openFlags, ULONG rootTropertyIndex);
static void StorageImpl_Destroy(StorageBaseImpl* iface);
static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
static void StorageImpl_SaveFileHeader(StorageImpl* This);
static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
static ULONG BlockChainStream_GetCount(BlockChainStream* This);
static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
ULONG blockIndex, ULONG offset, DWORD value);
static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This,
ULONG blockIndex, ULONG offset, DWORD* value);
/* OLESTREAM memory structure to use for Get and Put Routines */
/* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
typedef struct
{
DWORD dwOleID;
DWORD dwTypeID;
DWORD dwOleTypeNameLength;
CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
CHAR *pstrOleObjFileName;
DWORD dwOleObjFileNameLength;
DWORD dwMetaFileWidth;
DWORD dwMetaFileHeight;
CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
DWORD dwDataLength;
BYTE *pData;
}OLECONVERT_OLESTREAM_DATA;
/* CompObj Stream structure */
/* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
typedef struct
{
BYTE byUnknown1[12];
CLSID clsid;
DWORD dwCLSIDNameLength;
CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
DWORD dwOleTypeNameLength;
CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
DWORD dwProgIDNameLength;
CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
BYTE byUnknown2[16];
}OLECONVERT_ISTORAGE_COMPOBJ;
/* Ole Presention Stream structure */
/* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
typedef struct
{
BYTE byUnknown1[28];
DWORD dwExtentX;
DWORD dwExtentY;
DWORD dwSize;
BYTE *pData;
}OLECONVERT_ISTORAGE_OLEPRES;
/***********************************************************************
* Forward declaration of internal functions used by the method DestroyElement
*/
static HRESULT deleteStorageProperty(
StorageImpl *parentStorage,
ULONG foundPropertyIndexToDelete,
StgProperty propertyToDelete);
static HRESULT deleteStreamProperty(
StorageImpl *parentStorage,
ULONG foundPropertyIndexToDelete,
StgProperty propertyToDelete);
static HRESULT findPlaceholder(
StorageImpl *storage,
ULONG propertyIndexToStore,
ULONG storagePropertyIndex,
INT typeOfRelation);
static HRESULT adjustPropertyChain(
StorageImpl *This,
StgProperty propertyToDelete,
StgProperty parentProperty,
ULONG parentPropertyId,
INT typeOfRelation);
/***********************************************************************
* Declaration of the functions used to manipulate StgProperty
*/
static ULONG getFreeProperty(
StorageImpl *storage);
static void updatePropertyChain(
StorageImpl *storage,
ULONG newPropertyIndex,
StgProperty newProperty);
static LONG propertyNameCmp(
const OLECHAR *newProperty,
const OLECHAR *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);
extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
/****************************************************************************
* IEnumSTATSTGImpl definitions.
*
* Definition of the implementation structure for the IEnumSTATSTGImpl interface.
* This class allows iterating through the content of a storage and to find
* specific items inside it.
*/
struct IEnumSTATSTGImpl
{
const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
* since we want to cast this in an IEnumSTATSTG pointer */
LONG ref; /* Reference count */
StorageImpl* parentStorage; /* Reference to the parent storage */
ULONG firstPropertyNode; /* Index of the root of the storage to enumerate */
/*
* The current implementation of the IEnumSTATSTGImpl class uses a stack
* to walk the property sets to get the content of a storage. This stack
* is implemented by the following 3 data members
*/
ULONG stackSize;
ULONG stackMaxSize;
ULONG* stackToVisit;
#define ENUMSTATSGT_SIZE_INCREMENT 10
};
static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageImpl* This, ULONG firstPropertyNode);
static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl* This, ULONG nodeToPush);
static ULONG IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl* This, BOOL remove);
static ULONG IEnumSTATSTGImpl_FindProperty(IEnumSTATSTGImpl* This, const OLECHAR* lpszPropName,
StgProperty* buffer);
static INT IEnumSTATSTGImpl_FindParentProperty(IEnumSTATSTGImpl *This, ULONG childProperty,
StgProperty *currentProperty, ULONG *propertyId);
/************************************************************************
** Block Functions
*/
static ULONG BLOCK_GetBigBlockOffset(ULONG index)
{
if (index == 0xffffffff)
index = 0;
else
index ++;
return index * BIG_BLOCK_SIZE;
}
/************************************************************************
** Storage32BaseImpl implementatiion
*/
static HRESULT StorageImpl_ReadAt(StorageImpl* This,
ULARGE_INTEGER offset,
void* buffer,
ULONG size,
ULONG* bytesRead)
{
return BIGBLOCKFILE_ReadAt(This->bigBlockFile,offset,buffer,size,bytesRead);
}
static HRESULT StorageImpl_WriteAt(StorageImpl* This,
ULARGE_INTEGER offset,
void* buffer,
const ULONG size,
ULONG* bytesWritten)
{
return BIGBLOCKFILE_WriteAt(This->bigBlockFile,offset,buffer,size,bytesWritten);
}
/************************************************************************
* 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.
*/
static HRESULT WINAPI StorageBaseImpl_QueryInterface(
IStorage* iface,
REFIID riid,
void** ppvObject)
{
StorageBaseImpl *This = (StorageBaseImpl *)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 (IsEqualGUID(&IID_IUnknown, riid) ||
IsEqualGUID(&IID_IStorage, riid))
{
*ppvObject = (IStorage*)This;
}
else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
{
*ppvObject = (IStorage*)&This->pssVtbl;
}
/*
* 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
*/
IStorage_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.
*/
static ULONG WINAPI StorageBaseImpl_AddRef(
IStorage* iface)
{
StorageBaseImpl *This = (StorageBaseImpl *)iface;
ULONG ref = InterlockedIncrement(&This->ref);
TRACE("(%p) AddRef to %d\n", This, ref);
return 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.
*/
static ULONG WINAPI StorageBaseImpl_Release(
IStorage* iface)
{
StorageBaseImpl *This = (StorageBaseImpl *)iface;
/*
* Decrease the reference count on this object.
*/
ULONG ref = InterlockedDecrement(&This->ref);
TRACE("(%p) ReleaseRef to %d\n", This, ref);
/*
* If the reference count goes down to 0, perform suicide.
*/
if (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 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.
*/
static HRESULT WINAPI StorageBaseImpl_OpenStream(
IStorage* iface,
const OLECHAR* pwcsName, /* [string][in] */
void* reserved1, /* [unique][in] */
DWORD grfMode, /* [in] */
DWORD reserved2, /* [in] */
IStream** ppstm) /* [out] */
{
StorageBaseImpl *This = (StorageBaseImpl *)iface;
IEnumSTATSTGImpl* propertyEnumeration;
StgStreamImpl* newStream;
StgProperty currentProperty;
ULONG foundPropertyIndex;
HRESULT res = STG_E_UNKNOWN;
TRACE("(%p, %s, %p, %x, %d, %p)\n",
iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
/*
* Perform a sanity check on the parameters.
*/
if ( (pwcsName==NULL) || (ppstm==0) )
{
res = E_INVALIDARG;
goto end;
}
/*
* Initialize the out parameter
*/
*ppstm = NULL;
/*
* Validate the STGM flags
*/
if ( FAILED( validateSTGM(grfMode) ) ||
STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
{
res = STG_E_INVALIDFLAG;
goto end;
}
/*
* As documented.
*/
if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
{
res = STG_E_INVALIDFUNCTION;
goto end;
}
/*
* Check that we're compatible with the parent's storage mode, but
* only if we are not in transacted mode
*/
if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
{
res = STG_E_ACCESSDENIED;
goto end;
}
}
/*
* 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,
&currentProperty);
/*
* 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, grfMode, foundPropertyIndex);
if (newStream!=0)
{
newStream->grfMode = grfMode;
*ppstm = (IStream*)newStream;
/*
* Since we are returning a pointer to the interface, we have to
* nail down the reference.
*/
IStream_AddRef(*ppstm);
res = S_OK;
goto end;
}
res = E_OUTOFMEMORY;
goto end;
}
res = STG_E_FILENOTFOUND;
end:
if (res == S_OK)
TRACE("<-- IStream %p\n", *ppstm);
TRACE("<-- %08x\n", res);
return res;
}
/************************************************************************
* Storage32BaseImpl_OpenStorage (IStorage)
*
* This method will open a new storage object from the current storage.
*
* See Windows documentation for more details on IStorage methods.
*/
static HRESULT WINAPI StorageBaseImpl_OpenStorage(
IStorage* iface,
const OLECHAR* pwcsName, /* [string][unique][in] */
IStorage* pstgPriority, /* [unique][in] */
DWORD grfMode, /* [in] */
SNB snbExclude, /* [unique][in] */
DWORD reserved, /* [in] */
IStorage** ppstg) /* [out] */
{
StorageBaseImpl *This = (StorageBaseImpl *)iface;
StorageInternalImpl* newStorage;
IEnumSTATSTGImpl* propertyEnumeration;
StgProperty currentProperty;
ULONG foundPropertyIndex;
HRESULT res = STG_E_UNKNOWN;
TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
iface, debugstr_w(pwcsName), pstgPriority,
grfMode, snbExclude, reserved, ppstg);
/*
* Perform a sanity check on the parameters.
*/
if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
{
res = E_INVALIDARG;
goto end;
}
/* as documented */
if (snbExclude != NULL)
{
res = STG_E_INVALIDPARAMETER;
goto end;
}
/*
* Validate the STGM flags
*/
if ( FAILED( validateSTGM(grfMode) ))
{
res = STG_E_INVALIDFLAG;
goto end;
}
/*
* As documented.
*/
if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
(grfMode & STGM_DELETEONRELEASE) ||
(grfMode & STGM_PRIORITY) )
{
res = STG_E_INVALIDFUNCTION;
goto end;
}
/*
* Check that we're compatible with the parent's storage mode,
* but only if we are not transacted
*/
if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
{
res = STG_E_ACCESSDENIED;
goto end;
}
}
/*
* Initialize the out parameter
*/
*ppstg = NULL;
/*
* 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,
&currentProperty);
/*
* 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 = StorageInternalImpl_Construct(
This->ancestorStorage,
grfMode,
foundPropertyIndex);
if (newStorage != 0)
{
*ppstg = (IStorage*)newStorage;
/*
* Since we are returning a pointer to the interface,
* we have to nail down the reference.
*/
StorageBaseImpl_AddRef(*ppstg);
res = S_OK;
goto end;
}
res = STG_E_INSUFFICIENTMEMORY;
goto end;
}
res = STG_E_FILENOTFOUND;
end:
TRACE("<-- %08x\n", res);
return res;
}
/************************************************************************
* 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.
*/
static HRESULT WINAPI StorageBaseImpl_EnumElements(
IStorage* iface,
DWORD reserved1, /* [in] */
void* reserved2, /* [size_is][unique][in] */
DWORD reserved3, /* [in] */
IEnumSTATSTG** ppenum) /* [out] */
{
StorageBaseImpl *This = (StorageBaseImpl *)iface;
IEnumSTATSTGImpl* newEnum;
TRACE("(%p, %d, %p, %d, %p)\n",
iface, reserved1, reserved2, reserved3, ppenum);
/*
* 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.
*/
IEnumSTATSTG_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.
*/
static HRESULT WINAPI StorageBaseImpl_Stat(
IStorage* iface,
STATSTG* pstatstg, /* [out] */
DWORD grfStatFlag) /* [in] */
{
StorageBaseImpl *This = (StorageBaseImpl *)iface;
StgProperty curProperty;
BOOL readSuccessful;
HRESULT res = STG_E_UNKNOWN;
TRACE("(%p, %p, %x)\n",
iface, pstatstg, grfStatFlag);
/*
* Perform a sanity check on the parameters.
*/
if ( (This==0) || (pstatstg==0))
{
res = E_INVALIDARG;
goto end;
}
/*
* Read the information from the property.
*/
readSuccessful = StorageImpl_ReadProperty(
This->ancestorStorage,
This->rootPropertySetIndex,
&curProperty);
if (readSuccessful)
{
StorageUtl_CopyPropertyToSTATSTG(
pstatstg,
&curProperty,
grfStatFlag);
pstatstg->grfMode = This->openFlags;
res = S_OK;
goto end;
}
res = E_FAIL;
end:
if (res == S_OK)
{
TRACE("<-- STATSTG: pwcsName: %s, type: %d, cbSize.Low/High: %d/%d, grfMode: %08x, grfLocksSupported: %d, grfStateBits: %08x\n", debugstr_w(pstatstg->pwcsName), pstatstg->type, pstatstg->cbSize.u.LowPart, pstatstg->cbSize.u.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits);
}
TRACE("<-- %08x\n", res);
return res;
}
/************************************************************************
* 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.
*/
static HRESULT WINAPI StorageBaseImpl_RenameElement(
IStorage* iface,
const OLECHAR* pwcsOldName, /* [in] */
const OLECHAR* pwcsNewName) /* [in] */
{
StorageBaseImpl *This = (StorageBaseImpl *)iface;
IEnumSTATSTGImpl* propertyEnumeration;
StgProperty currentProperty;
ULONG foundPropertyIndex;
TRACE("(%p, %s, %s)\n",
iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
/*
* 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,
&currentProperty);
if (foundPropertyIndex != PROPERTY_NULL)
{
/*
* There is already a property with the new name
*/
IEnumSTATSTGImpl_Destroy(propertyEnumeration);
return STG_E_FILEALREADYEXISTS;
}
IEnumSTATSTG_Reset((IEnumSTATSTG*)propertyEnumeration);
/*
* Search the enumeration for the old property name
*/
foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
pwcsOldName,
&currentProperty);
/*
* 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 =
( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
return STG_E_INVALIDNAME;
strcpyW(renamedProperty.name, pwcsNewName);
renamedProperty.propertyType = currentProperty.propertyType;
renamedProperty.startingBlock = currentProperty.startingBlock;
renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
renamedProperty.size.u.HighPart = currentProperty.size.u.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
*/
StorageImpl_WriteProperty(
This->ancestorStorage,
renamedPropertyIndex,
&renamedProperty);
/*
* Find a spot in the property chain for our newly created property.
*/
updatePropertyChain(
(StorageImpl*)This,
renamedPropertyIndex,
renamedProperty);
/*
* At this point the renamed property has been inserted in the tree,
* now, before Destroying the old property we must zero its 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 tweak the StgProperty if it is a stream or a
* non empty storage.
*/
StorageImpl_ReadProperty(This->ancestorStorage,
foundPropertyIndex,
&currentProperty);
currentProperty.dirProperty = PROPERTY_NULL;
currentProperty.propertyType = PROPTYPE_STORAGE;
StorageImpl_WriteProperty(
This->ancestorStorage,
foundPropertyIndex,
&currentProperty);
/*
* Invoke Destroy to get rid of the ole property and automatically redo
* the linking of its previous and next members...
*/
IStorage_DestroyElement((IStorage*)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.
*/
static HRESULT WINAPI StorageBaseImpl_CreateStream(
IStorage* iface,
const OLECHAR* pwcsName, /* [string][in] */
DWORD grfMode, /* [in] */
DWORD reserved1, /* [in] */
DWORD reserved2, /* [in] */
IStream** ppstm) /* [out] */
{
StorageBaseImpl *This = (StorageBaseImpl *)iface;
IEnumSTATSTGImpl* propertyEnumeration;
StgStreamImpl* newStream;
StgProperty currentProperty, newStreamProperty;
ULONG foundPropertyIndex, newPropertyIndex;
TRACE("(%p, %s, %x, %d, %d, %p)\n",
iface, debugstr_w(pwcsName), grfMode,
reserved1, reserved2, ppstm);
/*
* Validate parameters
*/
if (ppstm == 0)
return STG_E_INVALIDPOINTER;
if (pwcsName == 0)
return STG_E_INVALIDNAME;
if (reserved1 || reserved2)
return STG_E_INVALIDPARAMETER;
/*
* Validate the STGM flags
*/
if ( FAILED( validateSTGM(grfMode) ))
return STG_E_INVALIDFLAG;
if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
return STG_E_INVALIDFLAG;
/*
* As documented.
*/
if ((grfMode & STGM_DELETEONRELEASE) ||
(grfMode & STGM_TRANSACTED))
return STG_E_INVALIDFUNCTION;
/*
* Check that we're compatible with the parent's storage mode
* if not in transacted mode
*/
if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
return STG_E_ACCESSDENIED;
}
/*
* 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,
&currentProperty);
IEnumSTATSTGImpl_Destroy(propertyEnumeration);
if (foundPropertyIndex != PROPERTY_NULL)
{
/*
* An element with this name already exists
*/
if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
{
IStorage_DestroyElement(iface, pwcsName);
}
else
return STG_E_FILEALREADYEXISTS;
}
else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
{
WARN("read-only storage\n");
return STG_E_ACCESSDENIED;
}
/*
* memset the empty property
*/
memset(&newStreamProperty, 0, sizeof(StgProperty));
newStreamProperty.sizeOfNameString =
( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
return STG_E_INVALIDNAME;
strcpyW(newStreamProperty.name, pwcsName);
newStreamProperty.propertyType = PROPTYPE_STREAM;
newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
newStreamProperty.size.u.LowPart = 0;
newStreamProperty.size.u.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
*/
StorageImpl_WriteProperty(
This->ancestorStorage,
newPropertyIndex,
&newStreamProperty);
/*
* Find a spot in the property chain for our newly created property.
*/
updatePropertyChain(
(StorageImpl*)This,
newPropertyIndex,
newStreamProperty);
/*
* Open the stream to return it.
*/
newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
if (newStream != 0)
{
*ppstm = (IStream*)newStream;
/*
* Since we are returning a pointer to the interface, we have to nail down
* the reference.
*/
IStream_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.
*/
static HRESULT WINAPI StorageBaseImpl_SetClass(
IStorage* iface,
REFCLSID clsid) /* [in] */
{
StorageBaseImpl *This = (StorageBaseImpl *)iface;
HRESULT hRes = E_FAIL;
StgProperty curProperty;
BOOL success;
TRACE("(%p, %p)\n", iface, clsid);
success = StorageImpl_ReadProperty(This->ancestorStorage,
This->rootPropertySetIndex,
&curProperty);
if (success)
{
curProperty.propertyUniqueID = *clsid;
success = StorageImpl_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.
*/
static HRESULT WINAPI StorageImpl_CreateStorage(
IStorage* iface,
const OLECHAR *pwcsName, /* [string][in] */
DWORD grfMode, /* [in] */
DWORD reserved1, /* [in] */
DWORD reserved2, /* [in] */
IStorage **ppstg) /* [out] */
{
StorageImpl* const This=(StorageImpl*)iface;
IEnumSTATSTGImpl *propertyEnumeration;
StgProperty currentProperty;
StgProperty newProperty;
ULONG foundPropertyIndex;
ULONG newPropertyIndex;
HRESULT hr;
TRACE("(%p, %s, %x, %d, %d, %p)\n",
iface, debugstr_w(pwcsName), grfMode,
reserved1, reserved2, ppstg);
/*
* Validate parameters
*/
if (ppstg == 0)
return STG_E_INVALIDPOINTER;
if (pwcsName == 0)
return STG_E_INVALIDNAME;
/*
* Initialize the out parameter
*/
*ppstg = NULL;
/*
* Validate the STGM flags
*/
if ( FAILED( validateSTGM(grfMode) ) ||
(grfMode & STGM_DELETEONRELEASE) )
{
WARN("bad grfMode: 0x%x\n", grfMode);
return STG_E_INVALIDFLAG;
}
/*
* Check that we're compatible with the parent's storage mode
*/
if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->base.openFlags ) )
{
WARN("access denied\n");
return STG_E_ACCESSDENIED;
}
/*
* Create a property enumeration and search the properties
*/
propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
This->base.rootPropertySetIndex);
foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
pwcsName,
&currentProperty);
IEnumSTATSTGImpl_Destroy(propertyEnumeration);
if (foundPropertyIndex != PROPERTY_NULL)
{
/*
* An element with this name already exists
*/
if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
IStorage_DestroyElement(iface, pwcsName);
else
{
WARN("file already exists\n");
return STG_E_FILEALREADYEXISTS;
}
}
else if (STGM_ACCESS_MODE(This->base.openFlags) == STGM_READ)
{
WARN("read-only storage\n");
return STG_E_ACCESSDENIED;
}
/*
* memset the empty property
*/
memset(&newProperty, 0, sizeof(StgProperty));
newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
{
FIXME("name too long\n");
return STG_E_INVALIDNAME;
}
strcpyW(newProperty.name, pwcsName);
newProperty.propertyType = PROPTYPE_STORAGE;
newProperty.startingBlock = BLOCK_END_OF_CHAIN;
newProperty.size.u.LowPart = 0;
newProperty.size.u.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->base.ancestorStorage);
/*
* Save the new property into the new property spot
*/
StorageImpl_WriteProperty(
This->base.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 = IStorage_OpenStorage(
iface,
(const OLECHAR*)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(
StorageImpl *storage)
{
ULONG currentPropertyIndex = 0;
ULONG newPropertyIndex = PROPERTY_NULL;
BOOL readSuccessful = TRUE;
StgProperty currentProperty;
do
{
/*
* Start by reading the root property
*/
readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
currentPropertyIndex,
&currentProperty);
if (readSuccessful)
{
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 (! readSuccessful)
{
StgProperty emptyProperty;
ULARGE_INTEGER newSize;
ULONG propertyIndex;
ULONG lastProperty = 0;
ULONG blockCount = 0;
/*
* obtain the new count of property blocks
*/
blockCount = BlockChainStream_GetCount(
storage->base.ancestorStorage->rootBlockChain)+1;
/*
* initialize the size used by the property stream
*/
newSize.u.HighPart = 0;
newSize.u.LowPart = storage->bigBlockSize * blockCount;
/*
* add a property block to the property chain
*/
BlockChainStream_SetSize(storage->base.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++)
{
StorageImpl_WriteProperty(
storage->base.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(
const OLECHAR *newProperty,
const OLECHAR *currentProperty)
{
LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
if (diff == 0)
{
/*
* We compare the string themselves only when they are of the same length
*/
diff = lstrcmpiW( newProperty, currentProperty);
}
return diff;
}
/****************************************************************************
*
* Internal Method
*
* Properly link this new element in the property chain.
*/
static void updatePropertyChain(
StorageImpl *storage,
ULONG newPropertyIndex,
StgProperty newProperty)
{
StgProperty currentProperty;
/*
* Read the root property
*/
StorageImpl_ReadProperty(storage->base.ancestorStorage,
storage->base.rootPropertySetIndex,
&currentProperty);
if (currentProperty.dirProperty != PROPERTY_NULL)
{
/*
* The root storage contains some element, therefore, start the research
* for the appropriate location.
*/
BOOL found = 0;
ULONG current, next, previous, currentPropertyId;
/*
* Keep the StgProperty sequence number of the storage first property
*/
currentPropertyId = currentProperty.dirProperty;
/*
* Read
*/
StorageImpl_ReadProperty(storage->base.ancestorStorage,
currentProperty.dirProperty,
&currentProperty);
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)
{
StorageImpl_ReadProperty(storage->base.ancestorStorage,
previous,
&currentProperty);
current = previous;
}
else
{
currentProperty.previousProperty = newPropertyIndex;
StorageImpl_WriteProperty(storage->base.ancestorStorage,
current,
&currentProperty);
found = 1;
}
}
else if (diff > 0)
{
if (next != PROPERTY_NULL)
{
StorageImpl_ReadProperty(storage->base.ancestorStorage,
next,
&currentProperty);
current = next;
}
else
{
currentProperty.nextProperty = newPropertyIndex;
StorageImpl_WriteProperty(storage->base.ancestorStorage,
current,
&currentProperty);
found = 1;
}
}
else
{
/*
* Trying to insert an item with the same name in the
* subtree structure.
*/
assert(FALSE);
}
previous = currentProperty.previousProperty;
next = currentProperty.nextProperty;
}
}
else
{
/*
* The root storage is empty, link the new property to its dir property
*/
currentProperty.dirProperty = newPropertyIndex;
StorageImpl_WriteProperty(storage->base.ancestorStorage,
storage->base.rootPropertySetIndex,
&currentProperty);
}
}
/*************************************************************************
* CopyTo (IStorage)
*/
static HRESULT WINAPI StorageImpl_CopyTo(
IStorage* iface,
DWORD ciidExclude, /* [in] */
const IID* rgiidExclude, /* [size_is][unique][in] */
SNB snbExclude, /* [unique][in] */
IStorage* pstgDest) /* [unique][in] */
{
IEnumSTATSTG *elements = 0;
STATSTG curElement, strStat;
HRESULT hr;
IStorage *pstgTmp, *pstgChild;
IStream *pstrTmp, *pstrChild;
if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
FIXME("Exclude option not implemented\n");
TRACE("(%p, %d, %p, %p, %p)\n",
iface, ciidExclude, rgiidExclude,
snbExclude, pstgDest);
/*
* Perform a sanity check
*/
if ( pstgDest == 0 )
return STG_E_INVALIDPOINTER;
/*
* Enumerate the elements
*/
hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
if ( hr != S_OK )
return hr;
/*
* set the class ID
*/
IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
IStorage_SetClass( pstgDest, &curElement.clsid );
do
{
/*
* Obtain the next element
*/
hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
if ( hr == S_FALSE )
{
hr = S_OK; /* done, every element has been copied */
break;
}
if (curElement.type == STGTY_STORAGE)
{
/*
* open child source storage
*/
hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
STGM_READ|STGM_SHARE_EXCLUSIVE,
NULL, 0, &pstgChild );
if (hr != S_OK)
break;
/*
* Check if destination storage is not a child of the source
* storage, which will cause an infinite loop
*/
if (pstgChild == pstgDest)
{
IEnumSTATSTG_Release(elements);
return STG_E_ACCESSDENIED;
}
/*
* create a new storage in destination storage
*/
hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
0, 0,
&pstgTmp );
/*
* if it already exist, don't create a new one use this one
*/
if (hr == STG_E_FILEALREADYEXISTS)
{
hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
STGM_WRITE|STGM_SHARE_EXCLUSIVE,
NULL, 0, &pstgTmp );
}
if (hr != S_OK)
break;
/*
* do the copy recursively
*/
hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
snbExclude, pstgTmp );
IStorage_Release( pstgTmp );
IStorage_Release( pstgChild );
}
else if (curElement.type == STGTY_STREAM)
{
/*
* create a new stream in destination storage. If the stream already
* exist, it will be deleted and a new one will be created.
*/
hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
0, 0, &pstrTmp );
if (hr != S_OK)
break;
/*
* open child stream storage
*/
hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
STGM_READ|STGM_SHARE_EXCLUSIVE,
0, &pstrChild );
if (hr != S_OK)
break;
/*
* Get the size of the source stream
*/
IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
/*
* Set the size of the destination stream.
*/
IStream_SetSize(pstrTmp, strStat.cbSize);
/*
* do the copy
*/
hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
NULL, NULL );
IStream_Release( pstrTmp );
IStream_Release( pstrChild );
}
else
{
WARN("unknown element type: %d\n", curElement.type);
}
} while (hr == S_OK);
/*
* Clean-up
*/
IEnumSTATSTG_Release(elements);
return hr;
}
/*************************************************************************
* MoveElementTo (IStorage)
*/
static HRESULT WINAPI StorageImpl_MoveElementTo(
IStorage* iface,
const OLECHAR *pwcsName, /* [string][in] */
IStorage *pstgDest, /* [unique][in] */
const OLECHAR *pwcsNewName,/* [string][in] */
DWORD grfFlags) /* [in] */
{
FIXME("(%p %s %p %s %u): stub\n", iface,
debugstr_w(pwcsName), pstgDest,
debugstr_w(pwcsNewName), grfFlags);
return E_NOTIMPL;
}
/*************************************************************************
* Commit (IStorage)
*
* Ensures that any changes made to a storage object open in transacted mode
* are reflected in the parent storage
*
* NOTES
* Wine doesn't implement transacted mode, which seems to be a basic
* optimization, so we can ignore this stub for now.
*/
static HRESULT WINAPI StorageImpl_Commit(
IStorage* iface,
DWORD grfCommitFlags)/* [in] */
{
FIXME("(%p %d): stub\n", iface, grfCommitFlags);
return S_OK;
}
/*************************************************************************
* Revert (IStorage)
*
* Discard all changes that have been made since the last commit operation
*/
static HRESULT WINAPI StorageImpl_Revert(
IStorage* iface)
{
FIXME("(%p): stub\n", iface);
return E_NOTIMPL;
}
/*************************************************************************
* DestroyElement (IStorage)
*
* Strategy: This implementation is built this way for simplicity not for speed.
* I always delete the topmost 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 resides in the usage of another
* enumeration strategy that would give all the leaves of a storage
* first. (postfix order)
*/
static HRESULT WINAPI StorageImpl_DestroyElement(
IStorage* iface,
const OLECHAR *pwcsName)/* [string][in] */
{
StorageImpl* const This=(StorageImpl*)iface;
IEnumSTATSTGImpl* propertyEnumeration;
HRESULT hr = S_OK;
BOOL res;
StgProperty propertyToDelete;
StgProperty parentProperty;
ULONG foundPropertyIndexToDelete;
ULONG typeOfRelation;
ULONG parentPropertyId = 0;
TRACE("(%p, %s)\n",
iface, debugstr_w(pwcsName));
/*
* 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->base.ancestorStorage,
This->base.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 its sibling...
*/
/*
* First, read This's StgProperty..
*/
res = StorageImpl_ReadProperty(
This->base.ancestorStorage,
This->base.rootPropertySetIndex,
&parentProperty);
assert(res);
/*
* 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->base.rootPropertySetIndex;
}
else
{
/*
* Create a property enumeration to search the parent properties, and
* delete it once done.
*/
IEnumSTATSTGImpl* propertyEnumeration2;
propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
This->base.ancestorStorage,
This->base.rootPropertySetIndex);
typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
propertyEnumeration2,
foundPropertyIndexToDelete,
&parentProperty,
&parentPropertyId);
IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
}
if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
{
hr = deleteStorageProperty(
This,
foundPropertyIndexToDelete,
propertyToDelete);
}
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;
}
/************************************************************************
* StorageImpl_Stat (IStorage)
*
* This method will retrieve information about this storage object.
*
* See Windows documentation for more details on IStorage methods.
*/
static HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
STATSTG* pstatstg, /* [out] */
DWORD grfStatFlag) /* [in] */
{
StorageImpl* const This = (StorageImpl*)iface;
HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
{
CoTaskMemFree(pstatstg->pwcsName);
pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
strcpyW(pstatstg->pwcsName, This->pwcsName);
}
return result;
}
/******************************************************************************
* Internal stream list handlers
*/
void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
{
TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
list_add_tail(&stg->strmHead,&strm->StrmListEntry);
}
void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
{
TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
list_remove(&(strm->StrmListEntry));
}
static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
{
struct list *cur, *cur2;
StgStreamImpl *strm=NULL;
LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
strm->parentStorage = NULL;
list_remove(cur);
}
}
/*********************************************************************
*
* Internal Method
*
* Perform the deletion of a complete storage node
*
*/
static HRESULT deleteStorageProperty(
StorageImpl *parentStorage,
ULONG indexOfPropertyToDelete,
StgProperty propertyToDelete)
{
IEnumSTATSTG *elements = 0;
IStorage *childStorage = 0;
STATSTG currentElement;
HRESULT hr;
HRESULT destroyHr = S_OK;
/*
* Open the storage and enumerate it
*/
hr = StorageBaseImpl_OpenStorage(
(IStorage*)parentStorage,
propertyToDelete.name,
0,
STGM_WRITE | STGM_SHARE_EXCLUSIVE,
0,
0,
&childStorage);
if (hr != S_OK)
{
return hr;
}
/*
* Enumerate the elements
*/
IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
do
{
/*
* Obtain the next element
*/
hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
if (hr==S_OK)
{
destroyHr = StorageImpl_DestroyElement(
(IStorage*)childStorage,
(OLECHAR*)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));
/*
* Invalidate the property by zeroing its name member.
*/
propertyToDelete.sizeOfNameString = 0;
StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
indexOfPropertyToDelete,
&propertyToDelete);
IStorage_Release(childStorage);
IEnumSTATSTG_Release(elements);
return destroyHr;
}
/*********************************************************************
*
* Internal Method
*
* Perform the deletion of a stream node
*
*/
static HRESULT deleteStreamProperty(
StorageImpl *parentStorage,
ULONG indexOfPropertyToDelete,
StgProperty propertyToDelete)
{
IStream *pis;
HRESULT hr;
ULARGE_INTEGER size;
size.u.HighPart = 0;
size.u.LowPart = 0;
hr = StorageBaseImpl_OpenStream(
(IStorage*)parentStorage,
(OLECHAR*)propertyToDelete.name,
NULL,
STGM_WRITE | STGM_SHARE_EXCLUSIVE,
0,
&pis);
if (hr!=S_OK)
{
return(hr);
}
/*
* Zap the stream
*/
hr = IStream_SetSize(pis, size);
if(hr != S_OK)
{
return hr;
}
/*
* Release the stream object.
*/
IStream_Release(pis);
/*
* Invalidate the property by zeroing its 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...
*/
StorageImpl_WriteProperty(
parentStorage->base.ancestorStorage,
indexOfPropertyToDelete,
&propertyToDelete);
return S_OK;
}
/*********************************************************************
*
* Internal Method
*
* Finds a placeholder for the StgProperty within the Storage
*
*/
static HRESULT findPlaceholder(
StorageImpl *storage,
ULONG propertyIndexToStore,
ULONG storePropertyIndex,
INT typeOfRelation)
{
StgProperty storeProperty;
HRESULT hr = S_OK;
BOOL res = TRUE;
/*
* Read the storage property
*/
res = StorageImpl_ReadProperty(
storage->base.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 = StorageImpl_WriteProperty(
storage->base.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(
StorageImpl *This,
StgProperty propertyToDelete,
StgProperty parentProperty,
ULONG parentPropertyId,
INT typeOfRelation)
{
ULONG newLinkProperty = PROPERTY_NULL;
BOOL needToFindAPlaceholder = FALSE;
ULONG storeNode = PROPERTY_NULL;
ULONG toStoreNode = PROPERTY_NULL;
INT relationType = 0;
HRESULT hr = S_OK;
BOOL 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 = StorageImpl_WriteProperty(
This->base.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)
*/
static HRESULT WINAPI StorageImpl_SetElementTimes(
IStorage* iface,
const OLECHAR *pwcsName,/* [string][in] */
const FILETIME *pctime, /* [in] */
const FILETIME *patime, /* [in] */
const FILETIME *pmtime) /* [in] */
{
FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
return S_OK;
}
/******************************************************************************
* SetStateBits (IStorage)
*/
static HRESULT WINAPI StorageImpl_SetStateBits(
IStorage* iface,
DWORD grfStateBits,/* [in] */
DWORD grfMask) /* [in] */
{
FIXME("not implemented!\n");
return E_NOTIMPL;
}
/*
* Virtual function table for the IStorage32Impl class.
*/
static const IStorageVtbl Storage32Impl_Vtbl =
{
StorageBaseImpl_QueryInterface,
StorageBaseImpl_AddRef,
StorageBaseImpl_Release,
StorageBaseImpl_CreateStream,
StorageBaseImpl_OpenStream,
StorageImpl_CreateStorage,
StorageBaseImpl_OpenStorage,
StorageImpl_CopyTo,
StorageImpl_MoveElementTo,
StorageImpl_Commit,
StorageImpl_Revert,
StorageBaseImpl_EnumElements,
StorageImpl_DestroyElement,
StorageBaseImpl_RenameElement,
StorageImpl_SetElementTimes,
StorageBaseImpl_SetClass,
StorageImpl_SetStateBits,
StorageImpl_Stat
};
static HRESULT StorageImpl_Construct(
StorageImpl* This,
HANDLE hFile,
LPCOLESTR pwcsName,
ILockBytes* pLkbyt,
DWORD openFlags,
BOOL fileBased,
BOOL fileCreate)
{
HRESULT hr = S_OK;
StgProperty currentProperty;
BOOL readSuccessful;
ULONG currentPropertyIndex;
if ( FAILED( validateSTGM(openFlags) ))
return STG_E_INVALIDFLAG;
memset(This, 0, sizeof(StorageImpl));
/*
* Initialize stream list
*/
list_init(&This->base.strmHead);
/*
* Initialize the virtual function table.
*/
This->base.lpVtbl = &Storage32Impl_Vtbl;
This->base.pssVtbl = &IPropertySetStorage_Vtbl;
This->base.v_destructor = &StorageImpl_Destroy;
This->base.openFlags = (openFlags & ~STGM_CREATE);
/*
* This is the top-level storage so initialize the ancestor pointer
* to this.
*/
This->base.ancestorStorage = This;
/*
* Initialize the physical support of the storage.
*/
This->hFile = hFile;
/*
* Store copy of file path.
*/
if(pwcsName) {
This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
(lstrlenW(pwcsName)+1)*sizeof(WCHAR));
if (!This->pwcsName)
return STG_E_INSUFFICIENTMEMORY;
strcpyW(This->pwcsName, pwcsName);
}
/*
* Initialize the big block cache.
*/
This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
pLkbyt,
openFlags,
This->bigBlockSize,
fileBased);
if (This->bigBlockFile == 0)
return E_FAIL;
if (fileCreate)
{
ULARGE_INTEGER size;
BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
/*
* 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;
StorageImpl_SaveFileHeader(This);
/*
* Add one block for the big block depot and one block for the properties
*/
size.u.HighPart = 0;
size.u.LowPart = This->bigBlockSize * 3;
BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
/*
* Initialize the big block depot
*/
memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
}
else
{
/*
* Load the header for the file.
*/
hr = StorageImpl_LoadFileHeader(This);
if (FAILED(hr))
{
BIGBLOCKFILE_Destructor(This->bigBlockFile);
return hr;
}
}
/*
* There is no block depot cached yet.
*/
This->indexBlockDepotCached = 0xFFFFFFFF;
/*
* Start searching for free blocks with block 0.
*/
This->prevFreeBlock = 0;
/*
* Create the block chain abstractions.
*/
if(!(This->rootBlockChain =
BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
return STG_E_READFAULT;
if(!(This->smallBlockDepotChain =
BlockChainStream_Construct(This, &This->smallBlockDepotStart,
PROPERTY_NULL)))
return STG_E_READFAULT;
/*
* Write the root property (memory only)
*/
if (fileCreate)
{
StgProperty rootProp;
/*
* Initialize the property chain
*/
memset(&rootProp, 0, sizeof(rootProp));
MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
sizeof(rootProp.name)/sizeof(WCHAR) );
rootProp.sizeOfNameString = (strlenW(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.u.HighPart = 0;
rootProp.size.u.LowPart = 0;
StorageImpl_WriteProperty(This, 0, &rootProp);
}
/*
* Find the ID of the root in the property sets.
*/
currentPropertyIndex = 0;
do
{
readSuccessful = StorageImpl_ReadProperty(
This,
currentPropertyIndex,
&currentProperty);
if (readSuccessful)
{
if ( (currentProperty.sizeOfNameString != 0 ) &&
(currentProperty.propertyType == PROPTYPE_ROOT) )
{
This->base.rootPropertySetIndex = currentPropertyIndex;
}
}
currentPropertyIndex++;
} while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
if (!readSuccessful)
{
/* TODO CLEANUP */
return STG_E_READFAULT;
}
/*
* Create the block chain abstraction for the small block root chain.
*/
if(!(This->smallBlockRootChain =
BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
return STG_E_READFAULT;
return hr;
}
static void StorageImpl_Destroy(StorageBaseImpl* iface)
{
StorageImpl *This = (StorageImpl*) iface;
TRACE("(%p)\n", This);
StorageBaseImpl_DeleteAll(&This->base);
HeapFree(GetProcessHeap(), 0, This->pwcsName);
BlockChainStream_Destroy(This->smallBlockRootChain);
BlockChainStream_Destroy(This->rootBlockChain);
BlockChainStream_Destroy(This->smallBlockDepotChain);
BIGBLOCKFILE_Destructor(This->bigBlockFile);
HeapFree(GetProcessHeap(), 0, This);
}
/******************************************************************************
* Storage32Impl_GetNextFreeBigBlock
*
* Returns the index of the next free big block.
* If the big block depot is filled, this method will enlarge it.
*
*/
static ULONG StorageImpl_GetNextFreeBigBlock(
StorageImpl* This)
{
ULONG depotBlockIndexPos;
BYTE depotBuffer[BIG_BLOCK_SIZE];
BOOL success;
ULONG depotBlockOffset;
ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
ULONG nextBlockIndex = BLOCK_SPECIAL;
int depotIndex = 0;
ULONG freeBlock = BLOCK_UNUSED;
depotIndex = This->prevFreeBlock / blocksPerDepot;
depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
/*
* Scan the entire big block depot until we find a block marked free
*/
while (nextBlockIndex != BLOCK_UNUSED)
{
if (depotIndex < COUNT_BBDEPOTINHEADER)
{
depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
/*
* Grow the primary depot.
*/
if (depotBlockIndexPos == BLOCK_UNUSED)
{
depotBlockIndexPos = depotIndex*blocksPerDepot;
/*
* Add a block depot.
*/
Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
This->bigBlockDepotCount++;
This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
/*
* Flag it as a block depot.
*/
StorageImpl_SetNextBlockInChain(This,
depotBlockIndexPos,
BLOCK_SPECIAL);
/* Save new header information.
*/
StorageImpl_SaveFileHeader(This);
}
}
else
{
depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
if (depotBlockIndexPos == BLOCK_UNUSED)
{
/*
* Grow the extended depot.
*/
ULONG extIndex = BLOCK_UNUSED;
ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
if (extBlockOffset == 0)
{
/* We need an extended block.
*/
extIndex = Storage32Impl_AddExtBlockDepot(This);
This->extBigBlockDepotCount++;
depotBlockIndexPos = extIndex + 1;
}
else
depotBlockIndexPos = depotIndex * blocksPerDepot;
/*
* Add a block depot and mark it in the extended block.
*/
Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
This->bigBlockDepotCount++;
Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
/* Flag the block depot.
*/
StorageImpl_SetNextBlockInChain(This,
depotBlockIndexPos,
BLOCK_SPECIAL);
/* If necessary, flag the extended depot block.
*/
if (extIndex != BLOCK_UNUSED)
StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
/* Save header information.
*/
StorageImpl_SaveFileHeader(This);
}
}
success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
if (success)
{
while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
( nextBlockIndex != BLOCK_UNUSED))
{
StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
if (nextBlockIndex == BLOCK_UNUSED)
{
freeBlock = (depotIndex * blocksPerDepot) +
(depotBlockOffset/sizeof(ULONG));
}
depotBlockOffset += sizeof(ULONG);
}
}
depotIndex++;
depotBlockOffset = 0;
}
/*
* make sure that the block physically exists before using it
*/
BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
This->prevFreeBlock = freeBlock;
return freeBlock;
}
/******************************************************************************
* Storage32Impl_AddBlockDepot
*
* This will create a depot block, essentially it is a block initialized
* to BLOCK_UNUSEDs.
*/
static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
{
BYTE blockBuffer[BIG_BLOCK_SIZE];
/*
* Initialize blocks as free
*/
memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
}
/******************************************************************************
* Storage32Impl_GetExtDepotBlock
*
* Returns the index of the block that corresponds to the specified depot
* index. This method is only for depot indexes equal or greater than
* COUNT_BBDEPOTINHEADER.
*/
static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
{
ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
ULONG blockIndex = BLOCK_UNUSED;
ULONG extBlockIndex = This->extBigBlockDepotStart;
assert(depotIndex >= COUNT_BBDEPOTINHEADER);
if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
return BLOCK_UNUSED;
while (extBlockCount > 0)
{
extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
extBlockCount--;
}
if (extBlockIndex != BLOCK_UNUSED)
StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
extBlockOffset * sizeof(ULONG), &blockIndex);
return blockIndex;
}
/******************************************************************************
* Storage32Impl_SetExtDepotBlock
*
* Associates the specified block index to the specified depot index.
* This method is only for depot indexes equal or greater than
* COUNT_BBDEPOTINHEADER.
*/
static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
{
ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
ULONG extBlockIndex = This->extBigBlockDepotStart;
assert(depotIndex >= COUNT_BBDEPOTINHEADER);
while (extBlockCount > 0)
{
extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
extBlockCount--;
}
if (extBlockIndex != BLOCK_UNUSED)
{
StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
extBlockOffset * sizeof(ULONG),
blockIndex);
}
}
/******************************************************************************
* Storage32Impl_AddExtBlockDepot
*
* Creates an extended depot block.
*/
static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
{
ULONG numExtBlocks = This->extBigBlockDepotCount;
ULONG nextExtBlock = This->extBigBlockDepotStart;
BYTE depotBuffer[BIG_BLOCK_SIZE];
ULONG index = BLOCK_UNUSED;
ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
blocksPerDepotBlock;
if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
{
/*
* The first extended block.
*/
This->extBigBlockDepotStart = index;
}
else
{
unsigned int i;
/*
* Follow the chain to the last one.
*/
for (i = 0; i < (numExtBlocks - 1); i++)
{
nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
}
/*
* Add the new extended block to the chain.
*/
StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
index);
}
/*
* Initialize this block.
*/
memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
StorageImpl_WriteBigBlock(This, index, depotBuffer);
return index;
}
/******************************************************************************
* Storage32Impl_FreeBigBlock
*
* This method will flag the specified block as free in the big block depot.
*/
static void StorageImpl_FreeBigBlock(
StorageImpl* This,
ULONG blockIndex)
{
StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
if (blockIndex < This->prevFreeBlock)
This->prevFreeBlock = blockIndex;
}
/************************************************************************
* 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.
* nextBlockIndex - receives the return value.
*
* 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.
* BLOCK_EXTBBDEPOT - This block is part of the extended
* big block depot.
*
* See Windows documentation for more details on IStorage methods.
*/
static HRESULT StorageImpl_GetNextBlockInChain(
StorageImpl* This,
ULONG blockIndex,
ULONG* nextBlockIndex)
{
ULONG offsetInDepot = blockIndex * sizeof (ULONG);
ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
BYTE depotBuffer[BIG_BLOCK_SIZE];
BOOL success;
ULONG depotBlockIndexPos;
int index;
*nextBlockIndex = BLOCK_SPECIAL;
if(depotBlockCount >= This->bigBlockDepotCount)
{
WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
This->bigBlockDepotCount);
return STG_E_READFAULT;
}
/*
* Cache the currently accessed depot block.
*/
if (depotBlockCount != This->indexBlockDepotCached)
{
This->indexBlockDepotCached = depotBlockCount;
if (depotBlockCount < COUNT_BBDEPOTINHEADER)
{
depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
}
else
{
/*
* We have to look in the extended depot.
*/
depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
}
success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
if (!success)
return STG_E_READFAULT;
for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
{
StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
This->blockDepotCached[index] = *nextBlockIndex;
}
}
*nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
return S_OK;
}
/******************************************************************************
* Storage32Impl_GetNextExtendedBlock
*
* Given an extended block this method will return the next extended block.
*
* NOTES:
* The last ULONG of an extended block is the block index of the next
* extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
* depot.
*
* Return values:
* - The index of the next extended block
* - BLOCK_UNUSED: there is no next extended block.
* - Any other return values denotes failure.
*/
static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
{
ULONG nextBlockIndex = BLOCK_SPECIAL;
ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
&nextBlockIndex);
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);
*
*/
static void StorageImpl_SetNextBlockInChain(
StorageImpl* This,
ULONG blockIndex,
ULONG nextBlock)
{
ULONG offsetInDepot = blockIndex * sizeof (ULONG);
ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
ULONG depotBlockIndexPos;
assert(depotBlockCount < This->bigBlockDepotCount);
assert(blockIndex != nextBlock);
if (depotBlockCount < COUNT_BBDEPOTINHEADER)
{
depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
}
else
{
/*
* We have to look in the extended depot.
*/
depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
}
StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
nextBlock);
/*
* Update the cached block depot, if necessary.
*/
if (depotBlockCount == This->indexBlockDepotCached)
{
This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
}
}
/******************************************************************************
* Storage32Impl_LoadFileHeader
*
* This method will read in the file header, i.e. big block index -1.
*/
static HRESULT StorageImpl_LoadFileHeader(
StorageImpl* This)
{
HRESULT hr = STG_E_FILENOTFOUND;
BYTE headerBigBlock[BIG_BLOCK_SIZE];
BOOL success;
int index;
TRACE("\n");
/*
* Get a pointer to the big block of data containing the header.
*/
success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
/*
* Extract the information from the header.
*/
if (success)
{
/*
* Check for the "magic number" signature and return an error if it is not
* found.
*/
if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
{
return STG_E_OLDFORMAT;
}
if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
{
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.
*/
if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
{
WARN("Broken OLE storage file\n");
hr = STG_E_INVALIDHEADER;
}
else
hr = S_OK;
}
return hr;
}
/******************************************************************************
* Storage32Impl_SaveFileHeader
*
* This method will save to the file the header, i.e. big block -1.
*/
static void StorageImpl_SaveFileHeader(
StorageImpl* This)
{
BYTE headerBigBlock[BIG_BLOCK_SIZE];
int index;
BOOL success;
/*
* Get a pointer to the big block of data containing the header.
*/
success = StorageImpl_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);
}
/*
* Write the information to the header.
*/
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_SBDEPOTCOUNT,
This->smallBlockDepotChain ?
BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
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.
*/
StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
}
/******************************************************************************
* Storage32Impl_ReadProperty
*
* This method will read the specified property from the property chain.
*/
BOOL StorageImpl_ReadProperty(
StorageImpl* This,
ULONG index,
StgProperty* buffer)
{
BYTE currentProperty[PROPSET_BLOCK_SIZE];
ULARGE_INTEGER offsetInPropSet;
HRESULT readRes;
ULONG bytesRead;
offsetInPropSet.u.HighPart = 0;
offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
readRes = BlockChainStream_ReadAt(
This->rootBlockChain,
offsetInPropSet,
PROPSET_BLOCK_SIZE,
currentProperty,
&bytesRead);
if (SUCCEEDED(readRes))
{
/* replace the name of root entry (often "Root Entry") by the file name */
WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
memset(buffer->name, 0, sizeof(buffer->name));
memcpy(
buffer->name,
propName,
PROPERTY_NAME_BUFFER_LEN );
TRACE("storage name: %s\n", debugstr_w(buffer->name));
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.u.LowPart);
buffer->size.u.HighPart = 0;
}
return SUCCEEDED(readRes) ? TRUE : FALSE;
}
/*********************************************************************
* Write the specified property into the property chain
*/
BOOL StorageImpl_WriteProperty(
StorageImpl* This,
ULONG index,
StgProperty* buffer)
{
BYTE currentProperty[PROPSET_BLOCK_SIZE];
ULARGE_INTEGER offsetInPropSet;
HRESULT writeRes;
ULONG bytesWritten;
offsetInPropSet.u.HighPart = 0;
offsetInPropSet.u.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);
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.u.LowPart);
writeRes = BlockChainStream_WriteAt(This->rootBlockChain,
offsetInPropSet,
PROPSET_BLOCK_SIZE,
currentProperty,
&bytesWritten);
return SUCCEEDED(writeRes) ? TRUE : FALSE;
}
static BOOL StorageImpl_ReadBigBlock(
StorageImpl* This,
ULONG blockIndex,
void* buffer)
{
ULARGE_INTEGER ulOffset;
DWORD read;
ulOffset.u.HighPart = 0;
ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
return (read == This->bigBlockSize);
}
static BOOL StorageImpl_ReadDWordFromBigBlock(
StorageImpl* This,
ULONG blockIndex,
ULONG offset,
DWORD* value)
{
ULARGE_INTEGER ulOffset;
DWORD read;
DWORD tmp;
ulOffset.u.HighPart = 0;
ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
ulOffset.u.LowPart += offset;
StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
*value = le32toh(tmp);
return (read == sizeof(DWORD));
}
static BOOL StorageImpl_WriteBigBlock(
StorageImpl* This,
ULONG blockIndex,
void* buffer)
{
ULARGE_INTEGER ulOffset;
DWORD wrote;
ulOffset.u.HighPart = 0;
ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
return (wrote == This->bigBlockSize);
}
static BOOL StorageImpl_WriteDWordToBigBlock(
StorageImpl* This,
ULONG blockIndex,
ULONG offset,
DWORD value)
{
ULARGE_INTEGER ulOffset;
DWORD wrote;
ulOffset.u.HighPart = 0;
ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
ulOffset.u.LowPart += offset;
value = htole32(value);
StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
return (wrote == sizeof(DWORD));
}
/******************************************************************************
* 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(
StorageImpl* This,
SmallBlockChainStream** ppsbChain)
{
ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
ULARGE_INTEGER size, offset;
ULONG cbRead, cbWritten;
ULARGE_INTEGER cbTotalRead;
ULONG propertyIndex;
HRESULT resWrite = S_OK;
HRESULT resRead;
StgProperty chainProperty;
BYTE *buffer;
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);
if(!bbTempChain) return 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.u.LowPart = 0;
offset.u.HighPart = 0;
cbTotalRead.QuadPart = 0;
buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
do
{
resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
offset,
This->smallBlockSize,
buffer,
&cbRead);
if (FAILED(resRead))
break;
if (cbRead > 0)
{
cbTotalRead.QuadPart += cbRead;
resWrite = BlockChainStream_WriteAt(bbTempChain,
offset,
cbRead,
buffer,
&cbWritten);
if (FAILED(resWrite))
break;
offset.u.LowPart += This->smallBlockSize;
}
} while (cbTotalRead.QuadPart < size.QuadPart);
HeapFree(GetProcessHeap(),0,buffer);
if (FAILED(resRead) || FAILED(resWrite))
{
ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
BlockChainStream_Destroy(bbTempChain);
return NULL;
}
/*
* Destroy the small block chain.
*/
propertyIndex = (*ppsbChain)->ownerPropertyIndex;
size.u.HighPart = 0;
size.u.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.
*/
StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
chainProperty.startingBlock = bbHeadOfChain;
StorageImpl_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;
}
static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
{
StorageInternalImpl* This = (StorageInternalImpl*) iface;
StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
HeapFree(GetProcessHeap(), 0, This);
}
/******************************************************************************
**
** Storage32InternalImpl_Commit
**
** The non-root storages cannot be opened in transacted mode thus this function
** does nothing.
*/
static HRESULT WINAPI StorageInternalImpl_Commit(
IStorage* iface,
DWORD grfCommitFlags) /* [in] */
{
return S_OK;
}
/******************************************************************************
**
** Storage32InternalImpl_Revert
**
** The non-root storages cannot be opened in transacted mode thus this function
** does nothing.
*/
static HRESULT WINAPI StorageInternalImpl_Revert(
IStorage* iface)
{
return S_OK;
}
static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
{
IStorage_Release((IStorage*)This->parentStorage);
HeapFree(GetProcessHeap(), 0, This->stackToVisit);
HeapFree(GetProcessHeap(), 0, This);
}
static 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 (IsEqualGUID(&IID_IUnknown, riid) ||
IsEqualGUID(&IID_IEnumSTATSTG, riid))
{
*ppvObject = (IEnumSTATSTG*)This;
IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
return S_OK;
}
return E_NOINTERFACE;
}
static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
IEnumSTATSTG* iface)
{
IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
return InterlockedIncrement(&This->ref);
}
static ULONG WINAPI IEnumSTATSTGImpl_Release(
IEnumSTATSTG* iface)
{
IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
ULONG newRef;
newRef = InterlockedDecrement(&This->ref);
/*
* If the reference count goes down to 0, perform suicide.
*/
if (newRef==0)
{
IEnumSTATSTGImpl_Destroy(This);
}
return newRef;
}
static 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.
*/
StorageImpl_ReadProperty(This->parentStorage,
currentSearchNode,
&currentProperty);
/*
* Copy the information to the return buffer.
*/
StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
&currentProperty,
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;
}
static 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.
*/
StorageImpl_ReadProperty(This->parentStorage,
currentSearchNode,
&currentProperty);
/*
* 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;
}
static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
IEnumSTATSTG* iface)
{
IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
StgProperty rootProperty;
BOOL readSuccessful;
/*
* Re-initialize the search stack to an empty stack
*/
This->stackSize = 0;
/*
* Read the root property from the storage.
*/
readSuccessful = StorageImpl_ReadProperty(
This->parentStorage,
This->firstPropertyNode,
&rootProperty);
if (readSuccessful)
{
assert(rootProperty.sizeOfNameString!=0);
/*
* Push the search node in the search stack.
*/
IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
}
return S_OK;
}
static 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;
}
static INT 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.
*/
StorageImpl_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;
}
static ULONG IEnumSTATSTGImpl_FindProperty(
IEnumSTATSTGImpl* This,
const OLECHAR* 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.
*/
StorageImpl_ReadProperty(This->parentStorage,
currentSearchNode,
currentProperty);
if ( propertyNameCmp(
(const OLECHAR*)currentProperty->name,
(const OLECHAR*)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;
}
static void IEnumSTATSTGImpl_PushSearchNode(
IEnumSTATSTGImpl* This,
ULONG nodeToPush)
{
StgProperty rootProperty;
BOOL readSuccessful;
/*
* 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.
*/
readSuccessful = StorageImpl_ReadProperty(
This->parentStorage,
nodeToPush,
&rootProperty);
if (readSuccessful)
{
assert(rootProperty.sizeOfNameString!=0);
/*
* Push the previous search node in the search stack.
*/
IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
}
}
static ULONG IEnumSTATSTGImpl_PopSearchNode(
IEnumSTATSTGImpl* This,
BOOL remove)
{
ULONG topNode;
if (This->stackSize == 0)
return PROPERTY_NULL;
topNode = This->stackToVisit[This->stackSize-1];
if (remove)
This->stackSize--;
return topNode;
}
/*
* Virtual function table for the IEnumSTATSTGImpl class.
*/
static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
{
IEnumSTATSTGImpl_QueryInterface,
IEnumSTATSTGImpl_AddRef,
IEnumSTATSTGImpl_Release,
IEnumSTATSTGImpl_Next,
IEnumSTATSTGImpl_Skip,
IEnumSTATSTGImpl_Reset,
IEnumSTATSTGImpl_Clone
};
/******************************************************************************
** IEnumSTATSTGImpl implementation
*/
static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
StorageImpl* 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;
IStorage_AddRef((IStorage*)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;
}
/*
* Virtual function table for the Storage32InternalImpl class.
*/
static const IStorageVtbl Storage32InternalImpl_Vtbl =
{
StorageBaseImpl_QueryInterface,
StorageBaseImpl_AddRef,
StorageBaseImpl_Release,
StorageBaseImpl_CreateStream,
StorageBaseImpl_OpenStream,
StorageImpl_CreateStorage,
StorageBaseImpl_OpenStorage,
StorageImpl_CopyTo,
StorageImpl_MoveElementTo,
StorageInternalImpl_Commit,
StorageInternalImpl_Revert,
StorageBaseImpl_EnumElements,
StorageImpl_DestroyElement,
StorageBaseImpl_RenameElement,
StorageImpl_SetElementTimes,
StorageBaseImpl_SetClass,
StorageImpl_SetStateBits,
StorageBaseImpl_Stat
};
/******************************************************************************
** Storage32InternalImpl implementation
*/
static StorageInternalImpl* StorageInternalImpl_Construct(
StorageImpl* ancestorStorage,
DWORD openFlags,
ULONG rootPropertyIndex)
{
StorageInternalImpl* newStorage;
/*
* Allocate space for the new storage object
*/
newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
if (newStorage!=0)
{
memset(newStorage, 0, sizeof(StorageInternalImpl));
/*
* Initialize the stream list
*/
list_init(&newStorage->base.strmHead);
/*
* Initialize the virtual function table.
*/
newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
newStorage->base.v_destructor = &StorageInternalImpl_Destroy;
newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
/*
* Keep the ancestor storage pointer and nail a reference to it.
*/
newStorage->base.ancestorStorage = ancestorStorage;
StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
/*
* Keep the index of the root property set for this storage,
*/
newStorage->base.rootPropertySetIndex = rootPropertyIndex;
return newStorage;
}
return 0;
}
/******************************************************************************
** StorageUtl implementation
*/
void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
{
WORD tmp;
memcpy(&tmp, buffer+offset, sizeof(WORD));
*value = le16toh(tmp);
}
void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
{
value = htole16(value);
memcpy(buffer+offset, &value, sizeof(WORD));
}
void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
{
DWORD tmp;
memcpy(&tmp, buffer+offset, sizeof(DWORD));
*value = le32toh(tmp);
}
void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
{
value = htole32(value);
memcpy(buffer+offset, &value, sizeof(DWORD));
}
void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
ULARGE_INTEGER* value)
{
#ifdef WORDS_BIGENDIAN
ULARGE_INTEGER tmp;
memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
value->u.LowPart = htole32(tmp.u.HighPart);
value->u.HighPart = htole32(tmp.u.LowPart);
#else
memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
#endif
}
void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
const ULARGE_INTEGER *value)
{
#ifdef WORDS_BIGENDIAN
ULARGE_INTEGER tmp;
tmp.u.LowPart = htole32(value->u.HighPart);
tmp.u.HighPart = htole32(value->u.LowPart);
memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
#else
memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
#endif
}
void StorageUtl_ReadGUID(const BYTE* 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, buffer+offset+8, sizeof(value->Data4));
}
void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
{
StorageUtl_WriteDWord(buffer, offset, value->Data1);
StorageUtl_WriteWord(buffer, offset+4, value->Data2);
StorageUtl_WriteWord(buffer, offset+6, value->Data3);
memcpy(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) ||
(source->name == NULL) ||
(source->name[0] == 0) )
{
destination->pwcsName = 0;
}
else
{
destination->pwcsName =
CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
strcpyW((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(
StorageImpl* parentStorage,
ULONG* headOfStreamPlaceHolder,
ULONG propertyIndex)
{
BlockChainStream* newStream;
ULONG blockIndex;
newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
newStream->parentStorage = parentStorage;
newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
newStream->ownerPropertyIndex = propertyIndex;
newStream->lastBlockNoInSequence = 0xFFFFFFFF;
newStream->tailIndex = BLOCK_END_OF_CHAIN;
newStream->numBlocks = 0;
blockIndex = BlockChainStream_GetHeadOfChain(newStream);
while (blockIndex != BLOCK_END_OF_CHAIN)
{
newStream->numBlocks++;
newStream->tailIndex = blockIndex;
if(FAILED(StorageImpl_GetNextBlockInChain(
parentStorage,
blockIndex,
&blockIndex)))
{
HeapFree(GetProcessHeap(), 0, newStream);
return NULL;
}
}
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.
*
*/
static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
{
StgProperty chainProperty;
BOOL readSuccessful;
if (This->headOfStreamPlaceHolder != 0)
return *(This->headOfStreamPlaceHolder);
if (This->ownerPropertyIndex != PROPERTY_NULL)
{
readSuccessful = StorageImpl_ReadProperty(
This->parentStorage,
This->ownerPropertyIndex,
&chainProperty);
if (readSuccessful)
{
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!
*
*/
static ULONG BlockChainStream_GetCount(BlockChainStream* This)
{
ULONG blockIndex;
ULONG count = 0;
blockIndex = BlockChainStream_GetHeadOfChain(This);
while (blockIndex != BLOCK_END_OF_CHAIN)
{
count++;
if(FAILED(StorageImpl_GetNextBlockInChain(
This->parentStorage,
blockIndex,
&blockIndex)))
return 0;
}
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.
*/
HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
ULARGE_INTEGER offset,
ULONG size,
void* buffer,
ULONG* bytesRead)
{
ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
ULONG bytesToReadInBuffer;
ULONG blockIndex;
BYTE* bufferWalker;
TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
/*
* Find the first block in the stream that contains part of the buffer.
*/
if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
(This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
(blockNoInSequence < This->lastBlockNoInSequence) )
{
blockIndex = BlockChainStream_GetHeadOfChain(This);
This->lastBlockNoInSequence = blockNoInSequence;
}
else
{
ULONG temp = blockNoInSequence;
blockIndex = This->lastBlockNoInSequenceIndex;
blockNoInSequence -= This->lastBlockNoInSequence;
This->lastBlockNoInSequence = temp;
}
while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
{
if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
return STG_E_DOCFILECORRUPT;
blockNoInSequence--;
}
if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
This->lastBlockNoInSequenceIndex = blockIndex;
/*
* Start reading the buffer.
*/
*bytesRead = 0;
bufferWalker = buffer;
while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
{
ULARGE_INTEGER ulOffset;
DWORD bytesReadAt;
/*
* Calculate how many bytes we can copy from this big block.
*/
bytesToReadInBuffer =
min(This->parentStorage->bigBlockSize - offsetInBlock, size);
TRACE("block %i\n",blockIndex);
ulOffset.u.HighPart = 0;
ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
offsetInBlock;
StorageImpl_ReadAt(This->parentStorage,
ulOffset,
bufferWalker,
bytesToReadInBuffer,
&bytesReadAt);
/*
* Step to the next big block.
*/
if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
return STG_E_DOCFILECORRUPT;
bufferWalker += bytesReadAt;
size -= bytesReadAt;
*bytesRead += bytesReadAt;
offsetInBlock = 0; /* There is no offset on the next block */
if (bytesToReadInBuffer != bytesReadAt)
break;
}
return (size == 0) ? S_OK : STG_E_READFAULT;
}
/******************************************************************************
* 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.
*/
HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
ULARGE_INTEGER offset,
ULONG size,
const void* buffer,
ULONG* bytesWritten)
{
ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
ULONG bytesToWrite;
ULONG blockIndex;
const BYTE* bufferWalker;
/*
* Find the first block in the stream that contains part of the buffer.
*/
if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
(This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
(blockNoInSequence < This->lastBlockNoInSequence) )
{
blockIndex = BlockChainStream_GetHeadOfChain(This);
This->lastBlockNoInSequence = blockNoInSequence;
}
else
{
ULONG temp = blockNoInSequence;
blockIndex = This->lastBlockNoInSequenceIndex;
blockNoInSequence -= This->lastBlockNoInSequence;
This->lastBlockNoInSequence = temp;
}
while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
{
if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
&blockIndex)))
return STG_E_DOCFILECORRUPT;
blockNoInSequence--;
}
This->lastBlockNoInSequenceIndex = blockIndex;
/* BlockChainStream_SetSize should have already been called to ensure we have
* enough blocks in the chain to write into */
if (blockIndex == BLOCK_END_OF_CHAIN)
{
ERR("not enough blocks in chain to write data\n");
return STG_E_DOCFILECORRUPT;
}
/*
* 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 = (const BYTE*)buffer;
while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
{
ULARGE_INTEGER ulOffset;
DWORD bytesWrittenAt;
/*
* Calculate how many bytes we can copy from this big block.
*/
bytesToWrite =
min(This->parentStorage->bigBlockSize - offsetInBlock, size);
TRACE("block %i\n",blockIndex);
ulOffset.u.HighPart = 0;
ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
offsetInBlock;
StorageImpl_WriteAt(This->parentStorage,
ulOffset,
(BYTE*)bufferWalker,
bytesToWrite,
&bytesWrittenAt);
/*
* Step to the next big block.
*/
if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
&blockIndex)))
return STG_E_DOCFILECORRUPT;
bufferWalker += bytesWrittenAt;
size -= bytesWrittenAt;
*bytesWritten += bytesWrittenAt;
offsetInBlock = 0; /* There is no offset on the next block */
if (bytesWrittenAt != bytesToWrite)
break;
}
return (size == 0) ? S_OK : STG_E_WRITEFAULT;
}
/******************************************************************************
* BlockChainStream_Shrink
*
* Shrinks this chain in the big block depot.
*/
static BOOL BlockChainStream_Shrink(BlockChainStream* This,
ULARGE_INTEGER newSize)
{
ULONG blockIndex, extraBlock;
ULONG numBlocks;
ULONG count = 1;
/*
* Reset the last accessed block cache.
*/
This->lastBlockNoInSequence = 0xFFFFFFFF;
This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
/*
* Figure out how many blocks are needed to contain the new size
*/
numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
numBlocks++;
blockIndex = BlockChainStream_GetHeadOfChain(This);
/*
* Go to the new end of chain
*/
while (count < numBlocks)
{
if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
&blockIndex)))
return FALSE;
count++;
}
/* Get the next block before marking the new end */
if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
&extraBlock)))
return FALSE;
/* Mark the new end of chain */
StorageImpl_SetNextBlockInChain(
This->parentStorage,
blockIndex,
BLOCK_END_OF_CHAIN);
This->tailIndex = blockIndex;
This->numBlocks = numBlocks;
/*
* Mark the extra blocks as free
*/
while (extraBlock != BLOCK_END_OF_CHAIN)
{
if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
&blockIndex)))
return FALSE;
StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
extraBlock = blockIndex;
}
return TRUE;
}
/******************************************************************************
* BlockChainStream_Enlarge
*
* Grows this chain in the big block depot.
*/
static BOOL 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 = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
StorageImpl_SetNextBlockInChain(This->parentStorage,
blockIndex,
BLOCK_END_OF_CHAIN);
if (This->headOfStreamPlaceHolder != 0)
{
*(This->headOfStreamPlaceHolder) = blockIndex;
}
else
{
StgProperty chainProp;
assert(This->ownerPropertyIndex != PROPERTY_NULL);
StorageImpl_ReadProperty(
This->parentStorage,
This->ownerPropertyIndex,
&chainProp);
chainProp.startingBlock = blockIndex;
StorageImpl_WriteProperty(
This->parentStorage,
This->ownerPropertyIndex,
&chainProp);
}
This->tailIndex = blockIndex;
This->numBlocks = 1;
}
/*
* Figure out how many blocks are needed to contain this stream
*/
newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
newNumBlocks++;
/*
* Go to the current end of chain
*/
if (This->tailIndex == BLOCK_END_OF_CHAIN)
{
currentBlock = blockIndex;
while (blockIndex != BLOCK_END_OF_CHAIN)
{
This->numBlocks++;
currentBlock = blockIndex;
if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
&blockIndex)))
return FALSE;
}
This->tailIndex = currentBlock;
}
currentBlock = This->tailIndex;
oldNumBlocks = This->numBlocks;
/*
* Add new blocks to the chain
*/
if (oldNumBlocks < newNumBlocks)
{
while (oldNumBlocks < newNumBlocks)
{
blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
StorageImpl_SetNextBlockInChain(
This->parentStorage,
currentBlock,
blockIndex);
StorageImpl_SetNextBlockInChain(
This->parentStorage,
blockIndex,
BLOCK_END_OF_CHAIN);
currentBlock = blockIndex;
oldNumBlocks++;
}
This->tailIndex = blockIndex;
This->numBlocks = newNumBlocks;
}
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.
*/
BOOL BlockChainStream_SetSize(
BlockChainStream* This,
ULARGE_INTEGER newSize)
{
ULARGE_INTEGER size = BlockChainStream_GetSize(This);
if (newSize.u.LowPart == size.u.LowPart)
return TRUE;
if (newSize.u.LowPart < size.u.LowPart)
{
BlockChainStream_Shrink(This, newSize);
}
else
{
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.
*/
static 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
*/
StorageImpl_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.u.HighPart = 0;
result.u.LowPart =
BlockChainStream_GetCount(This) *
This->parentStorage->bigBlockSize;
return result;
}
}
/******************************************************************************
** SmallBlockChainStream implementation
*/
SmallBlockChainStream* SmallBlockChainStream_Construct(
StorageImpl* 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.
*/
static ULONG SmallBlockChainStream_GetHeadOfChain(
SmallBlockChainStream* This)
{
StgProperty chainProperty;
BOOL readSuccessful;
if (This->ownerPropertyIndex)
{
readSuccessful = StorageImpl_ReadProperty(
This->parentStorage,
This->ownerPropertyIndex,
&chainProperty);
if (readSuccessful)
{
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
*/
static HRESULT SmallBlockChainStream_GetNextBlockInChain(
SmallBlockChainStream* This,
ULONG blockIndex,
ULONG* nextBlockInChain)
{
ULARGE_INTEGER offsetOfBlockInDepot;
DWORD buffer;
ULONG bytesRead;
HRESULT res;
*nextBlockInChain = BLOCK_END_OF_CHAIN;
offsetOfBlockInDepot.u.HighPart = 0;
offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
/*
* Read those bytes in the buffer from the small block file.
*/
res = BlockChainStream_ReadAt(
This->parentStorage->smallBlockDepotChain,
offsetOfBlockInDepot,
sizeof(DWORD),
&buffer,
&bytesRead);
if (SUCCEEDED(res))
{
StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
return S_OK;
}
return res;
}
/******************************************************************************
* 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.
*/
static void SmallBlockChainStream_SetNextBlockInChain(
SmallBlockChainStream* This,
ULONG blockIndex,
ULONG nextBlock)
{
ULARGE_INTEGER offsetOfBlockInDepot;
DWORD buffer;
ULONG bytesWritten;
offsetOfBlockInDepot.u.HighPart = 0;
offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
StorageUtl_WriteDWord((BYTE *)&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.
*/
static 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.
*/
static ULONG SmallBlockChainStream_GetNextFreeBlock(
SmallBlockChainStream* This)
{
ULARGE_INTEGER offsetOfBlockInDepot;
DWORD buffer;
ULONG bytesRead;
ULONG blockIndex = 0;
ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
HRESULT res = S_OK;
ULONG smallBlocksPerBigBlock;
offsetOfBlockInDepot.u.HighPart = 0;
/*
* Scan the small block depot for a free block
*/
while (nextBlockIndex != BLOCK_UNUSED)
{
offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
res = BlockChainStream_ReadAt(
This->parentStorage->smallBlockDepotChain,
offsetOfBlockInDepot,
sizeof(DWORD),
&buffer,
&bytesRead);
/*
* If we run out of space for the small block depot, enlarge it
*/
if (SUCCEEDED(res))
{
StorageUtl_ReadDWord((BYTE *)&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[BIG_BLOCK_SIZE];
nextBlock = sbdIndex;
while (nextBlock != BLOCK_END_OF_CHAIN)
{
sbdIndex = nextBlock;
StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
}
newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
if (sbdIndex != BLOCK_END_OF_CHAIN)
StorageImpl_SetNextBlockInChain(
This->parentStorage,
sbdIndex,
newsbdIndex);
StorageImpl_SetNextBlockInChain(
This->parentStorage,
newsbdIndex,
BLOCK_END_OF_CHAIN);
/*
* Initialize all the small blocks to free
*/
memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, 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;
StorageImpl_SaveFileHeader(This->parentStorage);
/*
* And allocate the first big block that will contain small blocks
*/
sbStartIndex =
StorageImpl_GetNextFreeBigBlock(This->parentStorage);
StorageImpl_SetNextBlockInChain(
This->parentStorage,
sbStartIndex,
BLOCK_END_OF_CHAIN);
StorageImpl_ReadProperty(
This->parentStorage,
This->parentStorage->base.rootPropertySetIndex,
&rootProp);
rootProp.startingBlock = sbStartIndex;
rootProp.size.u.HighPart = 0;
rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
StorageImpl_WriteProperty(
This->parentStorage,
This->parentStorage->base.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;
StorageImpl_ReadProperty(
This->parentStorage,
This->parentStorage->base.rootPropertySetIndex,
&rootProp);
if (rootProp.size.u.LowPart <
(blocksRequired * This->parentStorage->bigBlockSize))
{
rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
BlockChainStream_SetSize(
This->parentStorage->smallBlockRootChain,
rootProp.size);
StorageImpl_WriteProperty(
This->parentStorage,
This->parentStorage->base.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.
*/
HRESULT SmallBlockChainStream_ReadAt(
SmallBlockChainStream* This,
ULARGE_INTEGER offset,
ULONG size,
void* buffer,
ULONG* bytesRead)
{
HRESULT rc = S_OK;
ULARGE_INTEGER offsetInBigBlockFile;
ULONG blockNoInSequence =
offset.u.LowPart / This->parentStorage->smallBlockSize;
ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
ULONG bytesToReadInBuffer;
ULONG blockIndex;
ULONG bytesReadFromBigBlockFile;
BYTE* bufferWalker;
/*
* This should never happen on a small block file.
*/
assert(offset.u.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))
{
rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
if(FAILED(rc))
return rc;
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.u.HighPart = 0;
offsetInBigBlockFile.u.LowPart =
blockIndex * This->parentStorage->smallBlockSize;
offsetInBigBlockFile.u.LowPart += offsetInBlock;
/*
* Read those bytes in the buffer from the small block file.
* The small block has already been identified so it shouldn't fail
* unless the file is corrupt.
*/
rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
offsetInBigBlockFile,
bytesToReadInBuffer,
bufferWalker,
&bytesReadFromBigBlockFile);
if (FAILED(rc))
return rc;
/*
* Step to the next big block.
*/
rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
if(FAILED(rc))
return STG_E_DOCFILECORRUPT;
bufferWalker += bytesReadFromBigBlockFile;
size -= bytesReadFromBigBlockFile;
*bytesRead += bytesReadFromBigBlockFile;
offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
}
return (size == 0) ? S_OK : STG_E_READFAULT;
}
/******************************************************************************
* 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.
*/
HRESULT SmallBlockChainStream_WriteAt(
SmallBlockChainStream* This,
ULARGE_INTEGER offset,
ULONG size,
const void* buffer,
ULONG* bytesWritten)
{
ULARGE_INTEGER offsetInBigBlockFile;
ULONG blockNoInSequence =
offset.u.LowPart / This->parentStorage->smallBlockSize;
ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
ULONG bytesToWriteInBuffer;
ULONG blockIndex;
ULONG bytesWrittenToBigBlockFile;
const BYTE* bufferWalker;
HRESULT res;
/*
* This should never happen on a small block file.
*/
assert(offset.u.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))
{
if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
return STG_E_DOCFILECORRUPT;
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 = (const 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.u.HighPart = 0;
offsetInBigBlockFile.u.LowPart =
blockIndex * This->parentStorage->smallBlockSize;
offsetInBigBlockFile.u.LowPart += offsetInBlock;
/*
* Write those bytes in the buffer to the small block file.
*/
res = BlockChainStream_WriteAt(
This->parentStorage->smallBlockRootChain,
offsetInBigBlockFile,
bytesToWriteInBuffer,
bufferWalker,
&bytesWrittenToBigBlockFile);
if (FAILED(res))
return res;
/*
* Step to the next big block.
*/
if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
&blockIndex)))
return FALSE;
bufferWalker += bytesWrittenToBigBlockFile;
size -= bytesWrittenToBigBlockFile;
*bytesWritten += bytesWrittenToBigBlockFile;
offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
}
return (size == 0) ? S_OK : STG_E_WRITEFAULT;
}
/******************************************************************************
* SmallBlockChainStream_Shrink
*
* Shrinks this chain in the small block depot.
*/
static BOOL SmallBlockChainStream_Shrink(
SmallBlockChainStream* This,
ULARGE_INTEGER newSize)
{
ULONG blockIndex, extraBlock;
ULONG numBlocks;
ULONG count = 0;
numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
numBlocks++;
blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
/*
* Go to the new end of chain
*/
while (count < numBlocks)
{
if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
&blockIndex)))
return FALSE;
count++;
}
/*
* If the count is 0, we have a special case, the head of the chain was
* just freed.
*/
if (count == 0)
{
StgProperty chainProp;
StorageImpl_ReadProperty(This->parentStorage,
This->ownerPropertyIndex,
&chainProp);
chainProp.startingBlock = BLOCK_END_OF_CHAIN;
StorageImpl_WriteProperty(This->parentStorage,
This->ownerPropertyIndex,
&chainProp);
/*
* We start freeing the chain at the head block.
*/
extraBlock = blockIndex;
}
else
{
/* Get the next block before marking the new end */
if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
&extraBlock)))
return FALSE;
/* 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)
{
if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
&blockIndex)))
return FALSE;
SmallBlockChainStream_FreeBlock(This, extraBlock);
extraBlock = blockIndex;
}
return TRUE;
}
/******************************************************************************
* SmallBlockChainStream_Enlarge
*
* Grows this chain in the small block depot.
*/
static BOOL 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;
StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
&chainProp);
chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
StorageImpl_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.u.LowPart / This->parentStorage->smallBlockSize;
if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
newNumBlocks++;
/*
* Go to the current end of chain
*/
while (blockIndex != BLOCK_END_OF_CHAIN)
{
oldNumBlocks++;
currentBlock = blockIndex;
if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
return FALSE;
}
/*
* 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_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.
*/
BOOL SmallBlockChainStream_SetSize(
SmallBlockChainStream* This,
ULARGE_INTEGER newSize)
{
ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
if (newSize.u.LowPart == size.u.LowPart)
return TRUE;
if (newSize.u.LowPart < size.u.LowPart)
{
SmallBlockChainStream_Shrink(This, newSize);
}
else
{
SmallBlockChainStream_Enlarge(This, newSize);
}
return TRUE;
}
/******************************************************************************
* SmallBlockChainStream_GetSize
*
* Returns the size of this chain.
*/
static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
{
StgProperty chainProperty;
StorageImpl_ReadProperty(
This->parentStorage,
This->ownerPropertyIndex,
&chainProperty);
return chainProperty.size;
}
/******************************************************************************
* StgCreateDocfile [OLE32.@]
* Creates a new compound file storage object
*
* PARAMS
* pwcsName [ I] Unicode string with filename (can be relative or NULL)
* grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
* reserved [ ?] unused?, usually 0
* ppstgOpen [IO] A pointer to IStorage pointer to the new onject
*
* RETURNS
* S_OK if the file was successfully created
* some STG_E_ value if error
* NOTES
* if pwcsName is NULL, create file with new unique name
* the function can returns
* STG_S_CONVERTED if the specified file was successfully converted to storage format
* (unrealized now)
*/
HRESULT WINAPI StgCreateDocfile(
LPCOLESTR pwcsName,
DWORD grfMode,
DWORD reserved,
IStorage **ppstgOpen)
{
StorageImpl* newStorage = 0;
HANDLE hFile = INVALID_HANDLE_VALUE;
HRESULT hr = STG_E_INVALIDFLAG;
DWORD shareMode;
DWORD accessMode;
DWORD creationMode;
DWORD fileAttributes;
WCHAR tempFileName[MAX_PATH];
TRACE("(%s, %x, %d, %p)\n",
debugstr_w(pwcsName), grfMode,
reserved, ppstgOpen);
/*
* Validate the parameters
*/
if (ppstgOpen == 0)
return STG_E_INVALIDPOINTER;
if (reserved != 0)
return STG_E_INVALIDPARAMETER;
/*
* Validate the STGM flags
*/
if ( FAILED( validateSTGM(grfMode) ))
goto end;
/* StgCreateDocFile seems to refuse readonly access, despite MSDN */
switch(STGM_ACCESS_MODE(grfMode))
{
case STGM_WRITE:
case STGM_READWRITE:
break;
default:
goto end;
}
/* if no share mode given then DENY_NONE is the default */
if (STGM_SHARE_MODE(grfMode) == 0)
grfMode |= STGM_SHARE_DENY_NONE;
/* must have at least one access mode */
if (STGM_ACCESS_MODE(grfMode) == 0)
goto end;
/* in direct mode, can only use SHARE_EXCLUSIVE */
if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
goto end;
/* but in transacted mode, any share mode is valid */
/*
* Generate a unique name.
*/
if (pwcsName == 0)
{
WCHAR tempPath[MAX_PATH];
static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
memset(tempPath, 0, sizeof(tempPath));
memset(tempFileName, 0, sizeof(tempFileName));
if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
tempPath[0] = '.';
if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
pwcsName = tempFileName;
else
{
hr = STG_E_INSUFFICIENTMEMORY;
goto end;
}
creationMode = TRUNCATE_EXISTING;
}
else
{
creationMode = GetCreationModeFromSTGM(grfMode);
}
/*
* Interpret the STGM value grfMode
*/
shareMode = GetShareModeFromSTGM(grfMode);
accessMode = GetAccessModeFromSTGM(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("Transacted mode not implemented.\n");
/*
* Initialize the "out" parameter.
*/
*ppstgOpen = 0;
hFile = CreateFileW(pwcsName,
accessMode,
shareMode,
NULL,
creationMode,
fileAttributes,
0);
if (hFile == INVALID_HANDLE_VALUE)
{
if(GetLastError() == ERROR_FILE_EXISTS)
hr = STG_E_FILEALREADYEXISTS;
else
hr = E_FAIL;
goto end;
}
/*
* Allocate and initialize the new IStorage32object.
*/
newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
if (newStorage == 0)
{
hr = STG_E_INSUFFICIENTMEMORY;
goto end;
}
hr = StorageImpl_Construct(
newStorage,
hFile,
pwcsName,
NULL,
grfMode,
TRUE,
TRUE);
if (FAILED(hr))
{
HeapFree(GetProcessHeap(), 0, newStorage);
goto end;
}
/*
* Get an "out" pointer for the caller.
*/
hr = StorageBaseImpl_QueryInterface(
(IStorage*)newStorage,
(REFIID)&IID_IStorage,
(void**)ppstgOpen);
end:
TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
return hr;
}
/******************************************************************************
* StgCreateStorageEx [OLE32.@]
*/
HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
{
TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
if (stgfmt != STGFMT_FILE && grfAttrs != 0)
{
ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
return STG_E_INVALIDPARAMETER;
}
if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
{
ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
return STG_E_INVALIDPARAMETER;
}
if (stgfmt == STGFMT_FILE)
{
ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
return STG_E_INVALIDPARAMETER;
}
if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
{
FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
}
ERR("Invalid stgfmt argument\n");
return STG_E_INVALIDPARAMETER;
}
/******************************************************************************
* StgCreatePropSetStg [OLE32.@]
*/
HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
IPropertySetStorage **ppPropSetStg)
{
HRESULT hr;
TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
if (reserved)
hr = STG_E_INVALIDPARAMETER;
else
hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
(void**)ppPropSetStg);
return hr;
}
/******************************************************************************
* StgOpenStorageEx [OLE32.@]
*/
HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
{
TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
{
ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
return STG_E_INVALIDPARAMETER;
}
switch (stgfmt)
{
case STGFMT_FILE:
ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
return STG_E_INVALIDPARAMETER;
case STGFMT_STORAGE:
break;
case STGFMT_DOCFILE:
if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
{
ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
return STG_E_INVALIDPARAMETER;
}
FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
break;
case STGFMT_ANY:
WARN("STGFMT_ANY assuming storage\n");
break;
default:
return STG_E_INVALIDPARAMETER;
}
return StgOpenStorage(pwcsName, NULL, grfMode, (SNB)NULL, 0, (IStorage **)ppObjectOpen);
}
/******************************************************************************
* StgOpenStorage [OLE32.@]
*/
HRESULT WINAPI StgOpenStorage(
const OLECHAR *pwcsName,
IStorage *pstgPriority,
DWORD grfMode,
SNB snbExclude,
DWORD reserved,
IStorage **ppstgOpen)
{
StorageImpl* newStorage = 0;
HRESULT hr = S_OK;
HANDLE hFile = 0;
DWORD shareMode;
DWORD accessMode;
WCHAR fullname[MAX_PATH];
TRACE("(%s, %p, %x, %p, %d, %p)\n",
debugstr_w(pwcsName), pstgPriority, grfMode,
snbExclude, reserved, ppstgOpen);
/*
* Perform sanity checks
*/
if (pwcsName == 0)
{
hr = STG_E_INVALIDNAME;
goto end;
}
if (ppstgOpen == 0)
{
hr = STG_E_INVALIDPOINTER;
goto end;
}
if (reserved)
{
hr = STG_E_INVALIDPARAMETER;
goto end;
}
if (grfMode & STGM_PRIORITY)
{
if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
return STG_E_INVALIDFLAG;
if (grfMode & STGM_DELETEONRELEASE)
return STG_E_INVALIDFUNCTION;
if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
return STG_E_INVALIDFLAG;
grfMode &= ~0xf0; /* remove the existing sharing mode */
grfMode |= STGM_SHARE_DENY_NONE;
/* STGM_PRIORITY stops other IStorage objects on the same file from
* committing until the STGM_PRIORITY IStorage is closed. it also
* stops non-transacted mode StgOpenStorage calls with write access from
* succeeding. obviously, both of these cannot be achieved through just
* file share flags */
FIXME("STGM_PRIORITY mode not implemented correctly\n");
}
/*
* Validate the sharing mode
*/
if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
switch(STGM_SHARE_MODE(grfMode))
{
case STGM_SHARE_EXCLUSIVE:
case STGM_SHARE_DENY_WRITE:
break;
default:
hr = STG_E_INVALIDFLAG;
goto end;
}
/*
* Validate the STGM flags
*/
if ( FAILED( validateSTGM(grfMode) ) ||
(grfMode&STGM_CREATE))
{
hr = STG_E_INVALIDFLAG;
goto end;
}
/* shared reading requires transacted mode */
if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
!(grfMode&STGM_TRANSACTED) )
{
hr = STG_E_INVALIDFLAG;
goto end;
}
/*
* Interpret the STGM value grfMode
*/
shareMode = GetShareModeFromSTGM(grfMode);
accessMode = GetAccessModeFromSTGM(grfMode);
/*
* Initialize the "out" parameter.
*/
*ppstgOpen = 0;
hFile = CreateFileW( pwcsName,
accessMode,
shareMode,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
0);
if (hFile==INVALID_HANDLE_VALUE)
{
DWORD last_error = GetLastError();
hr = E_FAIL;
switch (last_error)
{
case ERROR_FILE_NOT_FOUND:
hr = STG_E_FILENOTFOUND;
break;
case ERROR_PATH_NOT_FOUND:
hr = STG_E_PATHNOTFOUND;
break;
case ERROR_ACCESS_DENIED:
case ERROR_WRITE_PROTECT:
hr = STG_E_ACCESSDENIED;
break;
case ERROR_SHARING_VIOLATION:
hr = STG_E_SHAREVIOLATION;
break;
default:
hr = E_FAIL;
}
goto end;
}
/*
* Refuse to open the file if it's too small to be a structured storage file
* FIXME: verify the file when reading instead of here
*/
if (GetFileSize(hFile, NULL) < 0x100)
{
CloseHandle(hFile);
hr = STG_E_FILEALREADYEXISTS;
goto end;
}
/*
* Allocate and initialize the new IStorage32object.
*/
newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
if (newStorage == 0)
{
hr = STG_E_INSUFFICIENTMEMORY;
goto end;
}
/* Initialize the storage */
hr = StorageImpl_Construct(
newStorage,
hFile,
pwcsName,
NULL,
grfMode,
TRUE,
FALSE );
if (FAILED(hr))
{
HeapFree(GetProcessHeap(), 0, newStorage);
/*
* According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
*/
if(hr == STG_E_INVALIDHEADER)
hr = STG_E_FILEALREADYEXISTS;
goto end;
}
/* prepare the file name string given in lieu of the root property name */
GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
/*
* Get an "out" pointer for the caller.
*/
hr = StorageBaseImpl_QueryInterface(
(IStorage*)newStorage,
(REFIID)&IID_IStorage,
(void**)ppstgOpen);
end:
TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
return hr;
}
/******************************************************************************
* StgCreateDocfileOnILockBytes [OLE32.@]
*/
HRESULT WINAPI StgCreateDocfileOnILockBytes(
ILockBytes *plkbyt,
DWORD grfMode,
DWORD reserved,
IStorage** ppstgOpen)
{
StorageImpl* newStorage = 0;
HRESULT hr = S_OK;
/*
* Validate the parameters
*/
if ((ppstgOpen == 0) || (plkbyt == 0))
return STG_E_INVALIDPOINTER;
/*
* Allocate and initialize the new IStorage object.
*/
newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
if (newStorage == 0)
return STG_E_INSUFFICIENTMEMORY;
hr = StorageImpl_Construct(
newStorage,
0,
0,
plkbyt,
grfMode,
FALSE,
TRUE);
if (FAILED(hr))
{
HeapFree(GetProcessHeap(), 0, newStorage);
return hr;
}
/*
* Get an "out" pointer for the caller.
*/
hr = StorageBaseImpl_QueryInterface(
(IStorage*)newStorage,
(REFIID)&IID_IStorage,
(void**)ppstgOpen);
return hr;
}
/******************************************************************************
* StgOpenStorageOnILockBytes [OLE32.@]
*/
HRESULT WINAPI StgOpenStorageOnILockBytes(
ILockBytes *plkbyt,
IStorage *pstgPriority,
DWORD grfMode,
SNB snbExclude,
DWORD reserved,
IStorage **ppstgOpen)
{
StorageImpl* newStorage = 0;
HRESULT hr = S_OK;
/*
* Perform a sanity check
*/
if ((plkbyt == 0) || (ppstgOpen == 0))
return STG_E_INVALIDPOINTER;
/*
* Validate the STGM flags
*/
if ( FAILED( validateSTGM(grfMode) ))
return STG_E_INVALIDFLAG;
/*
* Initialize the "out" parameter.
*/
*ppstgOpen = 0;
/*
* Allocate and initialize the new IStorage object.
*/
newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
if (newStorage == 0)
return STG_E_INSUFFICIENTMEMORY;
hr = StorageImpl_Construct(
newStorage,
0,
0,
plkbyt,
grfMode,
FALSE,
FALSE);
if (FAILED(hr))
{
HeapFree(GetProcessHeap(), 0, newStorage);
return hr;
}
/*
* Get an "out" pointer for the caller.
*/
hr = StorageBaseImpl_QueryInterface(
(IStorage*)newStorage,
(REFIID)&IID_IStorage,
(void**)ppstgOpen);
return hr;
}
/******************************************************************************
* StgSetTimes [ole32.@]
* StgSetTimes [OLE32.@]
*
*
*/
HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
FILETIME const *patime, FILETIME const *pmtime)
{
IStorage *stg = NULL;
HRESULT r;
TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
0, 0, &stg);
if( SUCCEEDED(r) )
{
r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
IStorage_Release(stg);
}
return r;
}
/******************************************************************************
* StgIsStorageILockBytes [OLE32.@]
*
* Determines if the ILockBytes contains a storage object.
*/
HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
{
BYTE sig[8];
ULARGE_INTEGER offset;
offset.u.HighPart = 0;
offset.u.LowPart = 0;
ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
return S_OK;
return S_FALSE;
}
/******************************************************************************
* WriteClassStg [OLE32.@]
*
* This method will store the specified CLSID in the specified storage object
*/
HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
{
HRESULT hRes;
if(!pStg)
return E_INVALIDARG;
hRes = IStorage_SetClass(pStg, rclsid);
return hRes;
}
/***********************************************************************
* ReadClassStg (OLE32.@)
*
* This method reads the CLSID previously written to a storage object with
* the WriteClassStg.
*
* PARAMS
* pstg [I] IStorage pointer
* pclsid [O] Pointer to where the CLSID is written
*
* RETURNS
* Success: S_OK.
* Failure: HRESULT code.
*/
HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
STATSTG pstatstg;
HRESULT hRes;
TRACE("(%p, %p)\n", pstg, pclsid);
if(!pstg || !pclsid)
return E_INVALIDARG;
/*
* read a STATSTG structure (contains the clsid) from the storage
*/
hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
if(SUCCEEDED(hRes))
*pclsid=pstatstg.clsid;
return hRes;
}
/***********************************************************************
* OleLoadFromStream (OLE32.@)
*
* This function loads an object from stream
*/
HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
{
CLSID clsid;
HRESULT res;
LPPERSISTSTREAM xstm;
TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
res=ReadClassStm(pStm,&clsid);
if (!SUCCEEDED(res))
return res;
res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
if (!SUCCEEDED(res))
return res;
res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
if (!SUCCEEDED(res)) {
IUnknown_Release((IUnknown*)*ppvObj);
return res;
}
res=IPersistStream_Load(xstm,pStm);
IPersistStream_Release(xstm);
/* FIXME: all refcounts ok at this point? I think they should be:
* pStm : unchanged
* ppvObj : 1
* xstm : 0 (released)
*/
return res;
}
/***********************************************************************
* OleSaveToStream (OLE32.@)
*
* This function saves an object with the IPersistStream interface on it
* to the specified stream.
*/
HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
{
CLSID clsid;
HRESULT res;
TRACE("(%p,%p)\n",pPStm,pStm);
res=IPersistStream_GetClassID(pPStm,&clsid);
if (SUCCEEDED(res)){
res=WriteClassStm(pStm,&clsid);
if (SUCCEEDED(res))
res=IPersistStream_Save(pPStm,pStm,TRUE);
}
TRACE("Finished Save\n");
return res;
}
/****************************************************************************
* This method validate a STGM parameter that can contain the values below
*
* The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
* The stgm values contained in 0xffff0000 are bitmasks.
*
* 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)
{
DWORD access = STGM_ACCESS_MODE(stgm);
DWORD share = STGM_SHARE_MODE(stgm);
DWORD create = STGM_CREATE_MODE(stgm);
if (stgm&~STGM_KNOWN_FLAGS)
{
ERR("unknown flags %08x\n", stgm);
return E_FAIL;
}
switch (access)
{
case STGM_READ:
case STGM_WRITE:
case STGM_READWRITE:
break;
default:
return E_FAIL;
}
switch (share)
{
case STGM_SHARE_DENY_NONE:
case STGM_SHARE_DENY_READ:
case STGM_SHARE_DENY_WRITE:
case STGM_SHARE_EXCLUSIVE:
break;
default:
return E_FAIL;
}
switch (create)
{
case STGM_CREATE:
case STGM_FAILIFTHERE:
break;
default:
return E_FAIL;
}
/*
* STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
*/
if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
return E_FAIL;
/*
* STGM_CREATE | STGM_CONVERT
* if both are false, STGM_FAILIFTHERE is set to TRUE
*/
if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
return E_FAIL;
/*
* STGM_NOSCRATCH requires STGM_TRANSACTED
*/
if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
return E_FAIL;
/*
* STGM_NOSNAPSHOT requires STGM_TRANSACTED and
* not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
*/
if ( (stgm & STGM_NOSNAPSHOT) &&
(!(stgm & STGM_TRANSACTED) ||
share == STGM_SHARE_EXCLUSIVE ||
share == STGM_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)
{
switch (STGM_SHARE_MODE(stgm))
{
case STGM_SHARE_DENY_NONE:
return FILE_SHARE_READ | FILE_SHARE_WRITE;
case STGM_SHARE_DENY_READ:
return FILE_SHARE_WRITE;
case STGM_SHARE_DENY_WRITE:
return FILE_SHARE_READ;
case STGM_SHARE_EXCLUSIVE:
return 0;
}
ERR("Invalid share mode!\n");
assert(0);
return 0;
}
/****************************************************************************
* GetAccessModeFromSTGM
*
* This method will return an access mode flag from a STGM value.
* The STGM value is assumed valid.
*/
static DWORD GetAccessModeFromSTGM(DWORD stgm)
{
switch (STGM_ACCESS_MODE(stgm))
{
case STGM_READ:
return GENERIC_READ;
case STGM_WRITE:
case STGM_READWRITE:
return GENERIC_READ | GENERIC_WRITE;
}
ERR("Invalid access mode!\n");
assert(0);
return 0;
}
/****************************************************************************
* GetCreationModeFromSTGM
*
* This method will return a creation mode flag from a STGM value.
* The STGM value is assumed valid.
*/
static DWORD GetCreationModeFromSTGM(DWORD stgm)
{
switch(STGM_CREATE_MODE(stgm))
{
case STGM_CREATE:
return CREATE_ALWAYS;
case STGM_CONVERT:
FIXME("STGM_CONVERT not implemented!\n");
return CREATE_NEW;
case STGM_FAILIFTHERE:
return CREATE_NEW;
}
ERR("Invalid create mode!\n");
assert(0);
return 0;
}
/*************************************************************************
* OLECONVERT_LoadOLE10 [Internal]
*
* Loads the OLE10 STREAM to memory
*
* PARAMS
* pOleStream [I] The OLESTREAM
* pData [I] Data Structure for the OLESTREAM Data
*
* RETURNS
* Success: S_OK
* Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
* CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
*
* NOTES
* This function is used by OleConvertOLESTREAMToIStorage only.
*
* Memory allocated for pData must be freed by the caller
*/
static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
{
DWORD dwSize;
HRESULT hRes = S_OK;
int nTryCnt=0;
int max_try = 6;
pData->pData = NULL;
pData->pstrOleObjFileName = (CHAR *) NULL;
for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
{
/* Get the OleID */
dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
if(dwSize != sizeof(pData->dwOleID))
{
hRes = CONVERT10_E_OLESTREAM_GET;
}
else if(pData->dwOleID != OLESTREAM_ID)
{
hRes = CONVERT10_E_OLESTREAM_FMT;
}
else
{
hRes = S_OK;
break;
}
}
if(hRes == S_OK)
{
/* Get the TypeID...more info needed for this field */
dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
if(dwSize != sizeof(pData->dwTypeID))
{
hRes = CONVERT10_E_OLESTREAM_GET;
}
}
if(hRes == S_OK)
{
if(pData->dwTypeID != 0)
{
/* Get the length of the OleTypeName */
dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
if(dwSize != sizeof(pData->dwOleTypeNameLength))
{
hRes = CONVERT10_E_OLESTREAM_GET;
}
if(hRes == S_OK)
{
if(pData->dwOleTypeNameLength > 0)
{
/* Get the OleTypeName */
dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
if(dwSize != pData->dwOleTypeNameLength)
{
hRes = CONVERT10_E_OLESTREAM_GET;
}
}
}
if(bStrem1)
{
dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
if(dwSize != sizeof(pData->dwOleObjFileNameLength))
{
hRes = CONVERT10_E_OLESTREAM_GET;
}
if(hRes == S_OK)
{
if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
if(pData->pstrOleObjFileName)
{
dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
if(dwSize != pData->dwOleObjFileNameLength)
{
hRes = CONVERT10_E_OLESTREAM_GET;
}
}
else
hRes = CONVERT10_E_OLESTREAM_GET;
}
}
else
{
/* Get the Width of the Metafile */
dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
if(dwSize != sizeof(pData->dwMetaFileWidth))
{
hRes = CONVERT10_E_OLESTREAM_GET;
}
if(hRes == S_OK)
{
/* Get the Height of the Metafile */
dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
if(dwSize != sizeof(pData->dwMetaFileHeight))
{
hRes = CONVERT10_E_OLESTREAM_GET;
}
}
}
if(hRes == S_OK)
{
/* Get the Length of the Data */
dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
if(dwSize != sizeof(pData->dwDataLength))
{
hRes = CONVERT10_E_OLESTREAM_GET;
}
}
if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
{
if(!bStrem1) /* if it is a second OLE stream data */
{
pData->dwDataLength -= 8;
dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
if(dwSize != sizeof(pData->strUnknown))
{
hRes = CONVERT10_E_OLESTREAM_GET;
}
}
}
if(hRes == S_OK)
{
if(pData->dwDataLength > 0)
{
pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
/* Get Data (ex. IStorage, Metafile, or BMP) */
if(pData->pData)
{
dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
if(dwSize != pData->dwDataLength)
{
hRes = CONVERT10_E_OLESTREAM_GET;
}
}
else
{
hRes = CONVERT10_E_OLESTREAM_GET;
}
}
}
}
}
return hRes;
}
/*************************************************************************
* OLECONVERT_SaveOLE10 [Internal]
*
* Saves the OLE10 STREAM From memory
*
* PARAMS
* pData [I] Data Structure for the OLESTREAM Data
* pOleStream [I] The OLESTREAM to save
*
* RETURNS
* Success: S_OK
* Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
*
* NOTES
* This function is used by OleConvertIStorageToOLESTREAM only.
*
*/
static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
{
DWORD dwSize;
HRESULT hRes = S_OK;
/* Set the OleID */
dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
if(dwSize != sizeof(pData->dwOleID))
{
hRes = CONVERT10_E_OLESTREAM_PUT;
}
if(hRes == S_OK)
{
/* Set the TypeID */
dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
if(dwSize != sizeof(pData->dwTypeID))
{
hRes = CONVERT10_E_OLESTREAM_PUT;
}
}
if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
{
/* Set the Length of the OleTypeName */
dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
if(dwSize != sizeof(pData->dwOleTypeNameLength))
{
hRes = CONVERT10_E_OLESTREAM_PUT;
}
if(hRes == S_OK)
{
if(pData->dwOleTypeNameLength > 0)
{
/* Set the OleTypeName */
dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
if(dwSize != pData->dwOleTypeNameLength)
{
hRes = CONVERT10_E_OLESTREAM_PUT;
}
}
}
if(hRes == S_OK)
{
/* Set the width of the Metafile */
dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
if(dwSize != sizeof(pData->dwMetaFileWidth))
{
hRes = CONVERT10_E_OLESTREAM_PUT;
}
}
if(hRes == S_OK)
{
/* Set the height of the Metafile */
dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
if(dwSize != sizeof(pData->dwMetaFileHeight))
{
hRes = CONVERT10_E_OLESTREAM_PUT;
}
}
if(hRes == S_OK)
{
/* Set the length of the Data */
dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
if(dwSize != sizeof(pData->dwDataLength))
{
hRes = CONVERT10_E_OLESTREAM_PUT;
}
}
if(hRes == S_OK)
{
if(pData->dwDataLength > 0)
{
/* Set the Data (eg. IStorage, Metafile, Bitmap) */
dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
if(dwSize != pData->dwDataLength)
{
hRes = CONVERT10_E_OLESTREAM_PUT;
}
}
}
}
return hRes;
}
/*************************************************************************
* OLECONVERT_GetOLE20FromOLE10[Internal]
*
* This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
* opens it, and copies the content to the dest IStorage for
* OleConvertOLESTREAMToIStorage
*
*
* PARAMS
* pDestStorage [I] The IStorage to copy the data to
* pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
* nBufferLength [I] The size of the buffer
*
* RETURNS
* Nothing
*
* NOTES
*
*
*/
static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
{
HRESULT hRes;
HANDLE hFile;
IStorage *pTempStorage;
DWORD dwNumOfBytesWritten;
WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
/* Create a temp File */
GetTempPathW(MAX_PATH, wstrTempDir);
GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if(hFile != INVALID_HANDLE_VALUE)
{
/* Write IStorage Data to File */
WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
CloseHandle(hFile);
/* Open and copy temp storage to the Dest Storage */
hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
if(hRes == S_OK)
{
hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
StorageBaseImpl_Release(pTempStorage);
}
DeleteFileW(wstrTempFile);
}
}
/*************************************************************************
* OLECONVERT_WriteOLE20ToBuffer [Internal]
*
* Saves the OLE10 STREAM From memory
*
* PARAMS
* pStorage [I] The Src IStorage to copy
* pData [I] The Dest Memory to write to.
*
* RETURNS
* The size in bytes allocated for pData
*
* NOTES
* Memory allocated for pData must be freed by the caller
*
* Used by OleConvertIStorageToOLESTREAM only.
*
*/
static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
{
HANDLE hFile;
HRESULT hRes;
DWORD nDataLength = 0;
IStorage *pTempStorage;
WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
*pData = NULL;
/* Create temp Storage */
GetTempPathW(MAX_PATH, wstrTempDir);
GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
if(hRes == S_OK)
{
/* Copy Src Storage to the Temp Storage */
StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
StorageBaseImpl_Release(pTempStorage);
/* Open Temp Storage as a file and copy to memory */
hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if(hFile != INVALID_HANDLE_VALUE)
{
nDataLength = GetFileSize(hFile, NULL);
*pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
CloseHandle(hFile);
}
DeleteFileW(wstrTempFile);
}
return nDataLength;
}
/*************************************************************************
* OLECONVERT_CreateOleStream [Internal]
*
* Creates the "\001OLE" stream in the IStorage if necessary.
*
* PARAMS
* pStorage [I] Dest storage to create the stream in
*
* RETURNS
* Nothing
*
* NOTES
* This function is used by OleConvertOLESTREAMToIStorage only.
*
* This stream is still unknown, MS Word seems to have extra data
* but since the data is stored in the OLESTREAM there should be
* no need to recreate the stream. If the stream is manually
* deleted it will create it with this default data.
*
*/
void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
{
HRESULT hRes;
IStream *pStream;
static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
BYTE pOleStreamHeader [] =
{
0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
};
/* Create stream if not present */
hRes = IStorage_CreateStream(pStorage, wstrStreamName,
STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
if(hRes == S_OK)
{
/* Write default Data */
hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
IStream_Release(pStream);
}
}
/* write a string to a stream, preceded by its length */
static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
{
HRESULT r;
LPSTR str;
DWORD len = 0;
if( string )
len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
r = IStream_Write( stm, &len, sizeof(len), NULL);
if( FAILED( r ) )
return r;
if(len == 0)
return r;
str = CoTaskMemAlloc( len );
WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
r = IStream_Write( stm, str, len, NULL);
CoTaskMemFree( str );
return r;
}
/* read a string preceded by its length from a stream */
static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
{
HRESULT r;
DWORD len, count = 0;
LPSTR str;
LPWSTR wstr;
r = IStream_Read( stm, &len, sizeof(len), &count );
if( FAILED( r ) )
return r;
if( count != sizeof(len) )
return E_OUTOFMEMORY;
TRACE("%d bytes\n",len);
str = CoTaskMemAlloc( len );
if( !str )
return E_OUTOFMEMORY;
count = 0;
r = IStream_Read( stm, str, len, &count );
if( FAILED( r ) )
return r;
if( count != len )
{
CoTaskMemFree( str );
return E_OUTOFMEMORY;
}
TRACE("Read string %s\n",debugstr_an(str,len));
len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
if( wstr )
MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
CoTaskMemFree( str );
*string = wstr;
return r;
}
static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
{
IStream *pstm;
HRESULT r = S_OK;
static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
static const BYTE unknown1[12] =
{ 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF};
static const BYTE unknown2[16] =
{ 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
debugstr_w(lpszUserType), debugstr_w(szClipName),
debugstr_w(szProgIDName));
/* Create a CompObj stream if it doesn't exist */
r = IStorage_CreateStream(pstg, szwStreamName,
STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
if( FAILED (r) )
return r;
/* Write CompObj Structure to stream */
r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
if( SUCCEEDED( r ) )
r = WriteClassStm( pstm, clsid );
if( SUCCEEDED( r ) )
r = STREAM_WriteString( pstm, lpszUserType );
if( SUCCEEDED( r ) )
r = STREAM_WriteString( pstm, szClipName );
if( SUCCEEDED( r ) )
r = STREAM_WriteString( pstm, szProgIDName );
if( SUCCEEDED( r ) )
r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
IStream_Release( pstm );
return r;
}
/***********************************************************************
* WriteFmtUserTypeStg (OLE32.@)
*/
HRESULT WINAPI WriteFmtUserTypeStg(
LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
{
HRESULT r;
WCHAR szwClipName[0x40];
CLSID clsid = CLSID_NULL;
LPWSTR wstrProgID = NULL;
DWORD n;
TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
/* get the clipboard format name */
n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
szwClipName[n]=0;
TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
/* FIXME: There's room to save a CLSID and its ProgID, but
the CLSID is not looked up in the registry and in all the
tests I wrote it was CLSID_NULL. Where does it come from?
*/
/* get the real program ID. This may fail, but that's fine */
ProgIDFromCLSID(&clsid, &wstrProgID);
TRACE("progid is %s\n",debugstr_w(wstrProgID));
r = STORAGE_WriteCompObj( pstg, &clsid,
lpszUserType, szwClipName, wstrProgID );
CoTaskMemFree(wstrProgID);
return r;
}
/******************************************************************************
* ReadFmtUserTypeStg [OLE32.@]
*/
HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
{
HRESULT r;
IStream *stm = 0;
static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
unsigned char unknown1[12];
unsigned char unknown2[16];
DWORD count;
LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
CLSID clsid;
TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
r = IStorage_OpenStream( pstg, szCompObj, NULL,
STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
if( FAILED ( r ) )
{
WARN("Failed to open stream r = %08x\n", r);
return r;
}
/* read the various parts of the structure */
r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
if( FAILED( r ) || ( count != sizeof(unknown1) ) )
goto end;
r = ReadClassStm( stm, &clsid );
if( FAILED( r ) )
goto end;
r = STREAM_ReadString( stm, &szCLSIDName );
if( FAILED( r ) )
goto end;
r = STREAM_ReadString( stm, &szOleTypeName );
if( FAILED( r ) )
goto end;
r = STREAM_ReadString( stm, &szProgIDName );
if( FAILED( r ) )
goto end;
r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
if( FAILED( r ) || ( count != sizeof(unknown2) ) )
goto end;
/* ok, success... now we just need to store what we found */
if( pcf )
*pcf = RegisterClipboardFormatW( szOleTypeName );
CoTaskMemFree( szOleTypeName );
if( lplpszUserType )
*lplpszUserType = szCLSIDName;
CoTaskMemFree( szProgIDName );
end:
IStream_Release( stm );
return r;
}
/*************************************************************************
* OLECONVERT_CreateCompObjStream [Internal]
*
* Creates a "\001CompObj" is the destination IStorage if necessary.
*
* PARAMS
* pStorage [I] The dest IStorage to create the CompObj Stream
* if necessary.
* strOleTypeName [I] The ProgID
*
* RETURNS
* Success: S_OK
* Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
*
* NOTES
* This function is used by OleConvertOLESTREAMToIStorage only.
*
* The stream data is stored in the OLESTREAM and there should be
* no need to recreate the stream. If the stream is manually
* deleted it will attempt to create it by querying the registry.
*
*
*/
HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
{
IStream *pStream;
HRESULT hStorageRes, hRes = S_OK;
OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
/* Initialize the CompObj structure */
memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
/* Create a CompObj stream if it doesn't exist */
hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
if(hStorageRes == S_OK)
{
/* copy the OleTypeName to the compobj struct */
IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
/* copy the OleTypeName to the compobj struct */
/* Note: in the test made, these were Identical */
IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
/* Get the CLSID */
MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
bufferW, OLESTREAM_MAX_STR_LEN );
hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
if(hRes == S_OK)
{
HKEY hKey;
LONG hErr;
/* Get the CLSID Default Name from the Registry */
hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
if(hErr == ERROR_SUCCESS)
{
char strTemp[OLESTREAM_MAX_STR_LEN];
IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
if(hErr == ERROR_SUCCESS)
{
strcpy(IStorageCompObj.strCLSIDName, strTemp);
}
RegCloseKey(hKey);
}
}
/* Write CompObj Structure to stream */
hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
WriteClassStm(pStream,&(IStorageCompObj.clsid));
hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
if(IStorageCompObj.dwCLSIDNameLength > 0)
{
hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
}
hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
if(IStorageCompObj.dwOleTypeNameLength > 0)
{
hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
}
hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
if(IStorageCompObj.dwProgIDNameLength > 0)
{
hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
}
hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
IStream_Release(pStream);
}
return hRes;
}
/*************************************************************************
* OLECONVERT_CreateOlePresStream[Internal]
*
* Creates the "\002OlePres000" Stream with the Metafile data
*
* PARAMS
* pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
* dwExtentX [I] Width of the Metafile
* dwExtentY [I] Height of the Metafile
* pData [I] Metafile data
* dwDataLength [I] Size of the Metafile data
*
* RETURNS
* Success: S_OK
* Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
*
* NOTES
* This function is used by OleConvertOLESTREAMToIStorage only.
*
*/
static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
{
HRESULT hRes;
IStream *pStream;
static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
BYTE pOlePresStreamHeader [] =
{
0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
};
BYTE pOlePresStreamHeaderEmpty [] =
{
0x00, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
};
/* Create the OlePres000 Stream */
hRes = IStorage_CreateStream(pStorage, wstrStreamName,
STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
if(hRes == S_OK)
{
DWORD nHeaderSize;
OLECONVERT_ISTORAGE_OLEPRES OlePres;
memset(&OlePres, 0, sizeof(OlePres));
/* Do we have any metafile data to save */
if(dwDataLength > 0)
{
memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
nHeaderSize = sizeof(pOlePresStreamHeader);
}
else
{
memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
}
/* Set width and height of the metafile */
OlePres.dwExtentX = dwExtentX;
OlePres.dwExtentY = -dwExtentY;
/* Set Data and Length */
if(dwDataLength > sizeof(METAFILEPICT16))
{
OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
OlePres.pData = &(pData[8]);
}
/* Save OlePres000 Data to Stream */
hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
if(OlePres.dwSize > 0)
{
hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
}
IStream_Release(pStream);
}
}
/*************************************************************************
* OLECONVERT_CreateOle10NativeStream [Internal]
*
* Creates the "\001Ole10Native" Stream (should contain a BMP)
*
* PARAMS
* pStorage [I] Dest storage to create the stream in
* pData [I] Ole10 Native Data (ex. bmp)
* dwDataLength [I] Size of the Ole10 Native Data
*
* RETURNS
* Nothing
*
* NOTES
* This function is used by OleConvertOLESTREAMToIStorage only.
*
* Might need to verify the data and return appropriate error message
*
*/
static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
{
HRESULT hRes;
IStream *pStream;
static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
/* Create the Ole10Native Stream */
hRes = IStorage_CreateStream(pStorage, wstrStreamName,
STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
if(hRes == S_OK)
{
/* Write info to stream */
hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
IStream_Release(pStream);
}
}
/*************************************************************************
* OLECONVERT_GetOLE10ProgID [Internal]
*
* Finds the ProgID (or OleTypeID) from the IStorage
*
* PARAMS
* pStorage [I] The Src IStorage to get the ProgID
* strProgID [I] the ProgID string to get
* dwSize [I] the size of the string
*
* RETURNS
* Success: S_OK
* Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
*
* NOTES
* This function is used by OleConvertIStorageToOLESTREAM only.
*
*
*/
static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
{
HRESULT hRes;
IStream *pStream;
LARGE_INTEGER iSeekPos;
OLECONVERT_ISTORAGE_COMPOBJ CompObj;
static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
/* Open the CompObj Stream */
hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
if(hRes == S_OK)
{
/*Get the OleType from the CompObj Stream */
iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
iSeekPos.u.HighPart = 0;
IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
if(*dwSize > 0)
{
IStream_Read(pStream, strProgID, *dwSize, NULL);
}
IStream_Release(pStream);
}
else
{
STATSTG stat;
LPOLESTR wstrProgID;
/* Get the OleType from the registry */
REFCLSID clsid = &(stat.clsid);
IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
hRes = ProgIDFromCLSID(clsid, &wstrProgID);
if(hRes == S_OK)
{
*dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
}
}
return hRes;
}
/*************************************************************************
* OLECONVERT_GetOle10PresData [Internal]
*
* Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
*
* PARAMS
* pStorage [I] Src IStroage
* pOleStream [I] Dest OleStream Mem Struct
*
* RETURNS
* Nothing
*
* NOTES
* This function is used by OleConvertIStorageToOLESTREAM only.
*
* Memory allocated for pData must be freed by the caller
*
*
*/
static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
{
HRESULT hRes;
IStream *pStream;
static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
/* Initialize Default data for OLESTREAM */
pOleStreamData[0].dwOleID = OLESTREAM_ID;
pOleStreamData[0].dwTypeID = 2;
pOleStreamData[1].dwOleID = OLESTREAM_ID;
pOleStreamData[1].dwTypeID = 0;
pOleStreamData[0].dwMetaFileWidth = 0;
pOleStreamData[0].dwMetaFileHeight = 0;
pOleStreamData[0].pData = NULL;
pOleStreamData[1].pData = NULL;
/* Open Ole10Native Stream */
hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
if(hRes == S_OK)
{
/* Read Size and Data */
IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
if(pOleStreamData->dwDataLength > 0)
{
pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
}
IStream_Release(pStream);
}
}
/*************************************************************************
* OLECONVERT_GetOle20PresData[Internal]
*
* Converts IStorage "/002OlePres000" stream to a OLE10 Stream
*
* PARAMS
* pStorage [I] Src IStroage
* pOleStreamData [I] Dest OleStream Mem Struct
*
* RETURNS
* Nothing
*
* NOTES
* This function is used by OleConvertIStorageToOLESTREAM only.
*
* Memory allocated for pData must be freed by the caller
*/
static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
{
HRESULT hRes;
IStream *pStream;
OLECONVERT_ISTORAGE_OLEPRES olePress;
static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
/* Initialize Default data for OLESTREAM */
pOleStreamData[0].dwOleID = OLESTREAM_ID;
pOleStreamData[0].dwTypeID = 2;
pOleStreamData[0].dwMetaFileWidth = 0;
pOleStreamData[0].dwMetaFileHeight = 0;
pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
pOleStreamData[1].dwOleID = OLESTREAM_ID;
pOleStreamData[1].dwTypeID = 0;
pOleStreamData[1].dwOleTypeNameLength = 0;
pOleStreamData[1].strOleTypeName[0] = 0;
pOleStreamData[1].dwMetaFileWidth = 0;
pOleStreamData[1].dwMetaFileHeight = 0;
pOleStreamData[1].pData = NULL;
pOleStreamData[1].dwDataLength = 0;
/* Open OlePress000 stream */
hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
if(hRes == S_OK)
{
LARGE_INTEGER iSeekPos;
METAFILEPICT16 MetaFilePict;
static const char strMetafilePictName[] = "METAFILEPICT";
/* Set the TypeID for a Metafile */
pOleStreamData[1].dwTypeID = 5;
/* Set the OleTypeName to Metafile */
pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
iSeekPos.u.HighPart = 0;
iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
/* Get Presentation Data */
IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
/*Set width and Height */
pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
if(olePress.dwSize > 0)
{
/* Set Length */
pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
/* Set MetaFilePict struct */
MetaFilePict.mm = 8;
MetaFilePict.xExt = olePress.dwExtentX;
MetaFilePict.yExt = olePress.dwExtentY;
MetaFilePict.hMF = 0;
/* Get Metafile Data */
pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
}
IStream_Release(pStream);
}
}
/*************************************************************************
* OleConvertOLESTREAMToIStorage [OLE32.@]
*
* Read info on MSDN
*
* TODO
* DVTARGETDEVICE paramenter is not handled
* Still unsure of some mem fields for OLE 10 Stream
* Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
* and "\001OLE" streams
*
*/
HRESULT WINAPI OleConvertOLESTREAMToIStorage (
LPOLESTREAM pOleStream,
LPSTORAGE pstg,
const DVTARGETDEVICE* ptd)
{
int i;
HRESULT hRes=S_OK;
OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
TRACE("%p %p %p\n", pOleStream, pstg, ptd);
memset(pOleStreamData, 0, sizeof(pOleStreamData));
if(ptd != NULL)
{
FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
}
if(pstg == NULL || pOleStream == NULL)
{
hRes = E_INVALIDARG;
}
if(hRes == S_OK)
{
/* Load the OLESTREAM to Memory */
hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
}
if(hRes == S_OK)
{
/* Load the OLESTREAM to Memory (part 2)*/
hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
}
if(hRes == S_OK)
{
if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
{
/* Do we have the IStorage Data in the OLESTREAM */
if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
{
OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
}
else
{
/* It must be an original OLE 1.0 source */
OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
}
}
else
{
/* It must be an original OLE 1.0 source */
OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
}
/* Create CompObj Stream if necessary */
hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
if(hRes == S_OK)
{
/*Create the Ole Stream if necessary */
OLECONVERT_CreateOleStream(pstg);
}
}
/* Free allocated memory */
for(i=0; i < 2; i++)
{
HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
pOleStreamData[i].pstrOleObjFileName = NULL;
}
return hRes;
}
/*************************************************************************
* OleConvertIStorageToOLESTREAM [OLE32.@]
*
* Read info on MSDN
*
* Read info on MSDN
*
* TODO
* Still unsure of some mem fields for OLE 10 Stream
* Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
* and "\001OLE" streams.
*
*/
HRESULT WINAPI OleConvertIStorageToOLESTREAM (
LPSTORAGE pstg,
LPOLESTREAM pOleStream)
{
int i;
HRESULT hRes = S_OK;
IStream *pStream;
OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
TRACE("%p %p\n", pstg, pOleStream);
memset(pOleStreamData, 0, sizeof(pOleStreamData));
if(pstg == NULL || pOleStream == NULL)
{
hRes = E_INVALIDARG;
}
if(hRes == S_OK)
{
/* Get the ProgID */
pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
}
if(hRes == S_OK)
{
/* Was it originally Ole10 */
hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
if(hRes == S_OK)
{
IStream_Release(pStream);
/* Get Presentation Data for Ole10Native */
OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
}
else
{
/* Get Presentation Data (OLE20) */
OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
}
/* Save OLESTREAM */
hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
if(hRes == S_OK)
{
hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
}
}
/* Free allocated memory */
for(i=0; i < 2; i++)
{
HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
}
return hRes;
}
/***********************************************************************
* GetConvertStg (OLE32.@)
*/
HRESULT WINAPI GetConvertStg(IStorage *stg) {
FIXME("unimplemented stub!\n");
return E_FAIL;
}
/******************************************************************************
* StgIsStorageFile [OLE32.@]
* Verify if the file contains a storage object
*
* PARAMS
* fn [ I] Filename
*
* RETURNS
* S_OK if file has magic bytes as a storage object
* S_FALSE if file is not storage
*/
HRESULT WINAPI
StgIsStorageFile(LPCOLESTR fn)
{
HANDLE hf;
BYTE magic[8];
DWORD bytes_read;
TRACE("%s\n", debugstr_w(fn));
hf = CreateFileW(fn, GENERIC_READ,
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (hf == INVALID_HANDLE_VALUE)
return STG_E_FILENOTFOUND;
if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
{
WARN(" unable to read file\n");
CloseHandle(hf);
return S_FALSE;
}
CloseHandle(hf);
if (bytes_read != 8) {
WARN(" too short\n");
return S_FALSE;
}
if (!memcmp(magic,STORAGE_magic,8)) {
WARN(" -> YES\n");
return S_OK;
}
WARN(" -> Invalid header.\n");
return S_FALSE;
}
/***********************************************************************
* WriteClassStm (OLE32.@)
*
* Writes a CLSID to a stream.
*
* PARAMS
* pStm [I] Stream to write to.
* rclsid [I] CLSID to write.
*
* RETURNS
* Success: S_OK.
* Failure: HRESULT code.
*/
HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
{
TRACE("(%p,%p)\n",pStm,rclsid);
if (!pStm || !rclsid)
return E_INVALIDARG;
return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
}
/***********************************************************************
* ReadClassStm (OLE32.@)
*
* Reads a CLSID from a stream.
*
* PARAMS
* pStm [I] Stream to read from.
* rclsid [O] CLSID to read.
*
* RETURNS
* Success: S_OK.
* Failure: HRESULT code.
*/
HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
{
ULONG nbByte;
HRESULT res;
TRACE("(%p,%p)\n",pStm,pclsid);
if (!pStm || !pclsid)
return E_INVALIDARG;
/* clear the output args */
memcpy(pclsid, &CLSID_NULL, sizeof(*pclsid));
res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
if (FAILED(res))
return res;
if (nbByte != sizeof(CLSID))
return STG_E_READFAULT;
else
return S_OK;
}