-/*\r
- * Compound Storage (32 bit version)\r
- * Storage implementation\r
- *\r
- * This file contains the compound file implementation\r
- * of the storage interface.\r
- *\r
- * Copyright 1999 Francis Beaudet\r
- * Copyright 1999 Sylvain St-Germain\r
- * Copyright 1999 Thuy Nguyen\r
- * Copyright 2005 Mike McCormack\r
- *\r
- * This library is free software; you can redistribute it and/or\r
- * modify it under the terms of the GNU Lesser General Public\r
- * License as published by the Free Software Foundation; either\r
- * version 2.1 of the License, or (at your option) any later version.\r
- *\r
- * This library is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
- * Lesser General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU Lesser General Public\r
- * License along with this library; if not, write to the Free Software\r
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
- */\r
-\r
-#include <assert.h>\r
-#include <stdarg.h>\r
-#include <stdio.h>\r
-#include <stdlib.h>\r
-#include <string.h>\r
-\r
-#define COBJMACROS\r
-#define NONAMELESSUNION\r
-#define NONAMELESSSTRUCT\r
-\r
-#include "windef.h"\r
-#include "winbase.h"\r
-#include "winnls.h"\r
-#include "winuser.h"\r
-#include "wine/unicode.h"\r
-#include "wine/debug.h"\r
-\r
-#include "storage32.h"\r
-#include "ole2.h" /* For Write/ReadClassStm */\r
-\r
-#include "winreg.h"\r
-#include "wine/wingdi16.h"\r
-\r
-WINE_DEFAULT_DEBUG_CHANNEL(storage);\r
-\r
-#define FILE_BEGIN 0\r
-\r
-/* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */\r
-#define OLESTREAM_ID 0x501\r
-#define OLESTREAM_MAX_STR_LEN 255\r
-\r
-static const char rootPropertyName[] = "Root Entry";\r
-\r
-\r
-/* OLESTREAM memory structure to use for Get and Put Routines */\r
-/* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */\r
-typedef struct\r
-{\r
- DWORD dwOleID;\r
- DWORD dwTypeID;\r
- DWORD dwOleTypeNameLength;\r
- CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];\r
- CHAR *pstrOleObjFileName;\r
- DWORD dwOleObjFileNameLength;\r
- DWORD dwMetaFileWidth;\r
- DWORD dwMetaFileHeight;\r
- CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */\r
- DWORD dwDataLength;\r
- BYTE *pData;\r
-}OLECONVERT_OLESTREAM_DATA;\r
-\r
-/* CompObj Stream structure */\r
-/* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */\r
-typedef struct\r
-{\r
- BYTE byUnknown1[12];\r
- CLSID clsid;\r
- DWORD dwCLSIDNameLength;\r
- CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];\r
- DWORD dwOleTypeNameLength;\r
- CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];\r
- DWORD dwProgIDNameLength;\r
- CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];\r
- BYTE byUnknown2[16];\r
-}OLECONVERT_ISTORAGE_COMPOBJ;\r
-\r
-\r
-/* Ole Presention Stream structure */\r
-/* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */\r
-typedef struct\r
-{\r
- BYTE byUnknown1[28];\r
- DWORD dwExtentX;\r
- DWORD dwExtentY;\r
- DWORD dwSize;\r
- BYTE *pData;\r
-}OLECONVERT_ISTORAGE_OLEPRES;\r
-\r
-\r
-\r
-/***********************************************************************\r
- * Forward declaration of internal functions used by the method DestroyElement\r
- */\r
-static HRESULT deleteStorageProperty(\r
- StorageImpl *parentStorage,\r
- ULONG foundPropertyIndexToDelete,\r
- StgProperty propertyToDelete);\r
-\r
-static HRESULT deleteStreamProperty(\r
- StorageImpl *parentStorage,\r
- ULONG foundPropertyIndexToDelete,\r
- StgProperty propertyToDelete);\r
-\r
-static HRESULT findPlaceholder(\r
- StorageImpl *storage,\r
- ULONG propertyIndexToStore,\r
- ULONG storagePropertyIndex,\r
- INT typeOfRelation);\r
-\r
-static HRESULT adjustPropertyChain(\r
- StorageImpl *This,\r
- StgProperty propertyToDelete,\r
- StgProperty parentProperty,\r
- ULONG parentPropertyId,\r
- INT typeOfRelation);\r
-\r
-/***********************************************************************\r
- * Declaration of the functions used to manipulate StgProperty\r
- */\r
-\r
-static ULONG getFreeProperty(\r
- StorageImpl *storage);\r
-\r
-static void updatePropertyChain(\r
- StorageImpl *storage,\r
- ULONG newPropertyIndex,\r
- StgProperty newProperty);\r
-\r
-static LONG propertyNameCmp(\r
- const OLECHAR *newProperty,\r
- const OLECHAR *currentProperty);\r
-\r
-\r
-/***********************************************************************\r
- * Declaration of miscellaneous functions...\r
- */\r
-static HRESULT validateSTGM(DWORD stgmValue);\r
-\r
-static DWORD GetShareModeFromSTGM(DWORD stgm);\r
-static DWORD GetAccessModeFromSTGM(DWORD stgm);\r
-static DWORD GetCreationModeFromSTGM(DWORD stgm);\r
-\r
-extern IPropertySetStorageVtbl IPropertySetStorage_Vtbl;\r
-\r
-\r
-\r
-/************************************************************************\r
-** Storage32BaseImpl implementatiion\r
-*/\r
-\r
-/************************************************************************\r
- * Storage32BaseImpl_QueryInterface (IUnknown)\r
- *\r
- * This method implements the common QueryInterface for all IStorage32\r
- * implementations contained in this file.\r
- *\r
- * See Windows documentation for more details on IUnknown methods.\r
- */\r
-HRESULT WINAPI StorageBaseImpl_QueryInterface(\r
- IStorage* iface,\r
- REFIID riid,\r
- void** ppvObject)\r
-{\r
- StorageBaseImpl *This = (StorageBaseImpl *)iface;\r
- /*\r
- * Perform a sanity check on the parameters.\r
- */\r
- if ( (This==0) || (ppvObject==0) )\r
- return E_INVALIDARG;\r
-\r
- /*\r
- * Initialize the return parameter.\r
- */\r
- *ppvObject = 0;\r
-\r
- /*\r
- * Compare the riid with the interface IDs implemented by this object.\r
- */\r
- if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)\r
- {\r
- *ppvObject = (IStorage*)This;\r
- }\r
- else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)\r
- {\r
- *ppvObject = (IStorage*)This;\r
- }\r
- else if (memcmp(&IID_IPropertySetStorage, riid, sizeof(IID_IPropertySetStorage)) == 0)\r
- {\r
- *ppvObject = (IStorage*)&This->pssVtbl;\r
- }\r
-\r
- /*\r
- * Check that we obtained an interface.\r
- */\r
- if ((*ppvObject)==0)\r
- return E_NOINTERFACE;\r
-\r
- /*\r
- * Query Interface always increases the reference count by one when it is\r
- * successful\r
- */\r
- IStorage_AddRef(iface);\r
-\r
- return S_OK;\r
-}\r
-\r
-/************************************************************************\r
- * Storage32BaseImpl_AddRef (IUnknown)\r
- *\r
- * This method implements the common AddRef for all IStorage32\r
- * implementations contained in this file.\r
- *\r
- * See Windows documentation for more details on IUnknown methods.\r
- */\r
-ULONG WINAPI StorageBaseImpl_AddRef(\r
- IStorage* iface)\r
-{\r
- StorageBaseImpl *This = (StorageBaseImpl *)iface;\r
- ULONG ref = InterlockedIncrement(&This->ref);\r
-\r
- TRACE("(%p) AddRef to %ld\n", This, ref);\r
-\r
- return ref;\r
-}\r
-\r
-/************************************************************************\r
- * Storage32BaseImpl_Release (IUnknown)\r
- *\r
- * This method implements the common Release for all IStorage32\r
- * implementations contained in this file.\r
- *\r
- * See Windows documentation for more details on IUnknown methods.\r
- */\r
-ULONG WINAPI StorageBaseImpl_Release(\r
- IStorage* iface)\r
-{\r
- StorageBaseImpl *This = (StorageBaseImpl *)iface;\r
- /*\r
- * Decrease the reference count on this object.\r
- */\r
- ULONG ref = InterlockedDecrement(&This->ref);\r
-\r
- TRACE("(%p) ReleaseRef to %ld\n", This, ref);\r
-\r
- /*\r
- * If the reference count goes down to 0, perform suicide.\r
- */\r
- if (ref == 0)\r
- {\r
- /*\r
- * Since we are using a system of base-classes, we want to call the\r
- * destructor of the appropriate derived class. To do this, we are\r
- * using virtual functions to implement the destructor.\r
- */\r
- This->v_destructor(This);\r
- }\r
-\r
- return ref;\r
-}\r
-\r
-/************************************************************************\r
- * Storage32BaseImpl_OpenStream (IStorage)\r
- *\r
- * This method will open the specified stream object from the current storage.\r
- *\r
- * See Windows documentation for more details on IStorage methods.\r
- */\r
-HRESULT WINAPI StorageBaseImpl_OpenStream(\r
- IStorage* iface,\r
- const OLECHAR* pwcsName, /* [string][in] */\r
- void* reserved1, /* [unique][in] */\r
- DWORD grfMode, /* [in] */\r
- DWORD reserved2, /* [in] */\r
- IStream** ppstm) /* [out] */\r
-{\r
- StorageBaseImpl *This = (StorageBaseImpl *)iface;\r
- IEnumSTATSTGImpl* propertyEnumeration;\r
- StgStreamImpl* newStream;\r
- StgProperty currentProperty;\r
- ULONG foundPropertyIndex;\r
- HRESULT res = STG_E_UNKNOWN;\r
- DWORD parent_grfMode;\r
-\r
- TRACE("(%p, %s, %p, %lx, %ld, %p)\n",\r
- iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);\r
-\r
- /*\r
- * Perform a sanity check on the parameters.\r
- */\r
- if ( (pwcsName==NULL) || (ppstm==0) )\r
- {\r
- res = E_INVALIDARG;\r
- goto end;\r
- }\r
-\r
- /*\r
- * Initialize the out parameter\r
- */\r
- *ppstm = NULL;\r
-\r
- /*\r
- * Validate the STGM flags\r
- */\r
- if ( FAILED( validateSTGM(grfMode) ))\r
- {\r
- res = STG_E_INVALIDFLAG;\r
- goto end;\r
- }\r
-\r
- /*\r
- * As documented.\r
- */\r
- if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||\r
- (grfMode & STGM_DELETEONRELEASE) ||\r
- (grfMode & STGM_TRANSACTED) )\r
- {\r
- res = STG_E_INVALIDFUNCTION;\r
- goto end;\r
- }\r
-\r
- /*\r
- * Check that we're compatible with the parent's storage mode\r
- */\r
- parent_grfMode = STGM_ACCESS_MODE( This->ancestorStorage->base.openFlags );\r
- if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )\r
- {\r
- res = STG_E_ACCESSDENIED;\r
- goto end;\r
- }\r
-\r
- /*\r
- * Create a property enumeration to search the properties\r
- */\r
- propertyEnumeration = IEnumSTATSTGImpl_Construct(\r
- This->ancestorStorage,\r
- This->rootPropertySetIndex);\r
-\r
- /*\r
- * Search the enumeration for the property with the given name\r
- */\r
- foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(\r
- propertyEnumeration,\r
- pwcsName,\r
- ¤tProperty);\r
-\r
- /*\r
- * Delete the property enumeration since we don't need it anymore\r
- */\r
- IEnumSTATSTGImpl_Destroy(propertyEnumeration);\r
-\r
- /*\r
- * If it was found, construct the stream object and return a pointer to it.\r
- */\r
- if ( (foundPropertyIndex!=PROPERTY_NULL) &&\r
- (currentProperty.propertyType==PROPTYPE_STREAM) )\r
- {\r
- newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);\r
-\r
- if (newStream!=0)\r
- {\r
- newStream->grfMode = grfMode;\r
- *ppstm = (IStream*)newStream;\r
-\r
- /*\r
- * Since we are returning a pointer to the interface, we have to\r
- * nail down the reference.\r
- */\r
- IStream_AddRef(*ppstm);\r
-\r
- res = S_OK;\r
- goto end;\r
- }\r
-\r
- res = E_OUTOFMEMORY;\r
- goto end;\r
- }\r
-\r
- res = STG_E_FILENOTFOUND;\r
-\r
-end:\r
- if (res == S_OK)\r
- TRACE("<-- IStream %p\n", *ppstm);\r
- TRACE("<-- %08lx\n", res);\r
- return res;\r
-}\r
-\r
-/************************************************************************\r
- * Storage32BaseImpl_OpenStorage (IStorage)\r
- *\r
- * This method will open a new storage object from the current storage.\r
- *\r
- * See Windows documentation for more details on IStorage methods.\r
- */\r
-HRESULT WINAPI StorageBaseImpl_OpenStorage(\r
- IStorage* iface,\r
- const OLECHAR* pwcsName, /* [string][unique][in] */\r
- IStorage* pstgPriority, /* [unique][in] */\r
- DWORD grfMode, /* [in] */\r
- SNB snbExclude, /* [unique][in] */\r
- DWORD reserved, /* [in] */\r
- IStorage** ppstg) /* [out] */\r
-{\r
- StorageBaseImpl *This = (StorageBaseImpl *)iface;\r
- StorageInternalImpl* newStorage;\r
- IEnumSTATSTGImpl* propertyEnumeration;\r
- StgProperty currentProperty;\r
- ULONG foundPropertyIndex;\r
- HRESULT res = STG_E_UNKNOWN;\r
- DWORD parent_grfMode;\r
-\r
- TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",\r
- iface, debugstr_w(pwcsName), pstgPriority,\r
- grfMode, snbExclude, reserved, ppstg);\r
-\r
- /*\r
- * Perform a sanity check on the parameters.\r
- */\r
- if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )\r
- {\r
- res = E_INVALIDARG;\r
- goto end;\r
- }\r
-\r
- /* as documented */\r
- if (snbExclude != NULL)\r
- {\r
- res = STG_E_INVALIDPARAMETER;\r
- goto end;\r
- }\r
-\r
- /*\r
- * Validate the STGM flags\r
- */\r
- if ( FAILED( validateSTGM(grfMode) ))\r
- {\r
- res = STG_E_INVALIDFLAG;\r
- goto end;\r
- }\r
-\r
- /*\r
- * As documented.\r
- */\r
- if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||\r
- (grfMode & STGM_DELETEONRELEASE) ||\r
- (grfMode & STGM_PRIORITY) )\r
- {\r
- res = STG_E_INVALIDFUNCTION;\r
- goto end;\r
- }\r
-\r
- /*\r
- * Check that we're compatible with the parent's storage mode\r
- */\r
- parent_grfMode = STGM_ACCESS_MODE( This->ancestorStorage->base.openFlags );\r
- if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )\r
- {\r
- res = STG_E_ACCESSDENIED;\r
- goto end;\r
- }\r
-\r
- /*\r
- * Initialize the out parameter\r
- */\r
- *ppstg = NULL;\r
-\r
- /*\r
- * Create a property enumeration to search the properties\r
- */\r
- propertyEnumeration = IEnumSTATSTGImpl_Construct(\r
- This->ancestorStorage,\r
- This->rootPropertySetIndex);\r
-\r
- /*\r
- * Search the enumeration for the property with the given name\r
- */\r
- foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(\r
- propertyEnumeration,\r
- pwcsName,\r
- ¤tProperty);\r
-\r
- /*\r
- * Delete the property enumeration since we don't need it anymore\r
- */\r
- IEnumSTATSTGImpl_Destroy(propertyEnumeration);\r
-\r
- /*\r
- * If it was found, construct the stream object and return a pointer to it.\r
- */\r
- if ( (foundPropertyIndex!=PROPERTY_NULL) &&\r
- (currentProperty.propertyType==PROPTYPE_STORAGE) )\r
- {\r
- /*\r
- * Construct a new Storage object\r
- */\r
- newStorage = StorageInternalImpl_Construct(\r
- This->ancestorStorage,\r
- grfMode,\r
- foundPropertyIndex);\r
-\r
- if (newStorage != 0)\r
- {\r
- *ppstg = (IStorage*)newStorage;\r
-\r
- /*\r
- * Since we are returning a pointer to the interface,\r
- * we have to nail down the reference.\r
- */\r
- StorageBaseImpl_AddRef(*ppstg);\r
-\r
- res = S_OK;\r
- goto end;\r
- }\r
-\r
- res = STG_E_INSUFFICIENTMEMORY;\r
- goto end;\r
- }\r
-\r
- res = STG_E_FILENOTFOUND;\r
-\r
-end:\r
- TRACE("<-- %08lx\n", res);\r
- return res;\r
-}\r
-\r
-/************************************************************************\r
- * Storage32BaseImpl_EnumElements (IStorage)\r
- *\r
- * This method will create an enumerator object that can be used to\r
- * retrieve informatino about all the properties in the storage object.\r
- *\r
- * See Windows documentation for more details on IStorage methods.\r
- */\r
-HRESULT WINAPI StorageBaseImpl_EnumElements(\r
- IStorage* iface,\r
- DWORD reserved1, /* [in] */\r
- void* reserved2, /* [size_is][unique][in] */\r
- DWORD reserved3, /* [in] */\r
- IEnumSTATSTG** ppenum) /* [out] */\r
-{\r
- StorageBaseImpl *This = (StorageBaseImpl *)iface;\r
- IEnumSTATSTGImpl* newEnum;\r
-\r
- TRACE("(%p, %ld, %p, %ld, %p)\n",\r
- iface, reserved1, reserved2, reserved3, ppenum);\r
-\r
- /*\r
- * Perform a sanity check on the parameters.\r
- */\r
- if ( (This==0) || (ppenum==0))\r
- return E_INVALIDARG;\r
-\r
- /*\r
- * Construct the enumerator.\r
- */\r
- newEnum = IEnumSTATSTGImpl_Construct(\r
- This->ancestorStorage,\r
- This->rootPropertySetIndex);\r
-\r
- if (newEnum!=0)\r
- {\r
- *ppenum = (IEnumSTATSTG*)newEnum;\r
-\r
- /*\r
- * Don't forget to nail down a reference to the new object before\r
- * returning it.\r
- */\r
- IEnumSTATSTG_AddRef(*ppenum);\r
-\r
- return S_OK;\r
- }\r
-\r
- return E_OUTOFMEMORY;\r
-}\r
-\r
-/************************************************************************\r
- * Storage32BaseImpl_Stat (IStorage)\r
- *\r
- * This method will retrieve information about this storage object.\r
- *\r
- * See Windows documentation for more details on IStorage methods.\r
- */\r
-HRESULT WINAPI StorageBaseImpl_Stat(\r
- IStorage* iface,\r
- STATSTG* pstatstg, /* [out] */\r
- DWORD grfStatFlag) /* [in] */\r
-{\r
- StorageBaseImpl *This = (StorageBaseImpl *)iface;\r
- StgProperty curProperty;\r
- BOOL readSuccessful;\r
- HRESULT res = STG_E_UNKNOWN;\r
-\r
- TRACE("(%p, %p, %lx)\n",\r
- iface, pstatstg, grfStatFlag);\r
-\r
- /*\r
- * Perform a sanity check on the parameters.\r
- */\r
- if ( (This==0) || (pstatstg==0))\r
- {\r
- res = E_INVALIDARG;\r
- goto end;\r
- }\r
-\r
- /*\r
- * Read the information from the property.\r
- */\r
- readSuccessful = StorageImpl_ReadProperty(\r
- This->ancestorStorage,\r
- This->rootPropertySetIndex,\r
- &curProperty);\r
-\r
- if (readSuccessful)\r
- {\r
- StorageUtl_CopyPropertyToSTATSTG(\r
- pstatstg,\r
- &curProperty,\r
- grfStatFlag);\r
-\r
- res = S_OK;\r
- goto end;\r
- }\r
-\r
- res = E_FAIL;\r
-\r
-end:\r
- if (res == S_OK)\r
- {\r
- TRACE("<-- STATSTG: pwcsName: %s, type: %ld, cbSize.Low/High: %ld/%ld, grfMode: %08lx, grfLocksSupported: %ld, grfStateBits: %08lx\n", debugstr_w(pstatstg->pwcsName), pstatstg->type, pstatstg->cbSize.u.LowPart, pstatstg->cbSize.u.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits);\r
- }\r
- TRACE("<-- %08lx\n", res);\r
- return res;\r
-}\r
-\r
-/************************************************************************\r
- * Storage32BaseImpl_RenameElement (IStorage)\r
- *\r
- * This method will rename the specified element.\r
- *\r
- * See Windows documentation for more details on IStorage methods.\r
- *\r
- * Implementation notes: The method used to rename consists of creating a clone\r
- * of the deleted StgProperty object setting it with the new name and to\r
- * perform a DestroyElement of the old StgProperty.\r
- */\r
-HRESULT WINAPI StorageBaseImpl_RenameElement(\r
- IStorage* iface,\r
- const OLECHAR* pwcsOldName, /* [in] */\r
- const OLECHAR* pwcsNewName) /* [in] */\r
-{\r
- StorageBaseImpl *This = (StorageBaseImpl *)iface;\r
- IEnumSTATSTGImpl* propertyEnumeration;\r
- StgProperty currentProperty;\r
- ULONG foundPropertyIndex;\r
-\r
- TRACE("(%p, %s, %s)\n",\r
- iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));\r
-\r
- /*\r
- * Create a property enumeration to search the properties\r
- */\r
- propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,\r
- This->rootPropertySetIndex);\r
-\r
- /*\r
- * Search the enumeration for the new property name\r
- */\r
- foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,\r
- pwcsNewName,\r
- ¤tProperty);\r
-\r
- if (foundPropertyIndex != PROPERTY_NULL)\r
- {\r
- /*\r
- * There is already a property with the new name\r
- */\r
- IEnumSTATSTGImpl_Destroy(propertyEnumeration);\r
- return STG_E_FILEALREADYEXISTS;\r
- }\r
-\r
- IEnumSTATSTG_Reset((IEnumSTATSTG*)propertyEnumeration);\r
-\r
- /*\r
- * Search the enumeration for the old property name\r
- */\r
- foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,\r
- pwcsOldName,\r
- ¤tProperty);\r
-\r
- /*\r
- * Delete the property enumeration since we don't need it anymore\r
- */\r
- IEnumSTATSTGImpl_Destroy(propertyEnumeration);\r
-\r
- if (foundPropertyIndex != PROPERTY_NULL)\r
- {\r
- StgProperty renamedProperty;\r
- ULONG renamedPropertyIndex;\r
-\r
- /*\r
- * Setup a new property for the renamed property\r
- */\r
- renamedProperty.sizeOfNameString =\r
- ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);\r
-\r
- if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)\r
- return STG_E_INVALIDNAME;\r
-\r
- strcpyW(renamedProperty.name, pwcsNewName);\r
-\r
- renamedProperty.propertyType = currentProperty.propertyType;\r
- renamedProperty.startingBlock = currentProperty.startingBlock;\r
- renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;\r
- renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;\r
-\r
- renamedProperty.previousProperty = PROPERTY_NULL;\r
- renamedProperty.nextProperty = PROPERTY_NULL;\r
-\r
- /*\r
- * Bring the dirProperty link in case it is a storage and in which\r
- * case the renamed storage elements don't require to be reorganized.\r
- */\r
- renamedProperty.dirProperty = currentProperty.dirProperty;\r
-\r
- /* call CoFileTime to get the current time\r
- renamedProperty.timeStampS1\r
- renamedProperty.timeStampD1\r
- renamedProperty.timeStampS2\r
- renamedProperty.timeStampD2\r
- renamedProperty.propertyUniqueID\r
- */\r
-\r
- /*\r
- * Obtain a free property in the property chain\r
- */\r
- renamedPropertyIndex = getFreeProperty(This->ancestorStorage);\r
-\r
- /*\r
- * Save the new property into the new property spot\r
- */\r
- StorageImpl_WriteProperty(\r
- This->ancestorStorage,\r
- renamedPropertyIndex,\r
- &renamedProperty);\r
-\r
- /*\r
- * Find a spot in the property chain for our newly created property.\r
- */\r
- updatePropertyChain(\r
- (StorageImpl*)This,\r
- renamedPropertyIndex,\r
- renamedProperty);\r
-\r
- /*\r
- * At this point the renamed property has been inserted in the tree,\r
- * now, before to Destroy the old property we must zeroed it's dirProperty\r
- * otherwise the DestroyProperty below will zap it all and we do not want\r
- * this to happen.\r
- * Also, we fake that the old property is a storage so the DestroyProperty\r
- * will not do a SetSize(0) on the stream data.\r
- *\r
- * This means that we need to tweek the StgProperty if it is a stream or a\r
- * non empty storage.\r
- */\r
- StorageImpl_ReadProperty(This->ancestorStorage,\r
- foundPropertyIndex,\r
- ¤tProperty);\r
-\r
- currentProperty.dirProperty = PROPERTY_NULL;\r
- currentProperty.propertyType = PROPTYPE_STORAGE;\r
- StorageImpl_WriteProperty(\r
- This->ancestorStorage,\r
- foundPropertyIndex,\r
- ¤tProperty);\r
-\r
- /*\r
- * Invoke Destroy to get rid of the ole property and automatically redo\r
- * the linking of it's previous and next members...\r
- */\r
- IStorage_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);\r
-\r
- }\r
- else\r
- {\r
- /*\r
- * There is no property with the old name\r
- */\r
- return STG_E_FILENOTFOUND;\r
- }\r
-\r
- return S_OK;\r
-}\r
-\r
-/************************************************************************\r
- * Storage32BaseImpl_CreateStream (IStorage)\r
- *\r
- * This method will create a stream object within this storage\r
- *\r
- * See Windows documentation for more details on IStorage methods.\r
- */\r
-HRESULT WINAPI StorageBaseImpl_CreateStream(\r
- IStorage* iface,\r
- const OLECHAR* pwcsName, /* [string][in] */\r
- DWORD grfMode, /* [in] */\r
- DWORD reserved1, /* [in] */\r
- DWORD reserved2, /* [in] */\r
- IStream** ppstm) /* [out] */\r
-{\r
- StorageBaseImpl *This = (StorageBaseImpl *)iface;\r
- IEnumSTATSTGImpl* propertyEnumeration;\r
- StgStreamImpl* newStream;\r
- StgProperty currentProperty, newStreamProperty;\r
- ULONG foundPropertyIndex, newPropertyIndex;\r
- DWORD parent_grfMode;\r
-\r
- TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",\r
- iface, debugstr_w(pwcsName), grfMode,\r
- reserved1, reserved2, ppstm);\r
-\r
- /*\r
- * Validate parameters\r
- */\r
- if (ppstm == 0)\r
- return STG_E_INVALIDPOINTER;\r
-\r
- if (pwcsName == 0)\r
- return STG_E_INVALIDNAME;\r
-\r
- if (reserved1 || reserved2)\r
- return STG_E_INVALIDPARAMETER;\r
-\r
- /*\r
- * Validate the STGM flags\r
- */\r
- if ( FAILED( validateSTGM(grfMode) ))\r
- return STG_E_INVALIDFLAG;\r
-\r
- if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE) \r
- return STG_E_INVALIDFLAG;\r
-\r
- /*\r
- * As documented.\r
- */\r
- if ((grfMode & STGM_DELETEONRELEASE) ||\r
- (grfMode & STGM_TRANSACTED))\r
- return STG_E_INVALIDFUNCTION;\r
-\r
- /*\r
- * Check that we're compatible with the parent's storage mode\r
- */\r
- parent_grfMode = STGM_ACCESS_MODE( This->ancestorStorage->base.openFlags );\r
- if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )\r
- return STG_E_ACCESSDENIED;\r
-\r
- /*\r
- * Initialize the out parameter\r
- */\r
- *ppstm = 0;\r
-\r
- /*\r
- * Create a property enumeration to search the properties\r
- */\r
- propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,\r
- This->rootPropertySetIndex);\r
-\r
- foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,\r
- pwcsName,\r
- ¤tProperty);\r
-\r
- IEnumSTATSTGImpl_Destroy(propertyEnumeration);\r
-\r
- if (foundPropertyIndex != PROPERTY_NULL)\r
- {\r
- /*\r
- * An element with this name already exists\r
- */\r
- if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)\r
- {\r
- IStorage_DestroyElement(iface, pwcsName);\r
- }\r
- else\r
- return STG_E_FILEALREADYEXISTS;\r
- }\r
-\r
- /*\r
- * memset the empty property\r
- */\r
- memset(&newStreamProperty, 0, sizeof(StgProperty));\r
-\r
- newStreamProperty.sizeOfNameString =\r
- ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);\r
-\r
- if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)\r
- return STG_E_INVALIDNAME;\r
-\r
- strcpyW(newStreamProperty.name, pwcsName);\r
-\r
- newStreamProperty.propertyType = PROPTYPE_STREAM;\r
- newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;\r
- newStreamProperty.size.u.LowPart = 0;\r
- newStreamProperty.size.u.HighPart = 0;\r
-\r
- newStreamProperty.previousProperty = PROPERTY_NULL;\r
- newStreamProperty.nextProperty = PROPERTY_NULL;\r
- newStreamProperty.dirProperty = PROPERTY_NULL;\r
-\r
- /* call CoFileTime to get the current time\r
- newStreamProperty.timeStampS1\r
- newStreamProperty.timeStampD1\r
- newStreamProperty.timeStampS2\r
- newStreamProperty.timeStampD2\r
- */\r
-\r
- /* newStreamProperty.propertyUniqueID */\r
-\r
- /*\r
- * Get a free property or create a new one\r
- */\r
- newPropertyIndex = getFreeProperty(This->ancestorStorage);\r
-\r
- /*\r
- * Save the new property into the new property spot\r
- */\r
- StorageImpl_WriteProperty(\r
- This->ancestorStorage,\r
- newPropertyIndex,\r
- &newStreamProperty);\r
-\r
- /*\r
- * Find a spot in the property chain for our newly created property.\r
- */\r
- updatePropertyChain(\r
- (StorageImpl*)This,\r
- newPropertyIndex,\r
- newStreamProperty);\r
-\r
- /*\r
- * Open the stream to return it.\r
- */\r
- newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);\r
-\r
- if (newStream != 0)\r
- {\r
- *ppstm = (IStream*)newStream;\r
-\r
- /*\r
- * Since we are returning a pointer to the interface, we have to nail down\r
- * the reference.\r
- */\r
- IStream_AddRef(*ppstm);\r
- }\r
- else\r
- {\r
- return STG_E_INSUFFICIENTMEMORY;\r
- }\r
-\r
- return S_OK;\r
-}\r
-\r
-/************************************************************************\r
- * Storage32BaseImpl_SetClass (IStorage)\r
- *\r
- * This method will write the specified CLSID in the property of this\r
- * storage.\r
- *\r
- * See Windows documentation for more details on IStorage methods.\r
- */\r
-HRESULT WINAPI StorageBaseImpl_SetClass(\r
- IStorage* iface,\r
- REFCLSID clsid) /* [in] */\r
-{\r
- StorageBaseImpl *This = (StorageBaseImpl *)iface;\r
- HRESULT hRes = E_FAIL;\r
- StgProperty curProperty;\r
- BOOL success;\r
-\r
- TRACE("(%p, %p)\n", iface, clsid);\r
-\r
- success = StorageImpl_ReadProperty(This->ancestorStorage,\r
- This->rootPropertySetIndex,\r
- &curProperty);\r
- if (success)\r
- {\r
- curProperty.propertyUniqueID = *clsid;\r
-\r
- success = StorageImpl_WriteProperty(This->ancestorStorage,\r
- This->rootPropertySetIndex,\r
- &curProperty);\r
- if (success)\r
- hRes = S_OK;\r
- }\r
-\r
- return hRes;\r
-}\r
-\r
-/************************************************************************\r
-** Storage32Impl implementation\r
-*/\r
-\r
-/************************************************************************\r
- * Storage32Impl_CreateStorage (IStorage)\r
- *\r
- * This method will create the storage object within the provided storage.\r
- *\r
- * See Windows documentation for more details on IStorage methods.\r
- */\r
-HRESULT WINAPI StorageImpl_CreateStorage(\r
- IStorage* iface,\r
- const OLECHAR *pwcsName, /* [string][in] */\r
- DWORD grfMode, /* [in] */\r
- DWORD reserved1, /* [in] */\r
- DWORD reserved2, /* [in] */\r
- IStorage **ppstg) /* [out] */\r
-{\r
- StorageImpl* const This=(StorageImpl*)iface;\r
-\r
- IEnumSTATSTGImpl *propertyEnumeration;\r
- StgProperty currentProperty;\r
- StgProperty newProperty;\r
- ULONG foundPropertyIndex;\r
- ULONG newPropertyIndex;\r
- HRESULT hr;\r
- DWORD parent_grfMode;\r
-\r
- TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",\r
- iface, debugstr_w(pwcsName), grfMode,\r
- reserved1, reserved2, ppstg);\r
-\r
- /*\r
- * Validate parameters\r
- */\r
- if (ppstg == 0)\r
- return STG_E_INVALIDPOINTER;\r
-\r
- if (pwcsName == 0)\r
- return STG_E_INVALIDNAME;\r
-\r
- /*\r
- * Validate the STGM flags\r
- */\r
- if ( FAILED( validateSTGM(grfMode) ) ||\r
- (grfMode & STGM_DELETEONRELEASE) )\r
- return STG_E_INVALIDFLAG;\r
-\r
- /*\r
- * Check that we're compatible with the parent's storage mode\r
- */\r
- parent_grfMode = STGM_ACCESS_MODE( This->base.ancestorStorage->base.openFlags );\r
- if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )\r
- return STG_E_ACCESSDENIED;\r
-\r
- /*\r
- * Initialize the out parameter\r
- */\r
- *ppstg = 0;\r
-\r
- /*\r
- * Create a property enumeration and search the properties\r
- */\r
- propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,\r
- This->base.rootPropertySetIndex);\r
-\r
- foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,\r
- pwcsName,\r
- ¤tProperty);\r
- IEnumSTATSTGImpl_Destroy(propertyEnumeration);\r
-\r
- if (foundPropertyIndex != PROPERTY_NULL)\r
- {\r
- /*\r
- * An element with this name already exists\r
- */\r
- if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)\r
- IStorage_DestroyElement(iface, pwcsName);\r
- else\r
- return STG_E_FILEALREADYEXISTS;\r
- }\r
-\r
- /*\r
- * memset the empty property\r
- */\r
- memset(&newProperty, 0, sizeof(StgProperty));\r
-\r
- newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);\r
-\r
- if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)\r
- return STG_E_INVALIDNAME;\r
-\r
- strcpyW(newProperty.name, pwcsName);\r
-\r
- newProperty.propertyType = PROPTYPE_STORAGE;\r
- newProperty.startingBlock = BLOCK_END_OF_CHAIN;\r
- newProperty.size.u.LowPart = 0;\r
- newProperty.size.u.HighPart = 0;\r
-\r
- newProperty.previousProperty = PROPERTY_NULL;\r
- newProperty.nextProperty = PROPERTY_NULL;\r
- newProperty.dirProperty = PROPERTY_NULL;\r
-\r
- /* call CoFileTime to get the current time\r
- newProperty.timeStampS1\r
- newProperty.timeStampD1\r
- newProperty.timeStampS2\r
- newProperty.timeStampD2\r
- */\r
-\r
- /* newStorageProperty.propertyUniqueID */\r
-\r
- /*\r
- * Obtain a free property in the property chain\r
- */\r
- newPropertyIndex = getFreeProperty(This->base.ancestorStorage);\r
-\r
- /*\r
- * Save the new property into the new property spot\r
- */\r
- StorageImpl_WriteProperty(\r
- This->base.ancestorStorage,\r
- newPropertyIndex,\r
- &newProperty);\r
-\r
- /*\r
- * Find a spot in the property chain for our newly created property.\r
- */\r
- updatePropertyChain(\r
- This,\r
- newPropertyIndex,\r
- newProperty);\r
-\r
- /*\r
- * Open it to get a pointer to return.\r
- */\r
- hr = IStorage_OpenStorage(\r
- iface,\r
- (const OLECHAR*)pwcsName,\r
- 0,\r
- grfMode,\r
- 0,\r
- 0,\r
- ppstg);\r
-\r
- if( (hr != S_OK) || (*ppstg == NULL))\r
- {\r
- return hr;\r
- }\r
-\r
-\r
- return S_OK;\r
-}\r
-\r
-\r
-/***************************************************************************\r
- *\r
- * Internal Method\r
- *\r
- * Get a free property or create a new one.\r
- */\r
-static ULONG getFreeProperty(\r
- StorageImpl *storage)\r
-{\r
- ULONG currentPropertyIndex = 0;\r
- ULONG newPropertyIndex = PROPERTY_NULL;\r
- BOOL readSuccessful = TRUE;\r
- StgProperty currentProperty;\r
-\r
- do\r
- {\r
- /*\r
- * Start by reading the root property\r
- */\r
- readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,\r
- currentPropertyIndex,\r
- ¤tProperty);\r
- if (readSuccessful)\r
- {\r
- if (currentProperty.sizeOfNameString == 0)\r
- {\r
- /*\r
- * The property existis and is available, we found it.\r
- */\r
- newPropertyIndex = currentPropertyIndex;\r
- }\r
- }\r
- else\r
- {\r
- /*\r
- * We exhausted the property list, we will create more space below\r
- */\r
- newPropertyIndex = currentPropertyIndex;\r
- }\r
- currentPropertyIndex++;\r
-\r
- } while (newPropertyIndex == PROPERTY_NULL);\r
-\r
- /*\r
- * grow the property chain\r
- */\r
- if (! readSuccessful)\r
- {\r
- StgProperty emptyProperty;\r
- ULARGE_INTEGER newSize;\r
- ULONG propertyIndex;\r
- ULONG lastProperty = 0;\r
- ULONG blockCount = 0;\r
-\r
- /*\r
- * obtain the new count of property blocks\r
- */\r
- blockCount = BlockChainStream_GetCount(\r
- storage->base.ancestorStorage->rootBlockChain)+1;\r
-\r
- /*\r
- * initialize the size used by the property stream\r
- */\r
- newSize.u.HighPart = 0;\r
- newSize.u.LowPart = storage->bigBlockSize * blockCount;\r
-\r
- /*\r
- * add a property block to the property chain\r
- */\r
- BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);\r
-\r
- /*\r
- * memset the empty property in order to initialize the unused newly\r
- * created property\r
- */\r
- memset(&emptyProperty, 0, sizeof(StgProperty));\r
-\r
- /*\r
- * initialize them\r
- */\r
- lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;\r
-\r
- for(\r
- propertyIndex = newPropertyIndex;\r
- propertyIndex < lastProperty;\r
- propertyIndex++)\r
- {\r
- StorageImpl_WriteProperty(\r
- storage->base.ancestorStorage,\r
- propertyIndex,\r
- &emptyProperty);\r
- }\r
- }\r
-\r
- return newPropertyIndex;\r
-}\r
-\r
-/****************************************************************************\r
- *\r
- * Internal Method\r
- *\r
- * Case insensitive comparaison of StgProperty.name by first considering\r
- * their size.\r
- *\r
- * Returns <0 when newPrpoerty < currentProperty\r
- * >0 when newPrpoerty > currentProperty\r
- * 0 when newPrpoerty == currentProperty\r
- */\r
-static LONG propertyNameCmp(\r
- const OLECHAR *newProperty,\r
- const OLECHAR *currentProperty)\r
-{\r
- LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);\r
-\r
- if (diff == 0)\r
- {\r
- /*\r
- * We compare the string themselves only when they are of the same length\r
- */\r
- diff = lstrcmpiW( newProperty, currentProperty);\r
- }\r
-\r
- return diff;\r
-}\r
-\r
-/****************************************************************************\r
- *\r
- * Internal Method\r
- *\r
- * Properly link this new element in the property chain.\r
- */\r
-static void updatePropertyChain(\r
- StorageImpl *storage,\r
- ULONG newPropertyIndex,\r
- StgProperty newProperty)\r
-{\r
- StgProperty currentProperty;\r
-\r
- /*\r
- * Read the root property\r
- */\r
- StorageImpl_ReadProperty(storage->base.ancestorStorage,\r
- storage->base.rootPropertySetIndex,\r
- ¤tProperty);\r
-\r
- if (currentProperty.dirProperty != PROPERTY_NULL)\r
- {\r
- /*\r
- * The root storage contains some element, therefore, start the research\r
- * for the appropriate location.\r
- */\r
- BOOL found = 0;\r
- ULONG current, next, previous, currentPropertyId;\r
-\r
- /*\r
- * Keep the StgProperty sequence number of the storage first property\r
- */\r
- currentPropertyId = currentProperty.dirProperty;\r
-\r
- /*\r
- * Read\r
- */\r
- StorageImpl_ReadProperty(storage->base.ancestorStorage,\r
- currentProperty.dirProperty,\r
- ¤tProperty);\r
-\r
- previous = currentProperty.previousProperty;\r
- next = currentProperty.nextProperty;\r
- current = currentPropertyId;\r
-\r
- while (found == 0)\r
- {\r
- LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);\r
-\r
- if (diff < 0)\r
- {\r
- if (previous != PROPERTY_NULL)\r
- {\r
- StorageImpl_ReadProperty(storage->base.ancestorStorage,\r
- previous,\r
- ¤tProperty);\r
- current = previous;\r
- }\r
- else\r
- {\r
- currentProperty.previousProperty = newPropertyIndex;\r
- StorageImpl_WriteProperty(storage->base.ancestorStorage,\r
- current,\r
- ¤tProperty);\r
- found = 1;\r
- }\r
- }\r
- else if (diff > 0)\r
- {\r
- if (next != PROPERTY_NULL)\r
- {\r
- StorageImpl_ReadProperty(storage->base.ancestorStorage,\r
- next,\r
- ¤tProperty);\r
- current = next;\r
- }\r
- else\r
- {\r
- currentProperty.nextProperty = newPropertyIndex;\r
- StorageImpl_WriteProperty(storage->base.ancestorStorage,\r
- current,\r
- ¤tProperty);\r
- found = 1;\r
- }\r
- }\r
- else\r
- {\r
- /*\r
- * Trying to insert an item with the same name in the\r
- * subtree structure.\r
- */\r
- assert(FALSE);\r
- }\r
-\r
- previous = currentProperty.previousProperty;\r
- next = currentProperty.nextProperty;\r
- }\r
- }\r
- else\r
- {\r
- /*\r
- * The root storage is empty, link the new property to it's dir property\r
- */\r
- currentProperty.dirProperty = newPropertyIndex;\r
- StorageImpl_WriteProperty(storage->base.ancestorStorage,\r
- storage->base.rootPropertySetIndex,\r
- ¤tProperty);\r
- }\r
-}\r
-\r
-\r
-/*************************************************************************\r
- * CopyTo (IStorage)\r
- */\r
-HRESULT WINAPI StorageImpl_CopyTo(\r
- IStorage* iface,\r
- DWORD ciidExclude, /* [in] */\r
- const IID* rgiidExclude, /* [size_is][unique][in] */\r
- SNB snbExclude, /* [unique][in] */\r
- IStorage* pstgDest) /* [unique][in] */\r
-{\r
- IEnumSTATSTG *elements = 0;\r
- STATSTG curElement, strStat;\r
- HRESULT hr;\r
- IStorage *pstgTmp, *pstgChild;\r
- IStream *pstrTmp, *pstrChild;\r
-\r
- if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))\r
- FIXME("Exclude option not implemented\n");\r
-\r
- TRACE("(%p, %ld, %p, %p, %p)\n",\r
- iface, ciidExclude, rgiidExclude,\r
- snbExclude, pstgDest);\r
-\r
- /*\r
- * Perform a sanity check\r
- */\r
- if ( pstgDest == 0 )\r
- return STG_E_INVALIDPOINTER;\r
-\r
- /*\r
- * Enumerate the elements\r
- */\r
- hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );\r
-\r
- if ( hr != S_OK )\r
- return hr;\r
-\r
- /*\r
- * set the class ID\r
- */\r
- IStorage_Stat( iface, &curElement, STATFLAG_NONAME);\r
- IStorage_SetClass( pstgDest, &curElement.clsid );\r
-\r
- do\r
- {\r
- /*\r
- * Obtain the next element\r
- */\r
- hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );\r
-\r
- if ( hr == S_FALSE )\r
- {\r
- hr = S_OK; /* done, every element has been copied */\r
- break;\r
- }\r
-\r
- if (curElement.type == STGTY_STORAGE)\r
- {\r
- /*\r
- * open child source storage\r
- */\r
- hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,\r
- STGM_READ|STGM_SHARE_EXCLUSIVE,\r
- NULL, 0, &pstgChild );\r
-\r
- if (hr != S_OK)\r
- break;\r
-\r
- /*\r
- * Check if destination storage is not a child of the source\r
- * storage, which will cause an infinite loop\r
- */\r
- if (pstgChild == pstgDest)\r
- {\r
- IEnumSTATSTG_Release(elements);\r
-\r
- return STG_E_ACCESSDENIED;\r
- }\r
-\r
- /*\r
- * create a new storage in destination storage\r
- */\r
- hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,\r
- STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,\r
- 0, 0,\r
- &pstgTmp );\r
- /*\r
- * if it already exist, don't create a new one use this one\r
- */\r
- if (hr == STG_E_FILEALREADYEXISTS)\r
- {\r
- hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,\r
- STGM_WRITE|STGM_SHARE_EXCLUSIVE,\r
- NULL, 0, &pstgTmp );\r
- }\r
-\r
- if (hr != S_OK)\r
- break;\r
-\r
-\r
- /*\r
- * do the copy recursively\r
- */\r
- hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,\r
- snbExclude, pstgTmp );\r
-\r
- IStorage_Release( pstgTmp );\r
- IStorage_Release( pstgChild );\r
- }\r
- else if (curElement.type == STGTY_STREAM)\r
- {\r
- /*\r
- * create a new stream in destination storage. If the stream already\r
- * exist, it will be deleted and a new one will be created.\r
- */\r
- hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,\r
- STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,\r
- 0, 0, &pstrTmp );\r
-\r
- if (hr != S_OK)\r
- break;\r
-\r
- /*\r
- * open child stream storage\r
- */\r
- hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,\r
- STGM_READ|STGM_SHARE_EXCLUSIVE,\r
- 0, &pstrChild );\r
-\r
- if (hr != S_OK)\r
- break;\r
-\r
- /*\r
- * Get the size of the source stream\r
- */\r
- IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );\r
-\r
- /*\r
- * Set the size of the destination stream.\r
- */\r
- IStream_SetSize(pstrTmp, strStat.cbSize);\r
-\r
- /*\r
- * do the copy\r
- */\r
- hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,\r
- NULL, NULL );\r
-\r
- IStream_Release( pstrTmp );\r
- IStream_Release( pstrChild );\r
- }\r
- else\r
- {\r
- WARN("unknown element type: %ld\n", curElement.type);\r
- }\r
-\r
- } while (hr == S_OK);\r
-\r
- /*\r
- * Clean-up\r
- */\r
- IEnumSTATSTG_Release(elements);\r
-\r
- return hr;\r
-}\r
-\r
-/*************************************************************************\r
- * MoveElementTo (IStorage)\r
- */\r
-HRESULT WINAPI StorageImpl_MoveElementTo(\r
- IStorage* iface,\r
- const OLECHAR *pwcsName, /* [string][in] */\r
- IStorage *pstgDest, /* [unique][in] */\r
- const OLECHAR *pwcsNewName,/* [string][in] */\r
- DWORD grfFlags) /* [in] */\r
-{\r
- FIXME("not implemented!\n");\r
- return E_NOTIMPL;\r
-}\r
-\r
-/*************************************************************************\r
- * Commit (IStorage)\r
- */\r
-HRESULT WINAPI StorageImpl_Commit(\r
- IStorage* iface,\r
- DWORD grfCommitFlags)/* [in] */\r
-{\r
- FIXME("(%ld): stub!\n", grfCommitFlags);\r
- return S_OK;\r
-}\r
-\r
-/*************************************************************************\r
- * Revert (IStorage)\r
- */\r
-HRESULT WINAPI StorageImpl_Revert(\r
- IStorage* iface)\r
-{\r
- FIXME("not implemented!\n");\r
- return E_NOTIMPL;\r
-}\r
-\r
-/*************************************************************************\r
- * DestroyElement (IStorage)\r
- *\r
- * Stategy: This implementation is build this way for simplicity not for speed.\r
- * I always delete the top most element of the enumeration and adjust\r
- * the deleted element pointer all the time. This takes longer to\r
- * do but allow to reinvoke DestroyElement whenever we encounter a\r
- * storage object. The optimisation reside in the usage of another\r
- * enumeration stategy that would give all the leaves of a storage\r
- * first. (postfix order)\r
- */\r
-HRESULT WINAPI StorageImpl_DestroyElement(\r
- IStorage* iface,\r
- const OLECHAR *pwcsName)/* [string][in] */\r
-{\r
- StorageImpl* const This=(StorageImpl*)iface;\r
-\r
- IEnumSTATSTGImpl* propertyEnumeration;\r
- HRESULT hr = S_OK;\r
- BOOL res;\r
- StgProperty propertyToDelete;\r
- StgProperty parentProperty;\r
- ULONG foundPropertyIndexToDelete;\r
- ULONG typeOfRelation;\r
- ULONG parentPropertyId;\r
-\r
- TRACE("(%p, %s)\n",\r
- iface, debugstr_w(pwcsName));\r
-\r
- /*\r
- * Perform a sanity check on the parameters.\r
- */\r
- if (pwcsName==NULL)\r
- return STG_E_INVALIDPOINTER;\r
-\r
- /*\r
- * Create a property enumeration to search the property with the given name\r
- */\r
- propertyEnumeration = IEnumSTATSTGImpl_Construct(\r
- This->base.ancestorStorage,\r
- This->base.rootPropertySetIndex);\r
-\r
- foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(\r
- propertyEnumeration,\r
- pwcsName,\r
- &propertyToDelete);\r
-\r
- IEnumSTATSTGImpl_Destroy(propertyEnumeration);\r
-\r
- if ( foundPropertyIndexToDelete == PROPERTY_NULL )\r
- {\r
- return STG_E_FILENOTFOUND;\r
- }\r
-\r
- /*\r
- * Find the parent property of the property to delete (the one that\r
- * link to it). If This->dirProperty == foundPropertyIndexToDelete,\r
- * the parent is This. Otherwise, the parent is one of it's sibling...\r
- */\r
-\r
- /*\r
- * First, read This's StgProperty..\r
- */\r
- res = StorageImpl_ReadProperty(\r
- This->base.ancestorStorage,\r
- This->base.rootPropertySetIndex,\r
- &parentProperty);\r
-\r
- assert(res);\r
-\r
- /*\r
- * Second, check to see if by any chance the actual storage (This) is not\r
- * the parent of the property to delete... We never know...\r
- */\r
- if ( parentProperty.dirProperty == foundPropertyIndexToDelete )\r
- {\r
- /*\r
- * Set data as it would have been done in the else part...\r
- */\r
- typeOfRelation = PROPERTY_RELATION_DIR;\r
- parentPropertyId = This->base.rootPropertySetIndex;\r
- }\r
- else\r
- {\r
- /*\r
- * Create a property enumeration to search the parent properties, and\r
- * delete it once done.\r
- */\r
- IEnumSTATSTGImpl* propertyEnumeration2;\r
-\r
- propertyEnumeration2 = IEnumSTATSTGImpl_Construct(\r
- This->base.ancestorStorage,\r
- This->base.rootPropertySetIndex);\r
-\r
- typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(\r
- propertyEnumeration2,\r
- foundPropertyIndexToDelete,\r
- &parentProperty,\r
- &parentPropertyId);\r
-\r
- IEnumSTATSTGImpl_Destroy(propertyEnumeration2);\r
- }\r
-\r
- if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )\r
- {\r
- hr = deleteStorageProperty(\r
- This,\r
- foundPropertyIndexToDelete,\r
- propertyToDelete);\r
- }\r
- else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )\r
- {\r
- hr = deleteStreamProperty(\r
- This,\r
- foundPropertyIndexToDelete,\r
- propertyToDelete);\r
- }\r
-\r
- if (hr!=S_OK)\r
- return hr;\r
-\r
- /*\r
- * Adjust the property chain\r
- */\r
- hr = adjustPropertyChain(\r
- This,\r
- propertyToDelete,\r
- parentProperty,\r
- parentPropertyId,\r
- typeOfRelation);\r
-\r
- return hr;\r
-}\r
-\r
-\r
-/************************************************************************\r
- * StorageImpl_Stat (IStorage)\r
- *\r
- * This method will retrieve information about this storage object.\r
- *\r
- * See Windows documentation for more details on IStorage methods.\r
- */\r
-HRESULT WINAPI StorageImpl_Stat( IStorage* iface,\r
- STATSTG* pstatstg, /* [out] */\r
- DWORD grfStatFlag) /* [in] */\r
-{\r
- StorageImpl* const This = (StorageImpl*)iface;\r
- HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );\r
-\r
- if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )\r
- {\r
- CoTaskMemFree(pstatstg->pwcsName);\r
- pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));\r
- strcpyW(pstatstg->pwcsName, This->pwcsName);\r
- }\r
-\r
- return result;\r
-}\r
-\r
-\r
-\r
-/*********************************************************************\r
- *\r
- * Internal Method\r
- *\r
- * Perform the deletion of a complete storage node\r
- *\r
- */\r
-static HRESULT deleteStorageProperty(\r
- StorageImpl *parentStorage,\r
- ULONG indexOfPropertyToDelete,\r
- StgProperty propertyToDelete)\r
-{\r
- IEnumSTATSTG *elements = 0;\r
- IStorage *childStorage = 0;\r
- STATSTG currentElement;\r
- HRESULT hr;\r
- HRESULT destroyHr = S_OK;\r
-\r
- /*\r
- * Open the storage and enumerate it\r
- */\r
- hr = StorageBaseImpl_OpenStorage(\r
- (IStorage*)parentStorage,\r
- propertyToDelete.name,\r
- 0,\r
- STGM_SHARE_EXCLUSIVE,\r
- 0,\r
- 0,\r
- &childStorage);\r
-\r
- if (hr != S_OK)\r
- {\r
- return hr;\r
- }\r
-\r
- /*\r
- * Enumerate the elements\r
- */\r
- IStorage_EnumElements( childStorage, 0, 0, 0, &elements);\r
-\r
- do\r
- {\r
- /*\r
- * Obtain the next element\r
- */\r
- hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);\r
- if (hr==S_OK)\r
- {\r
- destroyHr = StorageImpl_DestroyElement(\r
- (IStorage*)childStorage,\r
- (OLECHAR*)currentElement.pwcsName);\r
-\r
- CoTaskMemFree(currentElement.pwcsName);\r
- }\r
-\r
- /*\r
- * We need to Reset the enumeration every time because we delete elements\r
- * and the enumeration could be invalid\r
- */\r
- IEnumSTATSTG_Reset(elements);\r
-\r
- } while ((hr == S_OK) && (destroyHr == S_OK));\r
-\r
- /*\r
- * Invalidate the property by zeroing it's name member.\r
- */\r
- propertyToDelete.sizeOfNameString = 0;\r
-\r
- StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,\r
- indexOfPropertyToDelete,\r
- &propertyToDelete);\r
-\r
- IStorage_Release(childStorage);\r
- IEnumSTATSTG_Release(elements);\r
-\r
- return destroyHr;\r
-}\r
-\r
-/*********************************************************************\r
- *\r
- * Internal Method\r
- *\r
- * Perform the deletion of a stream node\r
- *\r
- */\r
-static HRESULT deleteStreamProperty(\r
- StorageImpl *parentStorage,\r
- ULONG indexOfPropertyToDelete,\r
- StgProperty propertyToDelete)\r
-{\r
- IStream *pis;\r
- HRESULT hr;\r
- ULARGE_INTEGER size;\r
-\r
- size.u.HighPart = 0;\r
- size.u.LowPart = 0;\r
-\r
- hr = StorageBaseImpl_OpenStream(\r
- (IStorage*)parentStorage,\r
- (OLECHAR*)propertyToDelete.name,\r
- NULL,\r
- STGM_WRITE | STGM_SHARE_EXCLUSIVE,\r
- 0,\r
- &pis);\r
-\r
- if (hr!=S_OK)\r
- {\r
- return(hr);\r
- }\r
-\r
- /*\r
- * Zap the stream\r
- */\r
- hr = IStream_SetSize(pis, size);\r
-\r
- if(hr != S_OK)\r
- {\r
- return hr;\r
- }\r
-\r
- /*\r
- * Release the stream object.\r
- */\r
- IStream_Release(pis);\r
-\r
- /*\r
- * Invalidate the property by zeroing it's name member.\r
- */\r
- propertyToDelete.sizeOfNameString = 0;\r
-\r
- /*\r
- * Here we should re-read the property so we get the updated pointer\r
- * but since we are here to zap it, I don't do it...\r
- */\r
- StorageImpl_WriteProperty(\r
- parentStorage->base.ancestorStorage,\r
- indexOfPropertyToDelete,\r
- &propertyToDelete);\r
-\r
- return S_OK;\r
-}\r
-\r
-/*********************************************************************\r
- *\r
- * Internal Method\r
- *\r
- * Finds a placeholder for the StgProperty within the Storage\r
- *\r
- */\r
-static HRESULT findPlaceholder(\r
- StorageImpl *storage,\r
- ULONG propertyIndexToStore,\r
- ULONG storePropertyIndex,\r
- INT typeOfRelation)\r
-{\r
- StgProperty storeProperty;\r
- HRESULT hr = S_OK;\r
- BOOL res = TRUE;\r
-\r
- /*\r
- * Read the storage property\r
- */\r
- res = StorageImpl_ReadProperty(\r
- storage->base.ancestorStorage,\r
- storePropertyIndex,\r
- &storeProperty);\r
-\r
- if(! res)\r
- {\r
- return E_FAIL;\r
- }\r
-\r
- if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)\r
- {\r
- if (storeProperty.previousProperty != PROPERTY_NULL)\r
- {\r
- return findPlaceholder(\r
- storage,\r
- propertyIndexToStore,\r
- storeProperty.previousProperty,\r
- typeOfRelation);\r
- }\r
- else\r
- {\r
- storeProperty.previousProperty = propertyIndexToStore;\r
- }\r
- }\r
- else if (typeOfRelation == PROPERTY_RELATION_NEXT)\r
- {\r
- if (storeProperty.nextProperty != PROPERTY_NULL)\r
- {\r
- return findPlaceholder(\r
- storage,\r
- propertyIndexToStore,\r
- storeProperty.nextProperty,\r
- typeOfRelation);\r
- }\r
- else\r
- {\r
- storeProperty.nextProperty = propertyIndexToStore;\r
- }\r
- }\r
- else if (typeOfRelation == PROPERTY_RELATION_DIR)\r
- {\r
- if (storeProperty.dirProperty != PROPERTY_NULL)\r
- {\r
- return findPlaceholder(\r
- storage,\r
- propertyIndexToStore,\r
- storeProperty.dirProperty,\r
- typeOfRelation);\r
- }\r
- else\r
- {\r
- storeProperty.dirProperty = propertyIndexToStore;\r
- }\r
- }\r
-\r
- hr = StorageImpl_WriteProperty(\r
- storage->base.ancestorStorage,\r
- storePropertyIndex,\r
- &storeProperty);\r
-\r
- if(! hr)\r
- {\r
- return E_FAIL;\r
- }\r
-\r
- return S_OK;\r
-}\r
-\r
-/*************************************************************************\r
- *\r
- * Internal Method\r
- *\r
- * This method takes the previous and the next property link of a property\r
- * to be deleted and find them a place in the Storage.\r
- */\r
-static HRESULT adjustPropertyChain(\r
- StorageImpl *This,\r
- StgProperty propertyToDelete,\r
- StgProperty parentProperty,\r
- ULONG parentPropertyId,\r
- INT typeOfRelation)\r
-{\r
- ULONG newLinkProperty = PROPERTY_NULL;\r
- BOOL needToFindAPlaceholder = FALSE;\r
- ULONG storeNode = PROPERTY_NULL;\r
- ULONG toStoreNode = PROPERTY_NULL;\r
- INT relationType = 0;\r
- HRESULT hr = S_OK;\r
- BOOL res = TRUE;\r
-\r
- if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)\r
- {\r
- if (propertyToDelete.previousProperty != PROPERTY_NULL)\r
- {\r
- /*\r
- * Set the parent previous to the property to delete previous\r
- */\r
- newLinkProperty = propertyToDelete.previousProperty;\r
-\r
- if (propertyToDelete.nextProperty != PROPERTY_NULL)\r
- {\r
- /*\r
- * We also need to find a storage for the other link, setup variables\r
- * to do this at the end...\r
- */\r
- needToFindAPlaceholder = TRUE;\r
- storeNode = propertyToDelete.previousProperty;\r
- toStoreNode = propertyToDelete.nextProperty;\r
- relationType = PROPERTY_RELATION_NEXT;\r
- }\r
- }\r
- else if (propertyToDelete.nextProperty != PROPERTY_NULL)\r
- {\r
- /*\r
- * Set the parent previous to the property to delete next\r
- */\r
- newLinkProperty = propertyToDelete.nextProperty;\r
- }\r
-\r
- /*\r
- * Link it for real...\r
- */\r
- parentProperty.previousProperty = newLinkProperty;\r
-\r
- }\r
- else if (typeOfRelation == PROPERTY_RELATION_NEXT)\r
- {\r
- if (propertyToDelete.previousProperty != PROPERTY_NULL)\r
- {\r
- /*\r
- * Set the parent next to the property to delete next previous\r
- */\r
- newLinkProperty = propertyToDelete.previousProperty;\r
-\r
- if (propertyToDelete.nextProperty != PROPERTY_NULL)\r
- {\r
- /*\r
- * We also need to find a storage for the other link, setup variables\r
- * to do this at the end...\r
- */\r
- needToFindAPlaceholder = TRUE;\r
- storeNode = propertyToDelete.previousProperty;\r
- toStoreNode = propertyToDelete.nextProperty;\r
- relationType = PROPERTY_RELATION_NEXT;\r
- }\r
- }\r
- else if (propertyToDelete.nextProperty != PROPERTY_NULL)\r
- {\r
- /*\r
- * Set the parent next to the property to delete next\r
- */\r
- newLinkProperty = propertyToDelete.nextProperty;\r
- }\r
-\r
- /*\r
- * Link it for real...\r
- */\r
- parentProperty.nextProperty = newLinkProperty;\r
- }\r
- else /* (typeOfRelation == PROPERTY_RELATION_DIR) */\r
- {\r
- if (propertyToDelete.previousProperty != PROPERTY_NULL)\r
- {\r
- /*\r
- * Set the parent dir to the property to delete previous\r
- */\r
- newLinkProperty = propertyToDelete.previousProperty;\r
-\r
- if (propertyToDelete.nextProperty != PROPERTY_NULL)\r
- {\r
- /*\r
- * We also need to find a storage for the other link, setup variables\r
- * to do this at the end...\r
- */\r
- needToFindAPlaceholder = TRUE;\r
- storeNode = propertyToDelete.previousProperty;\r
- toStoreNode = propertyToDelete.nextProperty;\r
- relationType = PROPERTY_RELATION_NEXT;\r
- }\r
- }\r
- else if (propertyToDelete.nextProperty != PROPERTY_NULL)\r
- {\r
- /*\r
- * Set the parent dir to the property to delete next\r
- */\r
- newLinkProperty = propertyToDelete.nextProperty;\r
- }\r
-\r
- /*\r
- * Link it for real...\r
- */\r
- parentProperty.dirProperty = newLinkProperty;\r
- }\r
-\r
- /*\r
- * Write back the parent property\r
- */\r
- res = StorageImpl_WriteProperty(\r
- This->base.ancestorStorage,\r
- parentPropertyId,\r
- &parentProperty);\r
- if(! res)\r
- {\r
- return E_FAIL;\r
- }\r
-\r
- /*\r
- * If a placeholder is required for the other link, then, find one and\r
- * get out of here...\r
- */\r
- if (needToFindAPlaceholder)\r
- {\r
- hr = findPlaceholder(\r
- This,\r
- toStoreNode,\r
- storeNode,\r
- relationType);\r
- }\r
-\r
- return hr;\r
-}\r
-\r
-\r
-/******************************************************************************\r
- * SetElementTimes (IStorage)\r
- */\r
-HRESULT WINAPI StorageImpl_SetElementTimes(\r
- IStorage* iface,\r
- const OLECHAR *pwcsName,/* [string][in] */\r
- const FILETIME *pctime, /* [in] */\r
- const FILETIME *patime, /* [in] */\r
- const FILETIME *pmtime) /* [in] */\r
-{\r
- FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));\r
- return S_OK;\r
-}\r
-\r
-/******************************************************************************\r
- * SetStateBits (IStorage)\r
- */\r
-HRESULT WINAPI StorageImpl_SetStateBits(\r
- IStorage* iface,\r
- DWORD grfStateBits,/* [in] */\r
- DWORD grfMask) /* [in] */\r
-{\r
- FIXME("not implemented!\n");\r
- return E_NOTIMPL;\r
-}\r
-\r
-/*\r
- * Virtual function table for the IStorage32Impl class.\r
- */\r
-static IStorageVtbl Storage32Impl_Vtbl =\r
-{\r
- StorageBaseImpl_QueryInterface,\r
- StorageBaseImpl_AddRef,\r
- StorageBaseImpl_Release,\r
- StorageBaseImpl_CreateStream,\r
- StorageBaseImpl_OpenStream,\r
- StorageImpl_CreateStorage,\r
- StorageBaseImpl_OpenStorage,\r
- StorageImpl_CopyTo,\r
- StorageImpl_MoveElementTo,\r
- StorageImpl_Commit,\r
- StorageImpl_Revert,\r
- StorageBaseImpl_EnumElements,\r
- StorageImpl_DestroyElement,\r
- StorageBaseImpl_RenameElement,\r
- StorageImpl_SetElementTimes,\r
- StorageBaseImpl_SetClass,\r
- StorageImpl_SetStateBits,\r
- StorageImpl_Stat\r
-};\r
-\r
-HRESULT StorageImpl_Construct(\r
- StorageImpl* This,\r
- HANDLE hFile,\r
- LPCOLESTR pwcsName,\r
- ILockBytes* pLkbyt,\r
- DWORD openFlags,\r
- BOOL fileBased,\r
- BOOL fileCreate)\r
-{\r
- HRESULT hr = S_OK;\r
- StgProperty currentProperty;\r
- BOOL readSuccessful;\r
- ULONG currentPropertyIndex;\r
-\r
- if ( FAILED( validateSTGM(openFlags) ))\r
- return STG_E_INVALIDFLAG;\r
-\r
- memset(This, 0, sizeof(StorageImpl));\r
-\r
- /*\r
- * Initialize the virtual function table.\r
- */\r
- This->base.lpVtbl = &Storage32Impl_Vtbl;\r
- This->base.pssVtbl = &IPropertySetStorage_Vtbl;\r
- This->base.v_destructor = &StorageImpl_Destroy;\r
- This->base.openFlags = openFlags;\r
-\r
- /*\r
- * This is the top-level storage so initialize the ancestor pointer\r
- * to this.\r
- */\r
- This->base.ancestorStorage = This;\r
-\r
- /*\r
- * Initialize the physical support of the storage.\r
- */\r
- This->hFile = hFile;\r
-\r
- /*\r
- * Store copy of file path.\r
- */\r
- if(pwcsName) {\r
- This->pwcsName = HeapAlloc(GetProcessHeap(), 0,\r
- (lstrlenW(pwcsName)+1)*sizeof(WCHAR));\r
- if (!This->pwcsName)\r
- return STG_E_INSUFFICIENTMEMORY;\r
- strcpyW(This->pwcsName, pwcsName);\r
- }\r
-\r
- /*\r
- * Initialize the big block cache.\r
- */\r
- This->bigBlockSize = DEF_BIG_BLOCK_SIZE;\r
- This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;\r
- This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,\r
- pLkbyt,\r
- openFlags,\r
- This->bigBlockSize,\r
- fileBased);\r
-\r
- if (This->bigBlockFile == 0)\r
- return E_FAIL;\r
-\r
- if (fileCreate)\r
- {\r
- ULARGE_INTEGER size;\r
- BYTE* bigBlockBuffer;\r
-\r
- /*\r
- * Initialize all header variables:\r
- * - The big block depot consists of one block and it is at block 0\r
- * - The properties start at block 1\r
- * - There is no small block depot\r
- */\r
- memset( This->bigBlockDepotStart,\r
- BLOCK_UNUSED,\r
- sizeof(This->bigBlockDepotStart));\r
-\r
- This->bigBlockDepotCount = 1;\r
- This->bigBlockDepotStart[0] = 0;\r
- This->rootStartBlock = 1;\r
- This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;\r
- This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;\r
- This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;\r
- This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;\r
- This->extBigBlockDepotCount = 0;\r
-\r
- StorageImpl_SaveFileHeader(This);\r
-\r
- /*\r
- * Add one block for the big block depot and one block for the properties\r
- */\r
- size.u.HighPart = 0;\r
- size.u.LowPart = This->bigBlockSize * 3;\r
- BIGBLOCKFILE_SetSize(This->bigBlockFile, size);\r
-\r
- /*\r
- * Initialize the big block depot\r
- */\r
- bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);\r
- memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);\r
- StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);\r
- StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);\r
- StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);\r
- }\r
- else\r
- {\r
- /*\r
- * Load the header for the file.\r
- */\r
- hr = StorageImpl_LoadFileHeader(This);\r
-\r
- if (FAILED(hr))\r
- {\r
- BIGBLOCKFILE_Destructor(This->bigBlockFile);\r
-\r
- return hr;\r
- }\r
- }\r
-\r
- /*\r
- * There is no block depot cached yet.\r
- */\r
- This->indexBlockDepotCached = 0xFFFFFFFF;\r
-\r
- /*\r
- * Start searching for free blocks with block 0.\r
- */\r
- This->prevFreeBlock = 0;\r
-\r
- /*\r
- * Create the block chain abstractions.\r
- */\r
- if(!(This->rootBlockChain =\r
- BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))\r
- return STG_E_READFAULT;\r
-\r
- if(!(This->smallBlockDepotChain =\r
- BlockChainStream_Construct(This, &This->smallBlockDepotStart,\r
- PROPERTY_NULL)))\r
- return STG_E_READFAULT;\r
-\r
- /*\r
- * Write the root property\r
- */\r
- if (fileCreate)\r
- {\r
- StgProperty rootProp;\r
- /*\r
- * Initialize the property chain\r
- */\r
- memset(&rootProp, 0, sizeof(rootProp));\r
- MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,\r
- sizeof(rootProp.name)/sizeof(WCHAR) );\r
- rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);\r
- rootProp.propertyType = PROPTYPE_ROOT;\r
- rootProp.previousProperty = PROPERTY_NULL;\r
- rootProp.nextProperty = PROPERTY_NULL;\r
- rootProp.dirProperty = PROPERTY_NULL;\r
- rootProp.startingBlock = BLOCK_END_OF_CHAIN;\r
- rootProp.size.u.HighPart = 0;\r
- rootProp.size.u.LowPart = 0;\r
-\r
- StorageImpl_WriteProperty(This, 0, &rootProp);\r
- }\r
-\r
- /*\r
- * Find the ID of the root in the property sets.\r
- */\r
- currentPropertyIndex = 0;\r
-\r
- do\r
- {\r
- readSuccessful = StorageImpl_ReadProperty(\r
- This,\r
- currentPropertyIndex,\r
- ¤tProperty);\r
-\r
- if (readSuccessful)\r
- {\r
- if ( (currentProperty.sizeOfNameString != 0 ) &&\r
- (currentProperty.propertyType == PROPTYPE_ROOT) )\r
- {\r
- This->base.rootPropertySetIndex = currentPropertyIndex;\r
- }\r
- }\r
-\r
- currentPropertyIndex++;\r
-\r
- } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );\r
-\r
- if (!readSuccessful)\r
- {\r
- /* TODO CLEANUP */\r
- return STG_E_READFAULT;\r
- }\r
-\r
- /*\r
- * Create the block chain abstraction for the small block root chain.\r
- */\r
- if(!(This->smallBlockRootChain =\r
- BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))\r
- return STG_E_READFAULT;\r
-\r
- return hr;\r
-}\r
-\r
-void StorageImpl_Destroy(StorageBaseImpl* iface)\r
-{\r
- StorageImpl *This = (StorageImpl*) iface;\r
- TRACE("(%p)\n", This);\r
-\r
- HeapFree(GetProcessHeap(), 0, This->pwcsName);\r
-\r
- BlockChainStream_Destroy(This->smallBlockRootChain);\r
- BlockChainStream_Destroy(This->rootBlockChain);\r
- BlockChainStream_Destroy(This->smallBlockDepotChain);\r
-\r
- BIGBLOCKFILE_Destructor(This->bigBlockFile);\r
- HeapFree(GetProcessHeap(), 0, This);\r
-}\r
-\r
-/******************************************************************************\r
- * Storage32Impl_GetNextFreeBigBlock\r
- *\r
- * Returns the index of the next free big block.\r
- * If the big block depot is filled, this method will enlarge it.\r
- *\r
- */\r
-ULONG StorageImpl_GetNextFreeBigBlock(\r
- StorageImpl* This)\r
-{\r
- ULONG depotBlockIndexPos;\r
- void *depotBuffer;\r
- ULONG depotBlockOffset;\r
- ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);\r
- ULONG nextBlockIndex = BLOCK_SPECIAL;\r
- int depotIndex = 0;\r
- ULONG freeBlock = BLOCK_UNUSED;\r
-\r
- depotIndex = This->prevFreeBlock / blocksPerDepot;\r
- depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);\r
-\r
- /*\r
- * Scan the entire big block depot until we find a block marked free\r
- */\r
- while (nextBlockIndex != BLOCK_UNUSED)\r
- {\r
- if (depotIndex < COUNT_BBDEPOTINHEADER)\r
- {\r
- depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];\r
-\r
- /*\r
- * Grow the primary depot.\r
- */\r
- if (depotBlockIndexPos == BLOCK_UNUSED)\r
- {\r
- depotBlockIndexPos = depotIndex*blocksPerDepot;\r
-\r
- /*\r
- * Add a block depot.\r
- */\r
- Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);\r
- This->bigBlockDepotCount++;\r
- This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;\r
-\r
- /*\r
- * Flag it as a block depot.\r
- */\r
- StorageImpl_SetNextBlockInChain(This,\r
- depotBlockIndexPos,\r
- BLOCK_SPECIAL);\r
-\r
- /* Save new header information.\r
- */\r
- StorageImpl_SaveFileHeader(This);\r
- }\r
- }\r
- else\r
- {\r
- depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);\r
-\r
- if (depotBlockIndexPos == BLOCK_UNUSED)\r
- {\r
- /*\r
- * Grow the extended depot.\r
- */\r
- ULONG extIndex = BLOCK_UNUSED;\r
- ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;\r
- ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);\r
-\r
- if (extBlockOffset == 0)\r
- {\r
- /* We need an extended block.\r
- */\r
- extIndex = Storage32Impl_AddExtBlockDepot(This);\r
- This->extBigBlockDepotCount++;\r
- depotBlockIndexPos = extIndex + 1;\r
- }\r
- else\r
- depotBlockIndexPos = depotIndex * blocksPerDepot;\r
-\r
- /*\r
- * Add a block depot and mark it in the extended block.\r
- */\r
- Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);\r
- This->bigBlockDepotCount++;\r
- Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);\r
-\r
- /* Flag the block depot.\r
- */\r
- StorageImpl_SetNextBlockInChain(This,\r
- depotBlockIndexPos,\r
- BLOCK_SPECIAL);\r
-\r
- /* If necessary, flag the extended depot block.\r
- */\r
- if (extIndex != BLOCK_UNUSED)\r
- StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);\r
-\r
- /* Save header information.\r
- */\r
- StorageImpl_SaveFileHeader(This);\r
- }\r
- }\r
-\r
- depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);\r
-\r
- if (depotBuffer != 0)\r
- {\r
- while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&\r
- ( nextBlockIndex != BLOCK_UNUSED))\r
- {\r
- StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);\r
-\r
- if (nextBlockIndex == BLOCK_UNUSED)\r
- {\r
- freeBlock = (depotIndex * blocksPerDepot) +\r
- (depotBlockOffset/sizeof(ULONG));\r
- }\r
-\r
- depotBlockOffset += sizeof(ULONG);\r
- }\r
-\r
- StorageImpl_ReleaseBigBlock(This, depotBuffer);\r
- }\r
-\r
- depotIndex++;\r
- depotBlockOffset = 0;\r
- }\r
-\r
- This->prevFreeBlock = freeBlock;\r
-\r
- return freeBlock;\r
-}\r
-\r
-/******************************************************************************\r
- * Storage32Impl_AddBlockDepot\r
- *\r
- * This will create a depot block, essentially it is a block initialized\r
- * to BLOCK_UNUSEDs.\r
- */\r
-void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)\r
-{\r
- BYTE* blockBuffer;\r
-\r
- blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);\r
-\r
- /*\r
- * Initialize blocks as free\r
- */\r
- memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);\r
-\r
- StorageImpl_ReleaseBigBlock(This, blockBuffer);\r
-}\r
-\r
-/******************************************************************************\r
- * Storage32Impl_GetExtDepotBlock\r
- *\r
- * Returns the index of the block that corresponds to the specified depot\r
- * index. This method is only for depot indexes equal or greater than\r
- * COUNT_BBDEPOTINHEADER.\r
- */\r
-ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)\r
-{\r
- ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;\r
- ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;\r
- ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;\r
- ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;\r
- ULONG blockIndex = BLOCK_UNUSED;\r
- ULONG extBlockIndex = This->extBigBlockDepotStart;\r
-\r
- assert(depotIndex >= COUNT_BBDEPOTINHEADER);\r
-\r
- if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)\r
- return BLOCK_UNUSED;\r
-\r
- while (extBlockCount > 0)\r
- {\r
- extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);\r
- extBlockCount--;\r
- }\r
-\r
- if (extBlockIndex != BLOCK_UNUSED)\r
- {\r
- BYTE* depotBuffer;\r
-\r
- depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);\r
-\r
- if (depotBuffer != 0)\r
- {\r
- StorageUtl_ReadDWord(depotBuffer,\r
- extBlockOffset * sizeof(ULONG),\r
- &blockIndex);\r
-\r
- StorageImpl_ReleaseBigBlock(This, depotBuffer);\r
- }\r
- }\r
-\r
- return blockIndex;\r
-}\r
-\r
-/******************************************************************************\r
- * Storage32Impl_SetExtDepotBlock\r
- *\r
- * Associates the specified block index to the specified depot index.\r
- * This method is only for depot indexes equal or greater than\r
- * COUNT_BBDEPOTINHEADER.\r
- */\r
-void Storage32Impl_SetExtDepotBlock(StorageImpl* This,\r
- ULONG depotIndex,\r
- ULONG blockIndex)\r
-{\r
- ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;\r
- ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;\r
- ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;\r
- ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;\r
- ULONG extBlockIndex = This->extBigBlockDepotStart;\r
-\r
- assert(depotIndex >= COUNT_BBDEPOTINHEADER);\r
-\r
- while (extBlockCount > 0)\r
- {\r
- extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);\r
- extBlockCount--;\r
- }\r
-\r
- if (extBlockIndex != BLOCK_UNUSED)\r
- {\r
- BYTE* depotBuffer;\r
-\r
- depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);\r
-\r
- if (depotBuffer != 0)\r
- {\r
- StorageUtl_WriteDWord(depotBuffer,\r
- extBlockOffset * sizeof(ULONG),\r
- blockIndex);\r
-\r
- StorageImpl_ReleaseBigBlock(This, depotBuffer);\r
- }\r
- }\r
-}\r
-\r
-/******************************************************************************\r
- * Storage32Impl_AddExtBlockDepot\r
- *\r
- * Creates an extended depot block.\r
- */\r
-ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)\r
-{\r
- ULONG numExtBlocks = This->extBigBlockDepotCount;\r
- ULONG nextExtBlock = This->extBigBlockDepotStart;\r
- BYTE* depotBuffer = NULL;\r
- ULONG index = BLOCK_UNUSED;\r
- ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);\r
- ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);\r
- ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;\r
-\r
- index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *\r
- blocksPerDepotBlock;\r
-\r
- if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))\r
- {\r
- /*\r
- * The first extended block.\r
- */\r
- This->extBigBlockDepotStart = index;\r
- }\r
- else\r
- {\r
- unsigned int i;\r
- /*\r
- * Follow the chain to the last one.\r
- */\r
- for (i = 0; i < (numExtBlocks - 1); i++)\r
- {\r
- nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);\r
- }\r
-\r
- /*\r
- * Add the new extended block to the chain.\r
- */\r
- depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);\r
- StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);\r
- StorageImpl_ReleaseBigBlock(This, depotBuffer);\r
- }\r
-\r
- /*\r
- * Initialize this block.\r
- */\r
- depotBuffer = StorageImpl_GetBigBlock(This, index);\r
- memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);\r
- StorageImpl_ReleaseBigBlock(This, depotBuffer);\r
-\r
- return index;\r
-}\r
-\r
-/******************************************************************************\r
- * Storage32Impl_FreeBigBlock\r
- *\r
- * This method will flag the specified block as free in the big block depot.\r
- */\r
-void StorageImpl_FreeBigBlock(\r
- StorageImpl* This,\r
- ULONG blockIndex)\r
-{\r
- StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);\r
-\r
- if (blockIndex < This->prevFreeBlock)\r
- This->prevFreeBlock = blockIndex;\r
-}\r
-\r
-/************************************************************************\r
- * Storage32Impl_GetNextBlockInChain\r
- *\r
- * This method will retrieve the block index of the next big block in\r
- * in the chain.\r
- *\r
- * Params: This - Pointer to the Storage object.\r
- * blockIndex - Index of the block to retrieve the chain\r
- * for.\r
- * nextBlockIndex - receives the return value.\r
- *\r
- * Returns: This method returns the index of the next block in the chain.\r
- * It will return the constants:\r
- * BLOCK_SPECIAL - If the block given was not part of a\r
- * chain.\r
- * BLOCK_END_OF_CHAIN - If the block given was the last in\r
- * a chain.\r
- * BLOCK_UNUSED - If the block given was not past of a chain\r
- * and is available.\r
- * BLOCK_EXTBBDEPOT - This block is part of the extended\r
- * big block depot.\r
- *\r
- * See Windows documentation for more details on IStorage methods.\r
- */\r
-HRESULT StorageImpl_GetNextBlockInChain(\r
- StorageImpl* This,\r
- ULONG blockIndex,\r
- ULONG* nextBlockIndex)\r
-{\r
- ULONG offsetInDepot = blockIndex * sizeof (ULONG);\r
- ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;\r
- ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;\r
- void* depotBuffer;\r
- ULONG depotBlockIndexPos;\r
- int index;\r
-\r
- *nextBlockIndex = BLOCK_SPECIAL;\r
-\r
- if(depotBlockCount >= This->bigBlockDepotCount)\r
- {\r
- WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount,\r
- This->bigBlockDepotCount);\r
- return STG_E_READFAULT;\r
- }\r
-\r
- /*\r
- * Cache the currently accessed depot block.\r
- */\r
- if (depotBlockCount != This->indexBlockDepotCached)\r
- {\r
- This->indexBlockDepotCached = depotBlockCount;\r
-\r
- if (depotBlockCount < COUNT_BBDEPOTINHEADER)\r
- {\r
- depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];\r
- }\r
- else\r
- {\r
- /*\r
- * We have to look in the extended depot.\r
- */\r
- depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);\r
- }\r
-\r
- depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);\r
-\r
- if (!depotBuffer)\r
- return STG_E_READFAULT;\r
-\r
- for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)\r
- {\r
- StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);\r
- This->blockDepotCached[index] = *nextBlockIndex;\r
- }\r
- StorageImpl_ReleaseBigBlock(This, depotBuffer);\r
- }\r
-\r
- *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];\r
-\r
- return S_OK;\r
-}\r
-\r
-/******************************************************************************\r
- * Storage32Impl_GetNextExtendedBlock\r
- *\r
- * Given an extended block this method will return the next extended block.\r
- *\r
- * NOTES:\r
- * The last ULONG of an extended block is the block index of the next\r
- * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the\r
- * depot.\r
- *\r
- * Return values:\r
- * - The index of the next extended block\r
- * - BLOCK_UNUSED: there is no next extended block.\r
- * - Any other return values denotes failure.\r
- */\r
-ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)\r
-{\r
- ULONG nextBlockIndex = BLOCK_SPECIAL;\r
- ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);\r
- void* depotBuffer;\r
-\r
- depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);\r
-\r
- if (depotBuffer!=0)\r
- {\r
- StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);\r
-\r
- StorageImpl_ReleaseBigBlock(This, depotBuffer);\r
- }\r
-\r
- return nextBlockIndex;\r
-}\r
-\r
-/******************************************************************************\r
- * Storage32Impl_SetNextBlockInChain\r
- *\r
- * This method will write the index of the specified block's next block\r
- * in the big block depot.\r
- *\r
- * For example: to create the chain 3 -> 1 -> 7 -> End of Chain\r
- * do the following\r
- *\r
- * Storage32Impl_SetNextBlockInChain(This, 3, 1);\r
- * Storage32Impl_SetNextBlockInChain(This, 1, 7);\r
- * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);\r
- *\r
- */\r
-void StorageImpl_SetNextBlockInChain(\r
- StorageImpl* This,\r
- ULONG blockIndex,\r
- ULONG nextBlock)\r
-{\r
- ULONG offsetInDepot = blockIndex * sizeof (ULONG);\r
- ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;\r
- ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;\r
- ULONG depotBlockIndexPos;\r
- void* depotBuffer;\r
-\r
- assert(depotBlockCount < This->bigBlockDepotCount);\r
- assert(blockIndex != nextBlock);\r
-\r
- if (depotBlockCount < COUNT_BBDEPOTINHEADER)\r
- {\r
- depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];\r
- }\r
- else\r
- {\r
- /*\r
- * We have to look in the extended depot.\r
- */\r
- depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);\r
- }\r
-\r
- depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);\r
-\r
- if (depotBuffer!=0)\r
- {\r
- StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);\r
- StorageImpl_ReleaseBigBlock(This, depotBuffer);\r
- }\r
-\r
- /*\r
- * Update the cached block depot, if necessary.\r
- */\r
- if (depotBlockCount == This->indexBlockDepotCached)\r
- {\r
- This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;\r
- }\r
-}\r
-\r
-/******************************************************************************\r
- * Storage32Impl_LoadFileHeader\r
- *\r
- * This method will read in the file header, i.e. big block index -1.\r
- */\r
-HRESULT StorageImpl_LoadFileHeader(\r
- StorageImpl* This)\r
-{\r
- HRESULT hr = STG_E_FILENOTFOUND;\r
- void* headerBigBlock = NULL;\r
- int index;\r
-\r
- /*\r
- * Get a pointer to the big block of data containing the header.\r
- */\r
- headerBigBlock = StorageImpl_GetROBigBlock(This, -1);\r
-\r
- /*\r
- * Extract the information from the header.\r
- */\r
- if (headerBigBlock!=0)\r
- {\r
- /*\r
- * Check for the "magic number" signature and return an error if it is not\r
- * found.\r
- */\r
- if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)\r
- {\r
- StorageImpl_ReleaseBigBlock(This, headerBigBlock);\r
- return STG_E_OLDFORMAT;\r
- }\r
-\r
- if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)\r
- {\r
- StorageImpl_ReleaseBigBlock(This, headerBigBlock);\r
- return STG_E_INVALIDHEADER;\r
- }\r
-\r
- StorageUtl_ReadWord(\r
- headerBigBlock,\r
- OFFSET_BIGBLOCKSIZEBITS,\r
- &This->bigBlockSizeBits);\r
-\r
- StorageUtl_ReadWord(\r
- headerBigBlock,\r
- OFFSET_SMALLBLOCKSIZEBITS,\r
- &This->smallBlockSizeBits);\r
-\r
- StorageUtl_ReadDWord(\r
- headerBigBlock,\r
- OFFSET_BBDEPOTCOUNT,\r
- &This->bigBlockDepotCount);\r
-\r
- StorageUtl_ReadDWord(\r
- headerBigBlock,\r
- OFFSET_ROOTSTARTBLOCK,\r
- &This->rootStartBlock);\r
-\r
- StorageUtl_ReadDWord(\r
- headerBigBlock,\r
- OFFSET_SBDEPOTSTART,\r
- &This->smallBlockDepotStart);\r
-\r
- StorageUtl_ReadDWord(\r
- headerBigBlock,\r
- OFFSET_EXTBBDEPOTSTART,\r
- &This->extBigBlockDepotStart);\r
-\r
- StorageUtl_ReadDWord(\r
- headerBigBlock,\r
- OFFSET_EXTBBDEPOTCOUNT,\r
- &This->extBigBlockDepotCount);\r
-\r
- for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)\r
- {\r
- StorageUtl_ReadDWord(\r
- headerBigBlock,\r
- OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),\r
- &(This->bigBlockDepotStart[index]));\r
- }\r
-\r
- /*\r
- * Make the bitwise arithmetic to get the size of the blocks in bytes.\r
- */\r
- if ((1 << 2) == 4)\r
- {\r
- This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;\r
- This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;\r
- }\r
- else\r
- {\r
- This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;\r
- This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;\r
- }\r
-\r
- /*\r
- * Right now, the code is making some assumptions about the size of the\r
- * blocks, just make sure they are what we're expecting.\r
- */\r
- if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||\r
- This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)\r
- {\r
- WARN("Broken OLE storage file\n");\r
- hr = STG_E_INVALIDHEADER;\r
- }\r
- else\r
- hr = S_OK;\r
-\r
- /*\r
- * Release the block.\r
- */\r
- StorageImpl_ReleaseBigBlock(This, headerBigBlock);\r
- }\r
-\r
- return hr;\r
-}\r
-\r
-/******************************************************************************\r
- * Storage32Impl_SaveFileHeader\r
- *\r
- * This method will save to the file the header, i.e. big block -1.\r
- */\r
-void StorageImpl_SaveFileHeader(\r
- StorageImpl* This)\r
-{\r
- BYTE headerBigBlock[BIG_BLOCK_SIZE];\r
- int index;\r
- BOOL success;\r
-\r
- /*\r
- * Get a pointer to the big block of data containing the header.\r
- */\r
- success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);\r
-\r
- /*\r
- * If the block read failed, the file is probably new.\r
- */\r
- if (!success)\r
- {\r
- /*\r
- * Initialize for all unknown fields.\r
- */\r
- memset(headerBigBlock, 0, BIG_BLOCK_SIZE);\r
-\r
- /*\r
- * Initialize the magic number.\r
- */\r
- memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));\r
-\r
- /*\r
- * And a bunch of things we don't know what they mean\r
- */\r
- StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);\r
- StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);\r
- StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);\r
- StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);\r
- }\r
-\r
- /*\r
- * Write the information to the header.\r
- */\r
- StorageUtl_WriteWord(\r
- headerBigBlock,\r
- OFFSET_BIGBLOCKSIZEBITS,\r
- This->bigBlockSizeBits);\r
-\r
- StorageUtl_WriteWord(\r
- headerBigBlock,\r
- OFFSET_SMALLBLOCKSIZEBITS,\r
- This->smallBlockSizeBits);\r
-\r
- StorageUtl_WriteDWord(\r
- headerBigBlock,\r
- OFFSET_BBDEPOTCOUNT,\r
- This->bigBlockDepotCount);\r
-\r
- StorageUtl_WriteDWord(\r
- headerBigBlock,\r
- OFFSET_ROOTSTARTBLOCK,\r
- This->rootStartBlock);\r
-\r
- StorageUtl_WriteDWord(\r
- headerBigBlock,\r
- OFFSET_SBDEPOTSTART,\r
- This->smallBlockDepotStart);\r
-\r
- StorageUtl_WriteDWord(\r
- headerBigBlock,\r
- OFFSET_SBDEPOTCOUNT,\r
- This->smallBlockDepotChain ?\r
- BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);\r
-\r
- StorageUtl_WriteDWord(\r
- headerBigBlock,\r
- OFFSET_EXTBBDEPOTSTART,\r
- This->extBigBlockDepotStart);\r
-\r
- StorageUtl_WriteDWord(\r
- headerBigBlock,\r
- OFFSET_EXTBBDEPOTCOUNT,\r
- This->extBigBlockDepotCount);\r
-\r
- for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)\r
- {\r
- StorageUtl_WriteDWord(\r
- headerBigBlock,\r
- OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),\r
- (This->bigBlockDepotStart[index]));\r
- }\r
-\r
- /*\r
- * Write the big block back to the file.\r
- */\r
- StorageImpl_WriteBigBlock(This, -1, headerBigBlock);\r
-}\r
-\r
-/******************************************************************************\r
- * Storage32Impl_ReadProperty\r
- *\r
- * This method will read the specified property from the property chain.\r
- */\r
-BOOL StorageImpl_ReadProperty(\r
- StorageImpl* This,\r
- ULONG index,\r
- StgProperty* buffer)\r
-{\r
- BYTE currentProperty[PROPSET_BLOCK_SIZE];\r
- ULARGE_INTEGER offsetInPropSet;\r
- BOOL readSuccessful;\r
- ULONG bytesRead;\r
-\r
- offsetInPropSet.u.HighPart = 0;\r
- offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;\r
-\r
- readSuccessful = BlockChainStream_ReadAt(\r
- This->rootBlockChain,\r
- offsetInPropSet,\r
- PROPSET_BLOCK_SIZE,\r
- currentProperty,\r
- &bytesRead);\r
-\r
- if (readSuccessful)\r
- {\r
- /* replace the name of root entry (often "Root Entry") by the file name */\r
- WCHAR *propName = (index == This->base.rootPropertySetIndex) ?\r
- This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;\r
-\r
- memset(buffer->name, 0, sizeof(buffer->name));\r
- memcpy(\r
- buffer->name,\r
- propName,\r
- PROPERTY_NAME_BUFFER_LEN );\r
- TRACE("storage name: %s\n", debugstr_w(buffer->name));\r
-\r
- memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);\r
-\r
- StorageUtl_ReadWord(\r
- currentProperty,\r
- OFFSET_PS_NAMELENGTH,\r
- &buffer->sizeOfNameString);\r
-\r
- StorageUtl_ReadDWord(\r
- currentProperty,\r
- OFFSET_PS_PREVIOUSPROP,\r
- &buffer->previousProperty);\r
-\r
- StorageUtl_ReadDWord(\r
- currentProperty,\r
- OFFSET_PS_NEXTPROP,\r
- &buffer->nextProperty);\r
-\r
- StorageUtl_ReadDWord(\r
- currentProperty,\r
- OFFSET_PS_DIRPROP,\r
- &buffer->dirProperty);\r
-\r
- StorageUtl_ReadGUID(\r
- currentProperty,\r
- OFFSET_PS_GUID,\r
- &buffer->propertyUniqueID);\r
-\r
- StorageUtl_ReadDWord(\r
- currentProperty,\r
- OFFSET_PS_TSS1,\r
- &buffer->timeStampS1);\r
-\r
- StorageUtl_ReadDWord(\r
- currentProperty,\r
- OFFSET_PS_TSD1,\r
- &buffer->timeStampD1);\r
-\r
- StorageUtl_ReadDWord(\r
- currentProperty,\r
- OFFSET_PS_TSS2,\r
- &buffer->timeStampS2);\r
-\r
- StorageUtl_ReadDWord(\r
- currentProperty,\r
- OFFSET_PS_TSD2,\r
- &buffer->timeStampD2);\r
-\r
- StorageUtl_ReadDWord(\r
- currentProperty,\r
- OFFSET_PS_STARTBLOCK,\r
- &buffer->startingBlock);\r
-\r
- StorageUtl_ReadDWord(\r
- currentProperty,\r
- OFFSET_PS_SIZE,\r
- &buffer->size.u.LowPart);\r
-\r
- buffer->size.u.HighPart = 0;\r
- }\r
-\r
- return readSuccessful;\r
-}\r
-\r
-/*********************************************************************\r
- * Write the specified property into the property chain\r
- */\r
-BOOL StorageImpl_WriteProperty(\r
- StorageImpl* This,\r
- ULONG index,\r
- StgProperty* buffer)\r
-{\r
- BYTE currentProperty[PROPSET_BLOCK_SIZE];\r
- ULARGE_INTEGER offsetInPropSet;\r
- BOOL writeSuccessful;\r
- ULONG bytesWritten;\r
-\r
- offsetInPropSet.u.HighPart = 0;\r
- offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;\r
-\r
- memset(currentProperty, 0, PROPSET_BLOCK_SIZE);\r
-\r
- memcpy(\r
- currentProperty + OFFSET_PS_NAME,\r
- buffer->name,\r
- PROPERTY_NAME_BUFFER_LEN );\r
-\r
- memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);\r
-\r
- StorageUtl_WriteWord(\r
- currentProperty,\r
- OFFSET_PS_NAMELENGTH,\r
- buffer->sizeOfNameString);\r
-\r
- StorageUtl_WriteDWord(\r
- currentProperty,\r
- OFFSET_PS_PREVIOUSPROP,\r
- buffer->previousProperty);\r
-\r
- StorageUtl_WriteDWord(\r
- currentProperty,\r
- OFFSET_PS_NEXTPROP,\r
- buffer->nextProperty);\r
-\r
- StorageUtl_WriteDWord(\r
- currentProperty,\r
- OFFSET_PS_DIRPROP,\r
- buffer->dirProperty);\r
-\r
- StorageUtl_WriteGUID(\r
- currentProperty,\r
- OFFSET_PS_GUID,\r
- &buffer->propertyUniqueID);\r
-\r
- StorageUtl_WriteDWord(\r
- currentProperty,\r
- OFFSET_PS_TSS1,\r
- buffer->timeStampS1);\r
-\r
- StorageUtl_WriteDWord(\r
- currentProperty,\r
- OFFSET_PS_TSD1,\r
- buffer->timeStampD1);\r
-\r
- StorageUtl_WriteDWord(\r
- currentProperty,\r
- OFFSET_PS_TSS2,\r
- buffer->timeStampS2);\r
-\r
- StorageUtl_WriteDWord(\r
- currentProperty,\r
- OFFSET_PS_TSD2,\r
- buffer->timeStampD2);\r
-\r
- StorageUtl_WriteDWord(\r
- currentProperty,\r
- OFFSET_PS_STARTBLOCK,\r
- buffer->startingBlock);\r
-\r
- StorageUtl_WriteDWord(\r
- currentProperty,\r
- OFFSET_PS_SIZE,\r
- buffer->size.u.LowPart);\r
-\r
- writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,\r
- offsetInPropSet,\r
- PROPSET_BLOCK_SIZE,\r
- currentProperty,\r
- &bytesWritten);\r
- return writeSuccessful;\r
-}\r
-\r
-BOOL StorageImpl_ReadBigBlock(\r
- StorageImpl* This,\r
- ULONG blockIndex,\r
- void* buffer)\r
-{\r
- void* bigBlockBuffer;\r
-\r
- bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);\r
-\r
- if (bigBlockBuffer!=0)\r
- {\r
- memcpy(buffer, bigBlockBuffer, This->bigBlockSize);\r
-\r
- StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);\r
-\r
- return TRUE;\r
- }\r
-\r
- return FALSE;\r
-}\r
-\r
-BOOL StorageImpl_WriteBigBlock(\r
- StorageImpl* This,\r
- ULONG blockIndex,\r
- void* buffer)\r
-{\r
- void* bigBlockBuffer;\r
-\r
- bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);\r
-\r
- if (bigBlockBuffer!=0)\r
- {\r
- memcpy(bigBlockBuffer, buffer, This->bigBlockSize);\r
-\r
- StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);\r
-\r
- return TRUE;\r
- }\r
-\r
- return FALSE;\r
-}\r
-\r
-void* StorageImpl_GetROBigBlock(\r
- StorageImpl* This,\r
- ULONG blockIndex)\r
-{\r
- return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);\r
-}\r
-\r
-void* StorageImpl_GetBigBlock(\r
- StorageImpl* This,\r
- ULONG blockIndex)\r
-{\r
- return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);\r
-}\r
-\r
-void StorageImpl_ReleaseBigBlock(\r
- StorageImpl* This,\r
- void* pBigBlock)\r
-{\r
- BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);\r
-}\r
-\r
-/******************************************************************************\r
- * Storage32Impl_SmallBlocksToBigBlocks\r
- *\r
- * This method will convert a small block chain to a big block chain.\r
- * The small block chain will be destroyed.\r
- */\r
-BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(\r
- StorageImpl* This,\r
- SmallBlockChainStream** ppsbChain)\r
-{\r
- ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;\r
- ULARGE_INTEGER size, offset;\r
- ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;\r
- ULONG propertyIndex;\r
- BOOL successRead, successWrite;\r
- StgProperty chainProperty;\r
- BYTE *buffer;\r
- BlockChainStream *bbTempChain = NULL;\r
- BlockChainStream *bigBlockChain = NULL;\r
-\r
- /*\r
- * Create a temporary big block chain that doesn't have\r
- * an associated property. This temporary chain will be\r
- * used to copy data from small blocks to big blocks.\r
- */\r
- bbTempChain = BlockChainStream_Construct(This,\r
- &bbHeadOfChain,\r
- PROPERTY_NULL);\r
- if(!bbTempChain) return NULL;\r
- /*\r
- * Grow the big block chain.\r
- */\r
- size = SmallBlockChainStream_GetSize(*ppsbChain);\r
- BlockChainStream_SetSize(bbTempChain, size);\r
-\r
- /*\r
- * Copy the contents of the small block chain to the big block chain\r
- * by small block size increments.\r
- */\r
- offset.u.LowPart = 0;\r
- offset.u.HighPart = 0;\r
- cbTotalRead = 0;\r
- cbTotalWritten = 0;\r
-\r
- buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);\r
- do\r
- {\r
- successRead = SmallBlockChainStream_ReadAt(*ppsbChain,\r
- offset,\r
- DEF_SMALL_BLOCK_SIZE,\r
- buffer,\r
- &cbRead);\r
- cbTotalRead += cbRead;\r
-\r
- successWrite = BlockChainStream_WriteAt(bbTempChain,\r
- offset,\r
- cbRead,\r
- buffer,\r
- &cbWritten);\r
- cbTotalWritten += cbWritten;\r
-\r
- offset.u.LowPart += This->smallBlockSize;\r
-\r
- } while (successRead && successWrite);\r
- HeapFree(GetProcessHeap(),0,buffer);\r
-\r
- assert(cbTotalRead == cbTotalWritten);\r
-\r
- /*\r
- * Destroy the small block chain.\r
- */\r
- propertyIndex = (*ppsbChain)->ownerPropertyIndex;\r
- size.u.HighPart = 0;\r
- size.u.LowPart = 0;\r
- SmallBlockChainStream_SetSize(*ppsbChain, size);\r
- SmallBlockChainStream_Destroy(*ppsbChain);\r
- *ppsbChain = 0;\r
-\r
- /*\r
- * Change the property information. This chain is now a big block chain\r
- * and it doesn't reside in the small blocks chain anymore.\r
- */\r
- StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);\r
-\r
- chainProperty.startingBlock = bbHeadOfChain;\r
-\r
- StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);\r
-\r
- /*\r
- * Destroy the temporary propertyless big block chain.\r
- * Create a new big block chain associated with this property.\r
- */\r
- BlockChainStream_Destroy(bbTempChain);\r
- bigBlockChain = BlockChainStream_Construct(This,\r
- NULL,\r
- propertyIndex);\r
-\r
- return bigBlockChain;\r
-}\r
-\r
-void StorageInternalImpl_Destroy( StorageBaseImpl *iface)\r
-{\r
- StorageInternalImpl* This = (StorageInternalImpl*) iface;\r
-\r
- StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);\r
- HeapFree(GetProcessHeap(), 0, This);\r
-}\r
-\r
-/******************************************************************************\r
-**\r
-** Storage32InternalImpl_Commit\r
-**\r
-** The non-root storages cannot be opened in transacted mode thus this function\r
-** does nothing.\r
-*/\r
-HRESULT WINAPI StorageInternalImpl_Commit(\r
- IStorage* iface,\r
- DWORD grfCommitFlags) /* [in] */\r
-{\r
- return S_OK;\r
-}\r
-\r
-/******************************************************************************\r
-**\r
-** Storage32InternalImpl_Revert\r
-**\r
-** The non-root storages cannot be opened in transacted mode thus this function\r
-** does nothing.\r
-*/\r
-HRESULT WINAPI StorageInternalImpl_Revert(\r
- IStorage* iface)\r
-{\r
- return S_OK;\r
-}\r
-\r
-void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)\r
-{\r
- IStorage_Release((IStorage*)This->parentStorage);\r
- HeapFree(GetProcessHeap(), 0, This->stackToVisit);\r
- HeapFree(GetProcessHeap(), 0, This);\r
-}\r
-\r
-HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(\r
- IEnumSTATSTG* iface,\r
- REFIID riid,\r
- void** ppvObject)\r
-{\r
- IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;\r
-\r
- /*\r
- * Perform a sanity check on the parameters.\r
- */\r
- if (ppvObject==0)\r
- return E_INVALIDARG;\r
-\r
- /*\r
- * Initialize the return parameter.\r
- */\r
- *ppvObject = 0;\r
-\r
- /*\r
- * Compare the riid with the interface IDs implemented by this object.\r
- */\r
- if (IsEqualGUID(&IID_IUnknown, riid) ||\r
- IsEqualGUID(&IID_IStorage, riid))\r
- {\r
- *ppvObject = (IEnumSTATSTG*)This;\r
- IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);\r
- return S_OK;\r
- }\r
-\r
- return E_NOINTERFACE;\r
-}\r
-\r
-ULONG WINAPI IEnumSTATSTGImpl_AddRef(\r
- IEnumSTATSTG* iface)\r
-{\r
- IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;\r
- return InterlockedIncrement(&This->ref);\r
-}\r
-\r
-ULONG WINAPI IEnumSTATSTGImpl_Release(\r
- IEnumSTATSTG* iface)\r
-{\r
- IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;\r
-\r
- ULONG newRef;\r
-\r
- newRef = InterlockedDecrement(&This->ref);\r
-\r
- /*\r
- * If the reference count goes down to 0, perform suicide.\r
- */\r
- if (newRef==0)\r
- {\r
- IEnumSTATSTGImpl_Destroy(This);\r
- }\r
-\r
- return newRef;\r
-}\r
-\r
-HRESULT WINAPI IEnumSTATSTGImpl_Next(\r
- IEnumSTATSTG* iface,\r
- ULONG celt,\r
- STATSTG* rgelt,\r
- ULONG* pceltFetched)\r
-{\r
- IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;\r
-\r
- StgProperty currentProperty;\r
- STATSTG* currentReturnStruct = rgelt;\r
- ULONG objectFetched = 0;\r
- ULONG currentSearchNode;\r
-\r
- /*\r
- * Perform a sanity check on the parameters.\r
- */\r
- if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )\r
- return E_INVALIDARG;\r
-\r
- /*\r
- * To avoid the special case, get another pointer to a ULONG value if\r
- * the caller didn't supply one.\r
- */\r
- if (pceltFetched==0)\r
- pceltFetched = &objectFetched;\r
-\r
- /*\r
- * Start the iteration, we will iterate until we hit the end of the\r
- * linked list or until we hit the number of items to iterate through\r
- */\r
- *pceltFetched = 0;\r
-\r
- /*\r
- * Start with the node at the top of the stack.\r
- */\r
- currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);\r
-\r
- while ( ( *pceltFetched < celt) &&\r
- ( currentSearchNode!=PROPERTY_NULL) )\r
- {\r
- /*\r
- * Remove the top node from the stack\r
- */\r
- IEnumSTATSTGImpl_PopSearchNode(This, TRUE);\r
-\r
- /*\r
- * Read the property from the storage.\r
- */\r
- StorageImpl_ReadProperty(This->parentStorage,\r
- currentSearchNode,\r
- ¤tProperty);\r
-\r
- /*\r
- * Copy the information to the return buffer.\r
- */\r
- StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,\r
- ¤tProperty,\r
- STATFLAG_DEFAULT);\r
-\r
- /*\r
- * Step to the next item in the iteration\r
- */\r
- (*pceltFetched)++;\r
- currentReturnStruct++;\r
-\r
- /*\r
- * Push the next search node in the search stack.\r
- */\r
- IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);\r
-\r
- /*\r
- * continue the iteration.\r
- */\r
- currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);\r
- }\r
-\r
- if (*pceltFetched == celt)\r
- return S_OK;\r
-\r
- return S_FALSE;\r
-}\r
-\r
-\r
-HRESULT WINAPI IEnumSTATSTGImpl_Skip(\r
- IEnumSTATSTG* iface,\r
- ULONG celt)\r
-{\r
- IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;\r
-\r
- StgProperty currentProperty;\r
- ULONG objectFetched = 0;\r
- ULONG currentSearchNode;\r
-\r
- /*\r
- * Start with the node at the top of the stack.\r
- */\r
- currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);\r
-\r
- while ( (objectFetched < celt) &&\r
- (currentSearchNode!=PROPERTY_NULL) )\r
- {\r
- /*\r
- * Remove the top node from the stack\r
- */\r
- IEnumSTATSTGImpl_PopSearchNode(This, TRUE);\r
-\r
- /*\r
- * Read the property from the storage.\r
- */\r
- StorageImpl_ReadProperty(This->parentStorage,\r
- currentSearchNode,\r
- ¤tProperty);\r
-\r
- /*\r
- * Step to the next item in the iteration\r
- */\r
- objectFetched++;\r
-\r
- /*\r
- * Push the next search node in the search stack.\r
- */\r
- IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);\r
-\r
- /*\r
- * continue the iteration.\r
- */\r
- currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);\r
- }\r
-\r
- if (objectFetched == celt)\r
- return S_OK;\r
-\r
- return S_FALSE;\r
-}\r
-\r
-HRESULT WINAPI IEnumSTATSTGImpl_Reset(\r
- IEnumSTATSTG* iface)\r
-{\r
- IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;\r
-\r
- StgProperty rootProperty;\r
- BOOL readSuccessful;\r
-\r
- /*\r
- * Re-initialize the search stack to an empty stack\r
- */\r
- This->stackSize = 0;\r
-\r
- /*\r
- * Read the root property from the storage.\r
- */\r
- readSuccessful = StorageImpl_ReadProperty(\r
- This->parentStorage,\r
- This->firstPropertyNode,\r
- &rootProperty);\r
-\r
- if (readSuccessful)\r
- {\r
- assert(rootProperty.sizeOfNameString!=0);\r
-\r
- /*\r
- * Push the search node in the search stack.\r
- */\r
- IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);\r
- }\r
-\r
- return S_OK;\r
-}\r
-\r
-HRESULT WINAPI IEnumSTATSTGImpl_Clone(\r
- IEnumSTATSTG* iface,\r
- IEnumSTATSTG** ppenum)\r
-{\r
- IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;\r
-\r
- IEnumSTATSTGImpl* newClone;\r
-\r
- /*\r
- * Perform a sanity check on the parameters.\r
- */\r
- if (ppenum==0)\r
- return E_INVALIDARG;\r
-\r
- newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,\r
- This->firstPropertyNode);\r
-\r
-\r
- /*\r
- * The new clone enumeration must point to the same current node as\r
- * the ole one.\r
- */\r
- newClone->stackSize = This->stackSize ;\r
- newClone->stackMaxSize = This->stackMaxSize ;\r
- newClone->stackToVisit =\r
- HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);\r
-\r
- memcpy(\r
- newClone->stackToVisit,\r
- This->stackToVisit,\r
- sizeof(ULONG) * newClone->stackSize);\r
-\r
- *ppenum = (IEnumSTATSTG*)newClone;\r
-\r
- /*\r
- * Don't forget to nail down a reference to the clone before\r
- * returning it.\r
- */\r
- IEnumSTATSTGImpl_AddRef(*ppenum);\r
-\r
- return S_OK;\r
-}\r
-\r
-INT IEnumSTATSTGImpl_FindParentProperty(\r
- IEnumSTATSTGImpl *This,\r
- ULONG childProperty,\r
- StgProperty *currentProperty,\r
- ULONG *thisNodeId)\r
-{\r
- ULONG currentSearchNode;\r
- ULONG foundNode;\r
-\r
- /*\r
- * To avoid the special case, get another pointer to a ULONG value if\r
- * the caller didn't supply one.\r
- */\r
- if (thisNodeId==0)\r
- thisNodeId = &foundNode;\r
-\r
- /*\r
- * Start with the node at the top of the stack.\r
- */\r
- currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);\r
-\r
-\r
- while (currentSearchNode!=PROPERTY_NULL)\r
- {\r
- /*\r
- * Store the current node in the returned parameters\r
- */\r
- *thisNodeId = currentSearchNode;\r
-\r
- /*\r
- * Remove the top node from the stack\r
- */\r
- IEnumSTATSTGImpl_PopSearchNode(This, TRUE);\r
-\r
- /*\r
- * Read the property from the storage.\r
- */\r
- StorageImpl_ReadProperty(\r
- This->parentStorage,\r
- currentSearchNode,\r
- currentProperty);\r
-\r
- if (currentProperty->previousProperty == childProperty)\r
- return PROPERTY_RELATION_PREVIOUS;\r
-\r
- else if (currentProperty->nextProperty == childProperty)\r
- return PROPERTY_RELATION_NEXT;\r
-\r
- else if (currentProperty->dirProperty == childProperty)\r
- return PROPERTY_RELATION_DIR;\r
-\r
- /*\r
- * Push the next search node in the search stack.\r
- */\r
- IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);\r
-\r
- /*\r
- * continue the iteration.\r
- */\r
- currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);\r
- }\r
-\r
- return PROPERTY_NULL;\r
-}\r
-\r
-ULONG IEnumSTATSTGImpl_FindProperty(\r
- IEnumSTATSTGImpl* This,\r
- const OLECHAR* lpszPropName,\r
- StgProperty* currentProperty)\r
-{\r
- ULONG currentSearchNode;\r
-\r
- /*\r
- * Start with the node at the top of the stack.\r
- */\r
- currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);\r
-\r
- while (currentSearchNode!=PROPERTY_NULL)\r
- {\r
- /*\r
- * Remove the top node from the stack\r
- */\r
- IEnumSTATSTGImpl_PopSearchNode(This, TRUE);\r
-\r
- /*\r
- * Read the property from the storage.\r
- */\r
- StorageImpl_ReadProperty(This->parentStorage,\r
- currentSearchNode,\r
- currentProperty);\r
-\r
- if ( propertyNameCmp(\r
- (const OLECHAR*)currentProperty->name,\r
- (const OLECHAR*)lpszPropName) == 0)\r
- return currentSearchNode;\r
-\r
- /*\r
- * Push the next search node in the search stack.\r
- */\r
- IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);\r
-\r
- /*\r
- * continue the iteration.\r
- */\r
- currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);\r
- }\r
-\r
- return PROPERTY_NULL;\r
-}\r
-\r
-void IEnumSTATSTGImpl_PushSearchNode(\r
- IEnumSTATSTGImpl* This,\r
- ULONG nodeToPush)\r
-{\r
- StgProperty rootProperty;\r
- BOOL readSuccessful;\r
-\r
- /*\r
- * First, make sure we're not trying to push an unexisting node.\r
- */\r
- if (nodeToPush==PROPERTY_NULL)\r
- return;\r
-\r
- /*\r
- * First push the node to the stack\r
- */\r
- if (This->stackSize == This->stackMaxSize)\r
- {\r
- This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;\r
-\r
- This->stackToVisit = HeapReAlloc(\r
- GetProcessHeap(),\r
- 0,\r
- This->stackToVisit,\r
- sizeof(ULONG) * This->stackMaxSize);\r
- }\r
-\r
- This->stackToVisit[This->stackSize] = nodeToPush;\r
- This->stackSize++;\r
-\r
- /*\r
- * Read the root property from the storage.\r
- */\r
- readSuccessful = StorageImpl_ReadProperty(\r
- This->parentStorage,\r
- nodeToPush,\r
- &rootProperty);\r
-\r
- if (readSuccessful)\r
- {\r
- assert(rootProperty.sizeOfNameString!=0);\r
-\r
- /*\r
- * Push the previous search node in the search stack.\r
- */\r
- IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);\r
- }\r
-}\r
-\r
-ULONG IEnumSTATSTGImpl_PopSearchNode(\r
- IEnumSTATSTGImpl* This,\r
- BOOL remove)\r
-{\r
- ULONG topNode;\r
-\r
- if (This->stackSize == 0)\r
- return PROPERTY_NULL;\r
-\r
- topNode = This->stackToVisit[This->stackSize-1];\r
-\r
- if (remove)\r
- This->stackSize--;\r
-\r
- return topNode;\r
-}\r
-\r
-/*\r
- * Virtual function table for the IEnumSTATSTGImpl class.\r
- */\r
-static IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =\r
-{\r
- IEnumSTATSTGImpl_QueryInterface,\r
- IEnumSTATSTGImpl_AddRef,\r
- IEnumSTATSTGImpl_Release,\r
- IEnumSTATSTGImpl_Next,\r
- IEnumSTATSTGImpl_Skip,\r
- IEnumSTATSTGImpl_Reset,\r
- IEnumSTATSTGImpl_Clone\r
-};\r
-\r
-/******************************************************************************\r
-** IEnumSTATSTGImpl implementation\r
-*/\r
-\r
-IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(\r
- StorageImpl* parentStorage,\r
- ULONG firstPropertyNode)\r
-{\r
- IEnumSTATSTGImpl* newEnumeration;\r
-\r
- newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));\r
-\r
- if (newEnumeration!=0)\r
- {\r
- /*\r
- * Set-up the virtual function table and reference count.\r
- */\r
- newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;\r
- newEnumeration->ref = 0;\r
-\r
- /*\r
- * We want to nail-down the reference to the storage in case the\r
- * enumeration out-lives the storage in the client application.\r
- */\r
- newEnumeration->parentStorage = parentStorage;\r
- IStorage_AddRef((IStorage*)newEnumeration->parentStorage);\r
-\r
- newEnumeration->firstPropertyNode = firstPropertyNode;\r
-\r
- /*\r
- * Initialize the search stack\r
- */\r
- newEnumeration->stackSize = 0;\r
- newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;\r
- newEnumeration->stackToVisit =\r
- HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);\r
-\r
- /*\r
- * Make sure the current node of the iterator is the first one.\r
- */\r
- IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);\r
- }\r
-\r
- return newEnumeration;\r
-}\r
-\r
-/*\r
- * Virtual function table for the Storage32InternalImpl class.\r
- */\r
-static IStorageVtbl Storage32InternalImpl_Vtbl =\r
-{\r
- StorageBaseImpl_QueryInterface,\r
- StorageBaseImpl_AddRef,\r
- StorageBaseImpl_Release,\r
- StorageBaseImpl_CreateStream,\r
- StorageBaseImpl_OpenStream,\r
- StorageImpl_CreateStorage,\r
- StorageBaseImpl_OpenStorage,\r
- StorageImpl_CopyTo,\r
- StorageImpl_MoveElementTo,\r
- StorageInternalImpl_Commit,\r
- StorageInternalImpl_Revert,\r
- StorageBaseImpl_EnumElements,\r
- StorageImpl_DestroyElement,\r
- StorageBaseImpl_RenameElement,\r
- StorageImpl_SetElementTimes,\r
- StorageBaseImpl_SetClass,\r
- StorageImpl_SetStateBits,\r
- StorageBaseImpl_Stat\r
-};\r
-\r
-/******************************************************************************\r
-** Storage32InternalImpl implementation\r
-*/\r
-\r
-StorageInternalImpl* StorageInternalImpl_Construct(\r
- StorageImpl* ancestorStorage,\r
- DWORD openFlags,\r
- ULONG rootPropertyIndex)\r
-{\r
- StorageInternalImpl* newStorage;\r
-\r
- /*\r
- * Allocate space for the new storage object\r
- */\r
- newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));\r
-\r
- if (newStorage!=0)\r
- {\r
- memset(newStorage, 0, sizeof(StorageInternalImpl));\r
-\r
- /*\r
- * Initialize the virtual function table.\r
- */\r
- newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;\r
- newStorage->base.v_destructor = &StorageInternalImpl_Destroy;\r
- newStorage->base.openFlags = openFlags;\r
-\r
- /*\r
- * Keep the ancestor storage pointer and nail a reference to it.\r
- */\r
- newStorage->base.ancestorStorage = ancestorStorage;\r
- StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));\r
-\r
- /*\r
- * Keep the index of the root property set for this storage,\r
- */\r
- newStorage->base.rootPropertySetIndex = rootPropertyIndex;\r
-\r
- return newStorage;\r
- }\r
-\r
- return 0;\r
-}\r
-\r
-/******************************************************************************\r
-** StorageUtl implementation\r
-*/\r
-\r
-void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)\r
-{\r
- WORD tmp;\r
-\r
- memcpy(&tmp, buffer+offset, sizeof(WORD));\r
- *value = le16toh(tmp);\r
-}\r
-\r
-void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)\r
-{\r
- value = htole16(value);\r
- memcpy(buffer+offset, &value, sizeof(WORD));\r
-}\r
-\r
-void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)\r
-{\r
- DWORD tmp;\r
-\r
- memcpy(&tmp, buffer+offset, sizeof(DWORD));\r
- *value = le32toh(tmp);\r
-}\r
-\r
-void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)\r
-{\r
- value = htole32(value);\r
- memcpy(buffer+offset, &value, sizeof(DWORD));\r
-}\r
-\r
-void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,\r
- ULARGE_INTEGER* value)\r
-{\r
-#ifdef WORDS_BIGENDIAN\r
- ULARGE_INTEGER tmp;\r
-\r
- memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));\r
- value->u.LowPart = htole32(tmp.u.HighPart);\r
- value->u.HighPart = htole32(tmp.u.LowPart);\r
-#else\r
- memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));\r
-#endif\r
-}\r
-\r
-void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,\r
- const ULARGE_INTEGER *value)\r
-{\r
-#ifdef WORDS_BIGENDIAN\r
- ULARGE_INTEGER tmp;\r
-\r
- tmp.u.LowPart = htole32(value->u.HighPart);\r
- tmp.u.HighPart = htole32(value->u.LowPart);\r
- memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));\r
-#else\r
- memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));\r
-#endif\r
-}\r
-\r
-void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)\r
-{\r
- StorageUtl_ReadDWord(buffer, offset, &(value->Data1));\r
- StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));\r
- StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));\r
-\r
- memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));\r
-}\r
-\r
-void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)\r
-{\r
- StorageUtl_WriteDWord(buffer, offset, value->Data1);\r
- StorageUtl_WriteWord(buffer, offset+4, value->Data2);\r
- StorageUtl_WriteWord(buffer, offset+6, value->Data3);\r
-\r
- memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));\r
-}\r
-\r
-void StorageUtl_CopyPropertyToSTATSTG(\r
- STATSTG* destination,\r
- StgProperty* source,\r
- int statFlags)\r
-{\r
- /*\r
- * The copy of the string occurs only when the flag is not set\r
- */\r
- if( ((statFlags & STATFLAG_NONAME) != 0) || \r
- (source->name == NULL) || \r
- (source->name[0] == 0) )\r
- {\r
- destination->pwcsName = 0;\r
- }\r
- else\r
- {\r
- destination->pwcsName =\r
- CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));\r
-\r
- strcpyW((LPWSTR)destination->pwcsName, source->name);\r
- }\r
-\r
- switch (source->propertyType)\r
- {\r
- case PROPTYPE_STORAGE:\r
- case PROPTYPE_ROOT:\r
- destination->type = STGTY_STORAGE;\r
- break;\r
- case PROPTYPE_STREAM:\r
- destination->type = STGTY_STREAM;\r
- break;\r
- default:\r
- destination->type = STGTY_STREAM;\r
- break;\r
- }\r
-\r
- destination->cbSize = source->size;\r
-/*\r
- currentReturnStruct->mtime = {0}; TODO\r
- currentReturnStruct->ctime = {0};\r
- currentReturnStruct->atime = {0};\r
-*/\r
- destination->grfMode = 0;\r
- destination->grfLocksSupported = 0;\r
- destination->clsid = source->propertyUniqueID;\r
- destination->grfStateBits = 0;\r
- destination->reserved = 0;\r
-}\r
-\r
-/******************************************************************************\r
-** BlockChainStream implementation\r
-*/\r
-\r
-BlockChainStream* BlockChainStream_Construct(\r
- StorageImpl* parentStorage,\r
- ULONG* headOfStreamPlaceHolder,\r
- ULONG propertyIndex)\r
-{\r
- BlockChainStream* newStream;\r
- ULONG blockIndex;\r
-\r
- newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));\r
-\r
- newStream->parentStorage = parentStorage;\r
- newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;\r
- newStream->ownerPropertyIndex = propertyIndex;\r
- newStream->lastBlockNoInSequence = 0xFFFFFFFF;\r
- newStream->tailIndex = BLOCK_END_OF_CHAIN;\r
- newStream->numBlocks = 0;\r
-\r
- blockIndex = BlockChainStream_GetHeadOfChain(newStream);\r
-\r
- while (blockIndex != BLOCK_END_OF_CHAIN)\r
- {\r
- newStream->numBlocks++;\r
- newStream->tailIndex = blockIndex;\r
-\r
- if(FAILED(StorageImpl_GetNextBlockInChain(\r
- parentStorage,\r
- blockIndex,\r
- &blockIndex)))\r
- {\r
- HeapFree(GetProcessHeap(), 0, newStream);\r
- return NULL;\r
- }\r
- }\r
-\r
- return newStream;\r
-}\r
-\r
-void BlockChainStream_Destroy(BlockChainStream* This)\r
-{\r
- HeapFree(GetProcessHeap(), 0, This);\r
-}\r
-\r
-/******************************************************************************\r
- * BlockChainStream_GetHeadOfChain\r
- *\r
- * Returns the head of this stream chain.\r
- * Some special chains don't have properties, their heads are kept in\r
- * This->headOfStreamPlaceHolder.\r
- *\r
- */\r
-ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)\r
-{\r
- StgProperty chainProperty;\r
- BOOL readSuccessful;\r
-\r
- if (This->headOfStreamPlaceHolder != 0)\r
- return *(This->headOfStreamPlaceHolder);\r
-\r
- if (This->ownerPropertyIndex != PROPERTY_NULL)\r
- {\r
- readSuccessful = StorageImpl_ReadProperty(\r
- This->parentStorage,\r
- This->ownerPropertyIndex,\r
- &chainProperty);\r
-\r
- if (readSuccessful)\r
- {\r
- return chainProperty.startingBlock;\r
- }\r
- }\r
-\r
- return BLOCK_END_OF_CHAIN;\r
-}\r
-\r
-/******************************************************************************\r
- * BlockChainStream_GetCount\r
- *\r
- * Returns the number of blocks that comprises this chain.\r
- * This is not the size of the stream as the last block may not be full!\r
- *\r
- */\r
-ULONG BlockChainStream_GetCount(BlockChainStream* This)\r
-{\r
- ULONG blockIndex;\r
- ULONG count = 0;\r
-\r
- blockIndex = BlockChainStream_GetHeadOfChain(This);\r
-\r
- while (blockIndex != BLOCK_END_OF_CHAIN)\r
- {\r
- count++;\r
-\r
- if(FAILED(StorageImpl_GetNextBlockInChain(\r
- This->parentStorage,\r
- blockIndex,\r
- &blockIndex)))\r
- return 0;\r
- }\r
-\r
- return count;\r
-}\r
-\r
-/******************************************************************************\r
- * BlockChainStream_ReadAt\r
- *\r
- * Reads a specified number of bytes from this chain at the specified offset.\r
- * bytesRead may be NULL.\r
- * Failure will be returned if the specified number of bytes has not been read.\r
- */\r
-BOOL BlockChainStream_ReadAt(BlockChainStream* This,\r
- ULARGE_INTEGER offset,\r
- ULONG size,\r
- void* buffer,\r
- ULONG* bytesRead)\r
-{\r
- ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;\r
- ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;\r
- ULONG bytesToReadInBuffer;\r
- ULONG blockIndex;\r
- BYTE* bufferWalker;\r
- BYTE* bigBlockBuffer;\r
-\r
- /*\r
- * Find the first block in the stream that contains part of the buffer.\r
- */\r
- if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||\r
- (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||\r
- (blockNoInSequence < This->lastBlockNoInSequence) )\r
- {\r
- blockIndex = BlockChainStream_GetHeadOfChain(This);\r
- This->lastBlockNoInSequence = blockNoInSequence;\r
- }\r
- else\r
- {\r
- ULONG temp = blockNoInSequence;\r
-\r
- blockIndex = This->lastBlockNoInSequenceIndex;\r
- blockNoInSequence -= This->lastBlockNoInSequence;\r
- This->lastBlockNoInSequence = temp;\r
- }\r
-\r
- while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))\r
- {\r
- if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))\r
- return FALSE;\r
- blockNoInSequence--;\r
- }\r
-\r
- This->lastBlockNoInSequenceIndex = blockIndex;\r
-\r
- /*\r
- * Start reading the buffer.\r
- */\r
- *bytesRead = 0;\r
- bufferWalker = buffer;\r
-\r
- while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )\r
- {\r
- /*\r
- * Calculate how many bytes we can copy from this big block.\r
- */\r
- bytesToReadInBuffer =\r
- min(This->parentStorage->bigBlockSize - offsetInBlock, size);\r
-\r
- /*\r
- * Copy those bytes to the buffer\r
- */\r
- bigBlockBuffer =\r
- StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);\r
-\r
- memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);\r
-\r
- StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);\r
-\r
- /*\r
- * Step to the next big block.\r
- */\r
- if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))\r
- return FALSE;\r
-\r
- bufferWalker += bytesToReadInBuffer;\r
- size -= bytesToReadInBuffer;\r
- *bytesRead += bytesToReadInBuffer;\r
- offsetInBlock = 0; /* There is no offset on the next block */\r
-\r
- }\r
-\r
- return (size == 0);\r
-}\r
-\r
-/******************************************************************************\r
- * BlockChainStream_WriteAt\r
- *\r
- * Writes the specified number of bytes to this chain at the specified offset.\r
- * bytesWritten may be NULL.\r
- * Will fail if not all specified number of bytes have been written.\r
- */\r
-BOOL BlockChainStream_WriteAt(BlockChainStream* This,\r
- ULARGE_INTEGER offset,\r
- ULONG size,\r
- const void* buffer,\r
- ULONG* bytesWritten)\r
-{\r
- ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;\r
- ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;\r
- ULONG bytesToWrite;\r
- ULONG blockIndex;\r
- const BYTE* bufferWalker;\r
- BYTE* bigBlockBuffer;\r
-\r
- /*\r
- * Find the first block in the stream that contains part of the buffer.\r
- */\r
- if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||\r
- (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||\r
- (blockNoInSequence < This->lastBlockNoInSequence) )\r
- {\r
- blockIndex = BlockChainStream_GetHeadOfChain(This);\r
- This->lastBlockNoInSequence = blockNoInSequence;\r
- }\r
- else\r
- {\r
- ULONG temp = blockNoInSequence;\r
-\r
- blockIndex = This->lastBlockNoInSequenceIndex;\r
- blockNoInSequence -= This->lastBlockNoInSequence;\r
- This->lastBlockNoInSequence = temp;\r
- }\r
-\r
- while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))\r
- {\r
- if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,\r
- &blockIndex)))\r
- return FALSE;\r
- blockNoInSequence--;\r
- }\r
-\r
- This->lastBlockNoInSequenceIndex = blockIndex;\r
-\r
- /*\r
- * Here, I'm casting away the constness on the buffer variable\r
- * This is OK since we don't intend to modify that buffer.\r
- */\r
- *bytesWritten = 0;\r
- bufferWalker = (const BYTE*)buffer;\r
-\r
- while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )\r
- {\r
- /*\r
- * Calculate how many bytes we can copy from this big block.\r
- */\r
- bytesToWrite =\r
- min(This->parentStorage->bigBlockSize - offsetInBlock, size);\r
-\r
- /*\r
- * Copy those bytes to the buffer\r
- */\r
- bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);\r
-\r
- memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);\r
-\r
- StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);\r
-\r
- /*\r
- * Step to the next big block.\r
- */\r
- if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,\r
- &blockIndex)))\r
- return FALSE;\r
- bufferWalker += bytesToWrite;\r
- size -= bytesToWrite;\r
- *bytesWritten += bytesToWrite;\r
- offsetInBlock = 0; /* There is no offset on the next block */\r
- }\r
-\r
- return (size == 0);\r
-}\r
-\r
-/******************************************************************************\r
- * BlockChainStream_Shrink\r
- *\r
- * Shrinks this chain in the big block depot.\r
- */\r
-BOOL BlockChainStream_Shrink(BlockChainStream* This,\r
- ULARGE_INTEGER newSize)\r
-{\r
- ULONG blockIndex, extraBlock;\r
- ULONG numBlocks;\r
- ULONG count = 1;\r
-\r
- /*\r
- * Reset the last accessed block cache.\r
- */\r
- This->lastBlockNoInSequence = 0xFFFFFFFF;\r
- This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;\r
-\r
- /*\r
- * Figure out how many blocks are needed to contain the new size\r
- */\r
- numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;\r
-\r
- if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)\r
- numBlocks++;\r
-\r
- blockIndex = BlockChainStream_GetHeadOfChain(This);\r
-\r
- /*\r
- * Go to the new end of chain\r
- */\r
- while (count < numBlocks)\r
- {\r
- if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,\r
- &blockIndex)))\r
- return FALSE;\r
- count++;\r
- }\r
-\r
- /* Get the next block before marking the new end */\r
- if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,\r
- &extraBlock)))\r
- return FALSE;\r
-\r
- /* Mark the new end of chain */\r
- StorageImpl_SetNextBlockInChain(\r
- This->parentStorage,\r
- blockIndex,\r
- BLOCK_END_OF_CHAIN);\r
-\r
- This->tailIndex = blockIndex;\r
- This->numBlocks = numBlocks;\r
-\r
- /*\r
- * Mark the extra blocks as free\r
- */\r
- while (extraBlock != BLOCK_END_OF_CHAIN)\r
- {\r
- if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,\r
- &blockIndex)))\r
- return FALSE;\r
- StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);\r
- extraBlock = blockIndex;\r
- }\r
-\r
- return TRUE;\r
-}\r
-\r
-/******************************************************************************\r
- * BlockChainStream_Enlarge\r
- *\r
- * Grows this chain in the big block depot.\r
- */\r
-BOOL BlockChainStream_Enlarge(BlockChainStream* This,\r
- ULARGE_INTEGER newSize)\r
-{\r
- ULONG blockIndex, currentBlock;\r
- ULONG newNumBlocks;\r
- ULONG oldNumBlocks = 0;\r
-\r
- blockIndex = BlockChainStream_GetHeadOfChain(This);\r
-\r
- /*\r
- * Empty chain. Create the head.\r
- */\r
- if (blockIndex == BLOCK_END_OF_CHAIN)\r
- {\r
- blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);\r
- StorageImpl_SetNextBlockInChain(This->parentStorage,\r
- blockIndex,\r
- BLOCK_END_OF_CHAIN);\r
-\r
- if (This->headOfStreamPlaceHolder != 0)\r
- {\r
- *(This->headOfStreamPlaceHolder) = blockIndex;\r
- }\r
- else\r
- {\r
- StgProperty chainProp;\r
- assert(This->ownerPropertyIndex != PROPERTY_NULL);\r
-\r
- StorageImpl_ReadProperty(\r
- This->parentStorage,\r
- This->ownerPropertyIndex,\r
- &chainProp);\r
-\r
- chainProp.startingBlock = blockIndex;\r
-\r
- StorageImpl_WriteProperty(\r
- This->parentStorage,\r
- This->ownerPropertyIndex,\r
- &chainProp);\r
- }\r
-\r
- This->tailIndex = blockIndex;\r
- This->numBlocks = 1;\r
- }\r
-\r
- /*\r
- * Figure out how many blocks are needed to contain this stream\r
- */\r
- newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;\r
-\r
- if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)\r
- newNumBlocks++;\r
-\r
- /*\r
- * Go to the current end of chain\r
- */\r
- if (This->tailIndex == BLOCK_END_OF_CHAIN)\r
- {\r
- currentBlock = blockIndex;\r
-\r
- while (blockIndex != BLOCK_END_OF_CHAIN)\r
- {\r
- This->numBlocks++;\r
- currentBlock = blockIndex;\r
-\r
- if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,\r
- &blockIndex)))\r
- return FALSE;\r
- }\r
-\r
- This->tailIndex = currentBlock;\r
- }\r
-\r
- currentBlock = This->tailIndex;\r
- oldNumBlocks = This->numBlocks;\r
-\r
- /*\r
- * Add new blocks to the chain\r
- */\r
- if (oldNumBlocks < newNumBlocks)\r
- {\r
- while (oldNumBlocks < newNumBlocks)\r
- {\r
- blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);\r
-\r
- StorageImpl_SetNextBlockInChain(\r
- This->parentStorage,\r
- currentBlock,\r
- blockIndex);\r
-\r
- StorageImpl_SetNextBlockInChain(\r
- This->parentStorage,\r
- blockIndex,\r
- BLOCK_END_OF_CHAIN);\r
-\r
- currentBlock = blockIndex;\r
- oldNumBlocks++;\r
- }\r
-\r
- This->tailIndex = blockIndex;\r
- This->numBlocks = newNumBlocks;\r
- }\r
-\r
- return TRUE;\r
-}\r
-\r
-/******************************************************************************\r
- * BlockChainStream_SetSize\r
- *\r
- * Sets the size of this stream. The big block depot will be updated.\r
- * The file will grow if we grow the chain.\r
- *\r
- * TODO: Free the actual blocks in the file when we shrink the chain.\r
- * Currently, the blocks are still in the file. So the file size\r
- * doesn't shrink even if we shrink streams.\r
- */\r
-BOOL BlockChainStream_SetSize(\r
- BlockChainStream* This,\r
- ULARGE_INTEGER newSize)\r
-{\r
- ULARGE_INTEGER size = BlockChainStream_GetSize(This);\r
-\r
- if (newSize.u.LowPart == size.u.LowPart)\r
- return TRUE;\r
-\r
- if (newSize.u.LowPart < size.u.LowPart)\r
- {\r
- BlockChainStream_Shrink(This, newSize);\r
- }\r
- else\r
- {\r
- ULARGE_INTEGER fileSize =\r
- BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);\r
-\r
- ULONG diff = newSize.u.LowPart - size.u.LowPart;\r
-\r
- /*\r
- * Make sure the file stays a multiple of blocksize\r
- */\r
- if ((diff % This->parentStorage->bigBlockSize) != 0)\r
- diff += (This->parentStorage->bigBlockSize -\r
- (diff % This->parentStorage->bigBlockSize) );\r
-\r
- fileSize.u.LowPart += diff;\r
- BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);\r
-\r
- BlockChainStream_Enlarge(This, newSize);\r
- }\r
-\r
- return TRUE;\r
-}\r
-\r
-/******************************************************************************\r
- * BlockChainStream_GetSize\r
- *\r
- * Returns the size of this chain.\r
- * Will return the block count if this chain doesn't have a property.\r
- */\r
-ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)\r
-{\r
- StgProperty chainProperty;\r
-\r
- if(This->headOfStreamPlaceHolder == NULL)\r
- {\r
- /*\r
- * This chain is a data stream read the property and return\r
- * the appropriate size\r
- */\r
- StorageImpl_ReadProperty(\r
- This->parentStorage,\r
- This->ownerPropertyIndex,\r
- &chainProperty);\r
-\r
- return chainProperty.size;\r
- }\r
- else\r
- {\r
- /*\r
- * this chain is a chain that does not have a property, figure out the\r
- * size by making the product number of used blocks times the\r
- * size of them\r
- */\r
- ULARGE_INTEGER result;\r
- result.u.HighPart = 0;\r
-\r
- result.u.LowPart =\r
- BlockChainStream_GetCount(This) *\r
- This->parentStorage->bigBlockSize;\r
-\r
- return result;\r
- }\r
-}\r
-\r
-/******************************************************************************\r
-** SmallBlockChainStream implementation\r
-*/\r
-\r
-SmallBlockChainStream* SmallBlockChainStream_Construct(\r
- StorageImpl* parentStorage,\r
- ULONG propertyIndex)\r
-{\r
- SmallBlockChainStream* newStream;\r
-\r
- newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));\r
-\r
- newStream->parentStorage = parentStorage;\r
- newStream->ownerPropertyIndex = propertyIndex;\r
-\r
- return newStream;\r
-}\r
-\r
-void SmallBlockChainStream_Destroy(\r
- SmallBlockChainStream* This)\r
-{\r
- HeapFree(GetProcessHeap(), 0, This);\r
-}\r
-\r
-/******************************************************************************\r
- * SmallBlockChainStream_GetHeadOfChain\r
- *\r
- * Returns the head of this chain of small blocks.\r
- */\r
-ULONG SmallBlockChainStream_GetHeadOfChain(\r
- SmallBlockChainStream* This)\r
-{\r
- StgProperty chainProperty;\r
- BOOL readSuccessful;\r
-\r
- if (This->ownerPropertyIndex)\r
- {\r
- readSuccessful = StorageImpl_ReadProperty(\r
- This->parentStorage,\r
- This->ownerPropertyIndex,\r
- &chainProperty);\r
-\r
- if (readSuccessful)\r
- {\r
- return chainProperty.startingBlock;\r
- }\r
-\r
- }\r
-\r
- return BLOCK_END_OF_CHAIN;\r
-}\r
-\r
-/******************************************************************************\r
- * SmallBlockChainStream_GetNextBlockInChain\r
- *\r
- * Returns the index of the next small block in this chain.\r
- *\r
- * Return Values:\r
- * - BLOCK_END_OF_CHAIN: end of this chain\r
- * - BLOCK_UNUSED: small block 'blockIndex' is free\r
- */\r
-HRESULT SmallBlockChainStream_GetNextBlockInChain(\r
- SmallBlockChainStream* This,\r
- ULONG blockIndex,\r
- ULONG* nextBlockInChain)\r
-{\r
- ULARGE_INTEGER offsetOfBlockInDepot;\r
- DWORD buffer;\r
- ULONG bytesRead;\r
- BOOL success;\r
-\r
- *nextBlockInChain = BLOCK_END_OF_CHAIN;\r
-\r
- offsetOfBlockInDepot.u.HighPart = 0;\r
- offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);\r
-\r
- /*\r
- * Read those bytes in the buffer from the small block file.\r
- */\r
- success = BlockChainStream_ReadAt(\r
- This->parentStorage->smallBlockDepotChain,\r
- offsetOfBlockInDepot,\r
- sizeof(DWORD),\r
- &buffer,\r
- &bytesRead);\r
-\r
- if (success)\r
- {\r
- StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);\r
- return S_OK;\r
- }\r
-\r
- return STG_E_READFAULT;\r
-}\r
-\r
-/******************************************************************************\r
- * SmallBlockChainStream_SetNextBlockInChain\r
- *\r
- * Writes the index of the next block of the specified block in the small\r
- * block depot.\r
- * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.\r
- * To flag a block as free use BLOCK_UNUSED as nextBlock.\r
- */\r
-void SmallBlockChainStream_SetNextBlockInChain(\r
- SmallBlockChainStream* This,\r
- ULONG blockIndex,\r
- ULONG nextBlock)\r
-{\r
- ULARGE_INTEGER offsetOfBlockInDepot;\r
- DWORD buffer;\r
- ULONG bytesWritten;\r
-\r
- offsetOfBlockInDepot.u.HighPart = 0;\r
- offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);\r
-\r
- StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);\r
-\r
- /*\r
- * Read those bytes in the buffer from the small block file.\r
- */\r
- BlockChainStream_WriteAt(\r
- This->parentStorage->smallBlockDepotChain,\r
- offsetOfBlockInDepot,\r
- sizeof(DWORD),\r
- &buffer,\r
- &bytesWritten);\r
-}\r
-\r
-/******************************************************************************\r
- * SmallBlockChainStream_FreeBlock\r
- *\r
- * Flag small block 'blockIndex' as free in the small block depot.\r
- */\r
-void SmallBlockChainStream_FreeBlock(\r
- SmallBlockChainStream* This,\r
- ULONG blockIndex)\r
-{\r
- SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);\r
-}\r
-\r
-/******************************************************************************\r
- * SmallBlockChainStream_GetNextFreeBlock\r
- *\r
- * Returns the index of a free small block. The small block depot will be\r
- * enlarged if necessary. The small block chain will also be enlarged if\r
- * necessary.\r
- */\r
-ULONG SmallBlockChainStream_GetNextFreeBlock(\r
- SmallBlockChainStream* This)\r
-{\r
- ULARGE_INTEGER offsetOfBlockInDepot;\r
- DWORD buffer;\r
- ULONG bytesRead;\r
- ULONG blockIndex = 0;\r
- ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;\r
- BOOL success = TRUE;\r
- ULONG smallBlocksPerBigBlock;\r
-\r
- offsetOfBlockInDepot.u.HighPart = 0;\r
-\r
- /*\r
- * Scan the small block depot for a free block\r
- */\r
- while (nextBlockIndex != BLOCK_UNUSED)\r
- {\r
- offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);\r
-\r
- success = BlockChainStream_ReadAt(\r
- This->parentStorage->smallBlockDepotChain,\r
- offsetOfBlockInDepot,\r
- sizeof(DWORD),\r
- &buffer,\r
- &bytesRead);\r
-\r
- /*\r
- * If we run out of space for the small block depot, enlarge it\r
- */\r
- if (success)\r
- {\r
- StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);\r
-\r
- if (nextBlockIndex != BLOCK_UNUSED)\r
- blockIndex++;\r
- }\r
- else\r
- {\r
- ULONG count =\r
- BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);\r
-\r
- ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;\r
- ULONG nextBlock, newsbdIndex;\r
- BYTE* smallBlockDepot;\r
-\r
- nextBlock = sbdIndex;\r
- while (nextBlock != BLOCK_END_OF_CHAIN)\r
- {\r
- sbdIndex = nextBlock;\r
- StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);\r
- }\r
-\r
- newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);\r
- if (sbdIndex != BLOCK_END_OF_CHAIN)\r
- StorageImpl_SetNextBlockInChain(\r
- This->parentStorage,\r
- sbdIndex,\r
- newsbdIndex);\r
-\r
- StorageImpl_SetNextBlockInChain(\r
- This->parentStorage,\r
- newsbdIndex,\r
- BLOCK_END_OF_CHAIN);\r
-\r
- /*\r
- * Initialize all the small blocks to free\r
- */\r
- smallBlockDepot =\r
- StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);\r
-\r
- memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);\r
- StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);\r
-\r
- if (count == 0)\r
- {\r
- /*\r
- * We have just created the small block depot.\r
- */\r
- StgProperty rootProp;\r
- ULONG sbStartIndex;\r
-\r
- /*\r
- * Save it in the header\r
- */\r
- This->parentStorage->smallBlockDepotStart = newsbdIndex;\r
- StorageImpl_SaveFileHeader(This->parentStorage);\r
-\r
- /*\r
- * And allocate the first big block that will contain small blocks\r
- */\r
- sbStartIndex =\r
- StorageImpl_GetNextFreeBigBlock(This->parentStorage);\r
-\r
- StorageImpl_SetNextBlockInChain(\r
- This->parentStorage,\r
- sbStartIndex,\r
- BLOCK_END_OF_CHAIN);\r
-\r
- StorageImpl_ReadProperty(\r
- This->parentStorage,\r
- This->parentStorage->base.rootPropertySetIndex,\r
- &rootProp);\r
-\r
- rootProp.startingBlock = sbStartIndex;\r
- rootProp.size.u.HighPart = 0;\r
- rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;\r
-\r
- StorageImpl_WriteProperty(\r
- This->parentStorage,\r
- This->parentStorage->base.rootPropertySetIndex,\r
- &rootProp);\r
- }\r
- }\r
- }\r
-\r
- smallBlocksPerBigBlock =\r
- This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;\r
-\r
- /*\r
- * Verify if we have to allocate big blocks to contain small blocks\r
- */\r
- if (blockIndex % smallBlocksPerBigBlock == 0)\r
- {\r
- StgProperty rootProp;\r
- ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;\r
-\r
- StorageImpl_ReadProperty(\r
- This->parentStorage,\r
- This->parentStorage->base.rootPropertySetIndex,\r
- &rootProp);\r
-\r
- if (rootProp.size.u.LowPart <\r
- (blocksRequired * This->parentStorage->bigBlockSize))\r
- {\r
- rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;\r
-\r
- BlockChainStream_SetSize(\r
- This->parentStorage->smallBlockRootChain,\r
- rootProp.size);\r
-\r
- StorageImpl_WriteProperty(\r
- This->parentStorage,\r
- This->parentStorage->base.rootPropertySetIndex,\r
- &rootProp);\r
- }\r
- }\r
-\r
- return blockIndex;\r
-}\r
-\r
-/******************************************************************************\r
- * SmallBlockChainStream_ReadAt\r
- *\r
- * Reads a specified number of bytes from this chain at the specified offset.\r
- * bytesRead may be NULL.\r
- * Failure will be returned if the specified number of bytes has not been read.\r
- */\r
-BOOL SmallBlockChainStream_ReadAt(\r
- SmallBlockChainStream* This,\r
- ULARGE_INTEGER offset,\r
- ULONG size,\r
- void* buffer,\r
- ULONG* bytesRead)\r
-{\r
- ULARGE_INTEGER offsetInBigBlockFile;\r
- ULONG blockNoInSequence =\r
- offset.u.LowPart / This->parentStorage->smallBlockSize;\r
-\r
- ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;\r
- ULONG bytesToReadInBuffer;\r
- ULONG blockIndex;\r
- ULONG bytesReadFromBigBlockFile;\r
- BYTE* bufferWalker;\r
-\r
- /*\r
- * This should never happen on a small block file.\r
- */\r
- assert(offset.u.HighPart==0);\r
-\r
- /*\r
- * Find the first block in the stream that contains part of the buffer.\r
- */\r
- blockIndex = SmallBlockChainStream_GetHeadOfChain(This);\r
-\r
- while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))\r
- {\r
- if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,\r
- &blockIndex)))\r
- return FALSE;\r
- blockNoInSequence--;\r
- }\r
-\r
- /*\r
- * Start reading the buffer.\r
- */\r
- *bytesRead = 0;\r
- bufferWalker = buffer;\r
-\r
- while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )\r
- {\r
- /*\r
- * Calculate how many bytes we can copy from this small block.\r
- */\r
- bytesToReadInBuffer =\r
- min(This->parentStorage->smallBlockSize - offsetInBlock, size);\r
-\r
- /*\r
- * Calculate the offset of the small block in the small block file.\r
- */\r
- offsetInBigBlockFile.u.HighPart = 0;\r
- offsetInBigBlockFile.u.LowPart =\r
- blockIndex * This->parentStorage->smallBlockSize;\r
-\r
- offsetInBigBlockFile.u.LowPart += offsetInBlock;\r
-\r
- /*\r
- * Read those bytes in the buffer from the small block file.\r
- */\r
- BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,\r
- offsetInBigBlockFile,\r
- bytesToReadInBuffer,\r
- bufferWalker,\r
- &bytesReadFromBigBlockFile);\r
-\r
- assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);\r
-\r
- /*\r
- * Step to the next big block.\r
- */\r
- if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))\r
- return FALSE;\r
- bufferWalker += bytesToReadInBuffer;\r
- size -= bytesToReadInBuffer;\r
- *bytesRead += bytesToReadInBuffer;\r
- offsetInBlock = 0; /* There is no offset on the next block */\r
- }\r
-\r
- return (size == 0);\r
-}\r
-\r
-/******************************************************************************\r
- * SmallBlockChainStream_WriteAt\r
- *\r
- * Writes the specified number of bytes to this chain at the specified offset.\r
- * bytesWritten may be NULL.\r
- * Will fail if not all specified number of bytes have been written.\r
- */\r
-BOOL SmallBlockChainStream_WriteAt(\r
- SmallBlockChainStream* This,\r
- ULARGE_INTEGER offset,\r
- ULONG size,\r
- const void* buffer,\r
- ULONG* bytesWritten)\r
-{\r
- ULARGE_INTEGER offsetInBigBlockFile;\r
- ULONG blockNoInSequence =\r
- offset.u.LowPart / This->parentStorage->smallBlockSize;\r
-\r
- ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;\r
- ULONG bytesToWriteInBuffer;\r
- ULONG blockIndex;\r
- ULONG bytesWrittenFromBigBlockFile;\r
- const BYTE* bufferWalker;\r
-\r
- /*\r
- * This should never happen on a small block file.\r
- */\r
- assert(offset.u.HighPart==0);\r
-\r
- /*\r
- * Find the first block in the stream that contains part of the buffer.\r
- */\r
- blockIndex = SmallBlockChainStream_GetHeadOfChain(This);\r
-\r
- while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))\r
- {\r
- if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))\r
- return FALSE;\r
- blockNoInSequence--;\r
- }\r
-\r
- /*\r
- * Start writing the buffer.\r
- *\r
- * Here, I'm casting away the constness on the buffer variable\r
- * This is OK since we don't intend to modify that buffer.\r
- */\r
- *bytesWritten = 0;\r
- bufferWalker = (const BYTE*)buffer;\r
- while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )\r
- {\r
- /*\r
- * Calculate how many bytes we can copy to this small block.\r
- */\r
- bytesToWriteInBuffer =\r
- min(This->parentStorage->smallBlockSize - offsetInBlock, size);\r
-\r
- /*\r
- * Calculate the offset of the small block in the small block file.\r
- */\r
- offsetInBigBlockFile.u.HighPart = 0;\r
- offsetInBigBlockFile.u.LowPart =\r
- blockIndex * This->parentStorage->smallBlockSize;\r
-\r
- offsetInBigBlockFile.u.LowPart += offsetInBlock;\r
-\r
- /*\r
- * Write those bytes in the buffer to the small block file.\r
- */\r
- BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,\r
- offsetInBigBlockFile,\r
- bytesToWriteInBuffer,\r
- bufferWalker,\r
- &bytesWrittenFromBigBlockFile);\r
-\r
- assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);\r
-\r
- /*\r
- * Step to the next big block.\r
- */\r
- if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,\r
- &blockIndex)))\r
- return FALSE;\r
- bufferWalker += bytesToWriteInBuffer;\r
- size -= bytesToWriteInBuffer;\r
- *bytesWritten += bytesToWriteInBuffer;\r
- offsetInBlock = 0; /* There is no offset on the next block */\r
- }\r
-\r
- return (size == 0);\r
-}\r
-\r
-/******************************************************************************\r
- * SmallBlockChainStream_Shrink\r
- *\r
- * Shrinks this chain in the small block depot.\r
- */\r
-BOOL SmallBlockChainStream_Shrink(\r
- SmallBlockChainStream* This,\r
- ULARGE_INTEGER newSize)\r
-{\r
- ULONG blockIndex, extraBlock;\r
- ULONG numBlocks;\r
- ULONG count = 0;\r
-\r
- numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;\r
-\r
- if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)\r
- numBlocks++;\r
-\r
- blockIndex = SmallBlockChainStream_GetHeadOfChain(This);\r
-\r
- /*\r
- * Go to the new end of chain\r
- */\r
- while (count < numBlocks)\r
- {\r
- if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,\r
- &blockIndex)))\r
- return FALSE;\r
- count++;\r
- }\r
-\r
- /*\r
- * If the count is 0, we have a special case, the head of the chain was\r
- * just freed.\r
- */\r
- if (count == 0)\r
- {\r
- StgProperty chainProp;\r
-\r
- StorageImpl_ReadProperty(This->parentStorage,\r
- This->ownerPropertyIndex,\r
- &chainProp);\r
-\r
- chainProp.startingBlock = BLOCK_END_OF_CHAIN;\r
-\r
- StorageImpl_WriteProperty(This->parentStorage,\r
- This->ownerPropertyIndex,\r
- &chainProp);\r
-\r
- /*\r
- * We start freeing the chain at the head block.\r
- */\r
- extraBlock = blockIndex;\r
- }\r
- else\r
- {\r
- /* Get the next block before marking the new end */\r
- if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,\r
- &extraBlock)))\r
- return FALSE;\r
-\r
- /* Mark the new end of chain */\r
- SmallBlockChainStream_SetNextBlockInChain(\r
- This,\r
- blockIndex,\r
- BLOCK_END_OF_CHAIN);\r
- }\r
-\r
- /*\r
- * Mark the extra blocks as free\r
- */\r
- while (extraBlock != BLOCK_END_OF_CHAIN)\r
- {\r
- if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,\r
- &blockIndex)))\r
- return FALSE;\r
- SmallBlockChainStream_FreeBlock(This, extraBlock);\r
- extraBlock = blockIndex;\r
- }\r
-\r
- return TRUE;\r
-}\r
-\r
-/******************************************************************************\r
- * SmallBlockChainStream_Enlarge\r
- *\r
- * Grows this chain in the small block depot.\r
- */\r
-BOOL SmallBlockChainStream_Enlarge(\r
- SmallBlockChainStream* This,\r
- ULARGE_INTEGER newSize)\r
-{\r
- ULONG blockIndex, currentBlock;\r
- ULONG newNumBlocks;\r
- ULONG oldNumBlocks = 0;\r
-\r
- blockIndex = SmallBlockChainStream_GetHeadOfChain(This);\r
-\r
- /*\r
- * Empty chain\r
- */\r
- if (blockIndex == BLOCK_END_OF_CHAIN)\r
- {\r
-\r
- StgProperty chainProp;\r
-\r
- StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,\r
- &chainProp);\r
-\r
- chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);\r
-\r
- StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,\r
- &chainProp);\r
-\r
- blockIndex = chainProp.startingBlock;\r
- SmallBlockChainStream_SetNextBlockInChain(\r
- This,\r
- blockIndex,\r
- BLOCK_END_OF_CHAIN);\r
- }\r
-\r
- currentBlock = blockIndex;\r
-\r
- /*\r
- * Figure out how many blocks are needed to contain this stream\r
- */\r
- newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;\r
-\r
- if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)\r
- newNumBlocks++;\r
-\r
- /*\r
- * Go to the current end of chain\r
- */\r
- while (blockIndex != BLOCK_END_OF_CHAIN)\r
- {\r
- oldNumBlocks++;\r
- currentBlock = blockIndex;\r
- if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))\r
- return FALSE;\r
- }\r
-\r
- /*\r
- * Add new blocks to the chain\r
- */\r
- while (oldNumBlocks < newNumBlocks)\r
- {\r
- blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);\r
- SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);\r
-\r
- SmallBlockChainStream_SetNextBlockInChain(\r
- This,\r
- blockIndex,\r
- BLOCK_END_OF_CHAIN);\r
-\r
- currentBlock = blockIndex;\r
- oldNumBlocks++;\r
- }\r
-\r
- return TRUE;\r
-}\r
-\r
-/******************************************************************************\r
- * SmallBlockChainStream_GetCount\r
- *\r
- * Returns the number of blocks that comprises this chain.\r
- * This is not the size of this chain as the last block may not be full!\r
- */\r
-ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)\r
-{\r
- ULONG blockIndex;\r
- ULONG count = 0;\r
-\r
- blockIndex = SmallBlockChainStream_GetHeadOfChain(This);\r
-\r
- while (blockIndex != BLOCK_END_OF_CHAIN)\r
- {\r
- count++;\r
-\r
- if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))\r
- return 0;\r
- }\r
-\r
- return count;\r
-}\r
-\r
-/******************************************************************************\r
- * SmallBlockChainStream_SetSize\r
- *\r
- * Sets the size of this stream.\r
- * The file will grow if we grow the chain.\r
- *\r
- * TODO: Free the actual blocks in the file when we shrink the chain.\r
- * Currently, the blocks are still in the file. So the file size\r
- * doesn't shrink even if we shrink streams.\r
- */\r
-BOOL SmallBlockChainStream_SetSize(\r
- SmallBlockChainStream* This,\r
- ULARGE_INTEGER newSize)\r
-{\r
- ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);\r
-\r
- if (newSize.u.LowPart == size.u.LowPart)\r
- return TRUE;\r
-\r
- if (newSize.u.LowPart < size.u.LowPart)\r
- {\r
- SmallBlockChainStream_Shrink(This, newSize);\r
- }\r
- else\r
- {\r
- SmallBlockChainStream_Enlarge(This, newSize);\r
- }\r
-\r
- return TRUE;\r
-}\r
-\r
-/******************************************************************************\r
- * SmallBlockChainStream_GetSize\r
- *\r
- * Returns the size of this chain.\r
- */\r
-ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)\r
-{\r
- StgProperty chainProperty;\r
-\r
- StorageImpl_ReadProperty(\r
- This->parentStorage,\r
- This->ownerPropertyIndex,\r
- &chainProperty);\r
-\r
- return chainProperty.size;\r
-}\r
-\r
-/******************************************************************************\r
- * StgCreateDocfile [OLE32.@]\r
- */\r
-HRESULT WINAPI StgCreateDocfile(\r
- LPCOLESTR pwcsName,\r
- DWORD grfMode,\r
- DWORD reserved,\r
- IStorage **ppstgOpen)\r
-{\r
- StorageImpl* newStorage = 0;\r
- HANDLE hFile = INVALID_HANDLE_VALUE;\r
- HRESULT hr = STG_E_INVALIDFLAG;\r
- DWORD shareMode;\r
- DWORD accessMode;\r
- DWORD creationMode;\r
- DWORD fileAttributes;\r
- WCHAR tempFileName[MAX_PATH];\r
-\r
- TRACE("(%s, %lx, %ld, %p)\n",\r
- debugstr_w(pwcsName), grfMode,\r
- reserved, ppstgOpen);\r
-\r
- /*\r
- * Validate the parameters\r
- */\r
- if (ppstgOpen == 0)\r
- return STG_E_INVALIDPOINTER;\r
- if (reserved != 0)\r
- return STG_E_INVALIDPARAMETER;\r
-\r
- /*\r
- * Validate the STGM flags\r
- */\r
- if ( FAILED( validateSTGM(grfMode) ))\r
- goto end;\r
-\r
- /* StgCreateDocFile always opens for write */\r
- switch(STGM_ACCESS_MODE(grfMode))\r
- {\r
- case STGM_WRITE:\r
- case STGM_READWRITE:\r
- break;\r
- default:\r
- goto end;\r
- }\r
-\r
- /* can't share write */\r
- switch(STGM_SHARE_MODE(grfMode))\r
- {\r
- case STGM_SHARE_EXCLUSIVE:\r
- case STGM_SHARE_DENY_WRITE:\r
- break;\r
- default:\r
- goto end;\r
- }\r
-\r
- /* shared reading requires transacted mode */\r
- if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&\r
- !(grfMode&STGM_TRANSACTED) )\r
- goto end;\r
-\r
- /*\r
- * Generate a unique name.\r
- */\r
- if (pwcsName == 0)\r
- {\r
- WCHAR tempPath[MAX_PATH];\r
- static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };\r
-\r
- if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)\r
- goto end;\r
-\r
- memset(tempPath, 0, sizeof(tempPath));\r
- memset(tempFileName, 0, sizeof(tempFileName));\r
-\r
- if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )\r
- tempPath[0] = '.';\r
-\r
- if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)\r
- pwcsName = tempFileName;\r
- else\r
- {\r
- hr = STG_E_INSUFFICIENTMEMORY;\r
- goto end;\r
- }\r
-\r
- creationMode = TRUNCATE_EXISTING;\r
- }\r
- else\r
- {\r
- creationMode = GetCreationModeFromSTGM(grfMode);\r
- }\r
-\r
- /*\r
- * Interpret the STGM value grfMode\r
- */\r
- shareMode = GetShareModeFromSTGM(grfMode);\r
- accessMode = GetAccessModeFromSTGM(grfMode);\r
-\r
- if (grfMode & STGM_DELETEONRELEASE)\r
- fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;\r
- else\r
- fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;\r
-\r
- if (grfMode & STGM_TRANSACTED)\r
- FIXME("Transacted mode not implemented.\n");\r
-\r
- /*\r
- * Initialize the "out" parameter.\r
- */\r
- *ppstgOpen = 0;\r
-\r
- hFile = CreateFileW(pwcsName,\r
- accessMode,\r
- shareMode,\r
- NULL,\r
- creationMode,\r
- fileAttributes,\r
- 0);\r
-\r
- if (hFile == INVALID_HANDLE_VALUE)\r
- {\r
- if(GetLastError() == ERROR_FILE_EXISTS)\r
- hr = STG_E_FILEALREADYEXISTS;\r
- else\r
- hr = E_FAIL;\r
- goto end;\r
- }\r
-\r
- /*\r
- * Allocate and initialize the new IStorage32object.\r
- */\r
- newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));\r
-\r
- if (newStorage == 0)\r
- {\r
- hr = STG_E_INSUFFICIENTMEMORY;\r
- goto end;\r
- }\r
-\r
- hr = StorageImpl_Construct(\r
- newStorage,\r
- hFile,\r
- pwcsName,\r
- NULL,\r
- grfMode,\r
- TRUE,\r
- TRUE);\r
-\r
- if (FAILED(hr))\r
- {\r
- HeapFree(GetProcessHeap(), 0, newStorage);\r
- goto end;\r
- }\r
-\r
- /*\r
- * Get an "out" pointer for the caller.\r
- */\r
- hr = StorageBaseImpl_QueryInterface(\r
- (IStorage*)newStorage,\r
- (REFIID)&IID_IStorage,\r
- (void**)ppstgOpen);\r
-end:\r
- TRACE("<-- %p r = %08lx\n", *ppstgOpen, hr);\r
-\r
- return hr;\r
-}\r
-\r
-/******************************************************************************\r
- * StgCreateStorageEx [OLE32.@]\r
- */\r
-HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)\r
-{\r
- TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),\r
- grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);\r
-\r
- if (stgfmt != STGFMT_FILE && grfAttrs != 0)\r
- {\r
- ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");\r
- return STG_E_INVALIDPARAMETER; \r
- }\r
-\r
- if (stgfmt != STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)\r
- {\r
- ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");\r
- return STG_E_INVALIDPARAMETER; \r
- }\r
-\r
- if (stgfmt == STGFMT_FILE)\r
- {\r
- ERR("Cannot use STGFMT_FILE - this is NTFS only\n"); \r
- return STG_E_INVALIDPARAMETER;\r
- }\r
-\r
- if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)\r
- {\r
- FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");\r
- return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen); \r
- }\r
-\r
- ERR("Invalid stgfmt argument\n");\r
- return STG_E_INVALIDPARAMETER;\r
-}\r
-\r
-/******************************************************************************\r
- * StgCreatePropSetStg [OLE32.@]\r
- */\r
-HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,\r
- IPropertySetStorage **ppPropSetStg)\r
-{\r
- HRESULT hr;\r
-\r
- TRACE("(%p, 0x%lx, %p): stub\n", pstg, reserved, ppPropSetStg);\r
- if (reserved)\r
- hr = STG_E_INVALIDPARAMETER;\r
- else\r
- hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,\r
- (void**)ppPropSetStg);\r
- return hr;\r
-}\r
-\r
-/******************************************************************************\r
- * StgOpenStorageEx [OLE32.@]\r
- */\r
-HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)\r
-{\r
- TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),\r
- grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);\r
-\r
- if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)\r
- {\r
- ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");\r
- return STG_E_INVALIDPARAMETER; \r
- }\r
-\r
- if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)\r
- {\r
- ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");\r
- return STG_E_INVALIDPARAMETER; \r
- }\r
-\r
- if (stgfmt == STGFMT_FILE)\r
- {\r
- ERR("Cannot use STGFMT_FILE - this is NTFS only\n"); \r
- return STG_E_INVALIDPARAMETER;\r
- }\r
-\r
- if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE || stgfmt == STGFMT_ANY)\r
- {\r
- if (stgfmt == STGFMT_ANY) \r
- WARN("STGFMT_ANY assuming storage\n");\r
- FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");\r
- return StgOpenStorage(pwcsName, NULL, grfMode, (SNB)NULL, 0, (IStorage **)ppObjectOpen); \r
- }\r
-\r
- ERR("Invalid stgfmt argument\n");\r
- return STG_E_INVALIDPARAMETER;\r
-}\r
-\r
-\r
-/******************************************************************************\r
- * StgOpenStorage [OLE32.@]\r
- */\r
-HRESULT WINAPI StgOpenStorage(\r
- const OLECHAR *pwcsName,\r
- IStorage *pstgPriority,\r
- DWORD grfMode,\r
- SNB snbExclude,\r
- DWORD reserved,\r
- IStorage **ppstgOpen)\r
-{\r
- StorageImpl* newStorage = 0;\r
- HRESULT hr = S_OK;\r
- HANDLE hFile = 0;\r
- DWORD shareMode;\r
- DWORD accessMode;\r
- WCHAR fullname[MAX_PATH];\r
- DWORD length;\r
-\r
- TRACE("(%s, %p, %lx, %p, %ld, %p)\n",\r
- debugstr_w(pwcsName), pstgPriority, grfMode,\r
- snbExclude, reserved, ppstgOpen);\r
-\r
- /*\r
- * Perform sanity checks\r
- */\r
- if (pwcsName == 0)\r
- {\r
- hr = STG_E_INVALIDNAME;\r
- goto end;\r
- }\r
-\r
- if (ppstgOpen == 0)\r
- {\r
- hr = STG_E_INVALIDPOINTER;\r
- goto end;\r
- }\r
-\r
- if (reserved)\r
- {\r
- hr = STG_E_INVALIDPARAMETER;\r
- goto end;\r
- }\r
-\r
- /*\r
- * Validate the sharing mode\r
- */\r
- switch(STGM_SHARE_MODE(grfMode))\r
- {\r
- case STGM_SHARE_EXCLUSIVE:\r
- case STGM_SHARE_DENY_WRITE:\r
- break;\r
- default:\r
- hr = STG_E_INVALIDFLAG;\r
- goto end;\r
- }\r
-\r
- /*\r
- * Validate the STGM flags\r
- */\r
- if ( FAILED( validateSTGM(grfMode) ) ||\r
- (grfMode&STGM_CREATE))\r
- {\r
- hr = STG_E_INVALIDFLAG;\r
- goto end;\r
- }\r
-\r
- /* shared reading requires transacted mode */\r
- if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&\r
- STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&\r
- !(grfMode&STGM_TRANSACTED) )\r
- {\r
- hr = STG_E_INVALIDFLAG;\r
- goto end;\r
- }\r
-\r
- /*\r
- * Interpret the STGM value grfMode\r
- */\r
- shareMode = GetShareModeFromSTGM(grfMode);\r
- accessMode = GetAccessModeFromSTGM(grfMode);\r
-\r
- /*\r
- * Initialize the "out" parameter.\r
- */\r
- *ppstgOpen = 0;\r
-\r
- hFile = CreateFileW( pwcsName,\r
- accessMode,\r
- shareMode,\r
- NULL,\r
- OPEN_EXISTING,\r
- FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,\r
- 0);\r
-\r
- if (hFile==INVALID_HANDLE_VALUE)\r
- {\r
- DWORD last_error = GetLastError();\r
-\r
- hr = E_FAIL;\r
-\r
- switch (last_error)\r
- {\r
- case ERROR_FILE_NOT_FOUND:\r
- hr = STG_E_FILENOTFOUND;\r
- break;\r
-\r
- case ERROR_PATH_NOT_FOUND:\r
- hr = STG_E_PATHNOTFOUND;\r
- break;\r
-\r
- case ERROR_ACCESS_DENIED:\r
- case ERROR_WRITE_PROTECT:\r
- hr = STG_E_ACCESSDENIED;\r
- break;\r
-\r
- case ERROR_SHARING_VIOLATION:\r
- hr = STG_E_SHAREVIOLATION;\r
- break;\r
-\r
- default:\r
- hr = E_FAIL;\r
- }\r
-\r
- goto end;\r
- }\r
-\r
- /*\r
- * Refuse to open the file if it's too small to be a structured storage file\r
- * FIXME: verify the file when reading instead of here\r
- */\r
- length = GetFileSize(hFile, NULL);\r
- if (length < 0x100)\r
- {\r
- CloseHandle(hFile);\r
- hr = STG_E_FILEALREADYEXISTS;\r
- goto end;\r
- }\r
-\r
- /*\r
- * Allocate and initialize the new IStorage32object.\r
- */\r
- newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));\r
-\r
- if (newStorage == 0)\r
- {\r
- hr = STG_E_INSUFFICIENTMEMORY;\r
- goto end;\r
- }\r
-\r
- /* if the file's length was zero, initialize the storage */\r
- hr = StorageImpl_Construct(\r
- newStorage,\r
- hFile,\r
- pwcsName,\r
- NULL,\r
- grfMode,\r
- TRUE,\r
- FALSE );\r
-\r
- if (FAILED(hr))\r
- {\r
- HeapFree(GetProcessHeap(), 0, newStorage);\r
- /*\r
- * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS\r
- */\r
- if(hr == STG_E_INVALIDHEADER)\r
- hr = STG_E_FILEALREADYEXISTS;\r
- goto end;\r
- }\r
-\r
- /* prepare the file name string given in lieu of the root property name */\r
- GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);\r
- memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);\r
- newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';\r
-\r
- /*\r
- * Get an "out" pointer for the caller.\r
- */\r
- hr = StorageBaseImpl_QueryInterface(\r
- (IStorage*)newStorage,\r
- (REFIID)&IID_IStorage,\r
- (void**)ppstgOpen);\r
-\r
-end:\r
- TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);\r
- return hr;\r
-}\r
-\r
-/******************************************************************************\r
- * StgCreateDocfileOnILockBytes [OLE32.@]\r
- */\r
-HRESULT WINAPI StgCreateDocfileOnILockBytes(\r
- ILockBytes *plkbyt,\r
- DWORD grfMode,\r
- DWORD reserved,\r
- IStorage** ppstgOpen)\r
-{\r
- StorageImpl* newStorage = 0;\r
- HRESULT hr = S_OK;\r
-\r
- /*\r
- * Validate the parameters\r
- */\r
- if ((ppstgOpen == 0) || (plkbyt == 0))\r
- return STG_E_INVALIDPOINTER;\r
-\r
- /*\r
- * Allocate and initialize the new IStorage object.\r
- */\r
- newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));\r
-\r
- if (newStorage == 0)\r
- return STG_E_INSUFFICIENTMEMORY;\r
-\r
- hr = StorageImpl_Construct(\r
- newStorage,\r
- 0,\r
- 0,\r
- plkbyt,\r
- grfMode,\r
- FALSE,\r
- TRUE);\r
-\r
- if (FAILED(hr))\r
- {\r
- HeapFree(GetProcessHeap(), 0, newStorage);\r
- return hr;\r
- }\r
-\r
- /*\r
- * Get an "out" pointer for the caller.\r
- */\r
- hr = StorageBaseImpl_QueryInterface(\r
- (IStorage*)newStorage,\r
- (REFIID)&IID_IStorage,\r
- (void**)ppstgOpen);\r
-\r
- return hr;\r
-}\r
-\r
-/******************************************************************************\r
- * StgOpenStorageOnILockBytes [OLE32.@]\r
- */\r
-HRESULT WINAPI StgOpenStorageOnILockBytes(\r
- ILockBytes *plkbyt,\r
- IStorage *pstgPriority,\r
- DWORD grfMode,\r
- SNB snbExclude,\r
- DWORD reserved,\r
- IStorage **ppstgOpen)\r
-{\r
- StorageImpl* newStorage = 0;\r
- HRESULT hr = S_OK;\r
-\r
- /*\r
- * Perform a sanity check\r
- */\r
- if ((plkbyt == 0) || (ppstgOpen == 0))\r
- return STG_E_INVALIDPOINTER;\r
-\r
- /*\r
- * Validate the STGM flags\r
- */\r
- if ( FAILED( validateSTGM(grfMode) ))\r
- return STG_E_INVALIDFLAG;\r
-\r
- /*\r
- * Initialize the "out" parameter.\r
- */\r
- *ppstgOpen = 0;\r
-\r
- /*\r
- * Allocate and initialize the new IStorage object.\r
- */\r
- newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));\r
-\r
- if (newStorage == 0)\r
- return STG_E_INSUFFICIENTMEMORY;\r
-\r
- hr = StorageImpl_Construct(\r
- newStorage,\r
- 0,\r
- 0,\r
- plkbyt,\r
- grfMode,\r
- FALSE,\r
- FALSE);\r
-\r
- if (FAILED(hr))\r
- {\r
- HeapFree(GetProcessHeap(), 0, newStorage);\r
- return hr;\r
- }\r
-\r
- /*\r
- * Get an "out" pointer for the caller.\r
- */\r
- hr = StorageBaseImpl_QueryInterface(\r
- (IStorage*)newStorage,\r
- (REFIID)&IID_IStorage,\r
- (void**)ppstgOpen);\r
-\r
- return hr;\r
-}\r
-\r
-/******************************************************************************\r
- * StgSetTimes [ole32.@]\r
- * StgSetTimes [OLE32.@]\r
- *\r
- *\r
- */\r
-HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,\r
- FILETIME const *patime, FILETIME const *pmtime)\r
-{\r
- IStorage *stg = NULL;\r
- HRESULT r;\r
- \r
- TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);\r
-\r
- r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,\r
- 0, 0, &stg);\r
- if( SUCCEEDED(r) )\r
- {\r
- r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);\r
- IStorage_Release(stg);\r
- }\r
-\r
- return r;\r
-}\r
-\r
-/******************************************************************************\r
- * StgIsStorageILockBytes [OLE32.@]\r
- *\r
- * Determines if the ILockBytes contains a storage object.\r
- */\r
-HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)\r
-{\r
- BYTE sig[8];\r
- ULARGE_INTEGER offset;\r
-\r
- offset.u.HighPart = 0;\r
- offset.u.LowPart = 0;\r
-\r
- ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);\r
-\r
- if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)\r
- return S_OK;\r
-\r
- return S_FALSE;\r
-}\r
-\r
-/******************************************************************************\r
- * WriteClassStg [OLE32.@]\r
- *\r
- * This method will store the specified CLSID in the specified storage object\r
- */\r
-HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)\r
-{\r
- HRESULT hRes;\r
-\r
- assert(pStg != 0);\r
-\r
- hRes = IStorage_SetClass(pStg, rclsid);\r
-\r
- return hRes;\r
-}\r
-\r
-/***********************************************************************\r
- * ReadClassStg (OLE32.@)\r
- *\r
- * This method reads the CLSID previously written to a storage object with the WriteClassStg.\r
- */\r
-HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){\r
-\r
- STATSTG pstatstg;\r
- HRESULT hRes;\r
-\r
- TRACE("()\n");\r
-\r
- if(pclsid==NULL)\r
- return E_POINTER;\r
- /*\r
- * read a STATSTG structure (contains the clsid) from the storage\r
- */\r
- hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);\r
-\r
- if(SUCCEEDED(hRes))\r
- *pclsid=pstatstg.clsid;\r
-\r
- return hRes;\r
-}\r
-\r
-/***********************************************************************\r
- * OleLoadFromStream (OLE32.@)\r
- *\r
- * This function loads an object from stream\r
- */\r
-HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)\r
-{\r
- CLSID clsid;\r
- HRESULT res;\r
- LPPERSISTSTREAM xstm;\r
-\r
- TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);\r
-\r
- res=ReadClassStm(pStm,&clsid);\r
- if (!SUCCEEDED(res))\r
- return res;\r
- res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);\r
- if (!SUCCEEDED(res))\r
- return res;\r
- res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);\r
- if (!SUCCEEDED(res)) {\r
- IUnknown_Release((IUnknown*)*ppvObj);\r
- return res;\r
- }\r
- res=IPersistStream_Load(xstm,pStm);\r
- IPersistStream_Release(xstm);\r
- /* FIXME: all refcounts ok at this point? I think they should be:\r
- * pStm : unchanged\r
- * ppvObj : 1\r
- * xstm : 0 (released)\r
- */\r
- return res;\r
-}\r
-\r
-/***********************************************************************\r
- * OleSaveToStream (OLE32.@)\r
- *\r
- * This function saves an object with the IPersistStream interface on it\r
- * to the specified stream.\r
- */\r
-HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)\r
-{\r
-\r
- CLSID clsid;\r
- HRESULT res;\r
-\r
- TRACE("(%p,%p)\n",pPStm,pStm);\r
-\r
- res=IPersistStream_GetClassID(pPStm,&clsid);\r
-\r
- if (SUCCEEDED(res)){\r
-\r
- res=WriteClassStm(pStm,&clsid);\r
-\r
- if (SUCCEEDED(res))\r
-\r
- res=IPersistStream_Save(pPStm,pStm,TRUE);\r
- }\r
-\r
- TRACE("Finished Save\n");\r
- return res;\r
-}\r
-\r
-/****************************************************************************\r
- * This method validate a STGM parameter that can contain the values below\r
- *\r
- * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.\r
- * The stgm values contained in 0xffff0000 are bitmasks.\r
- *\r
- * STGM_DIRECT 0x00000000\r
- * STGM_TRANSACTED 0x00010000\r
- * STGM_SIMPLE 0x08000000\r
- *\r
- * STGM_READ 0x00000000\r
- * STGM_WRITE 0x00000001\r
- * STGM_READWRITE 0x00000002\r
- *\r
- * STGM_SHARE_DENY_NONE 0x00000040\r
- * STGM_SHARE_DENY_READ 0x00000030\r
- * STGM_SHARE_DENY_WRITE 0x00000020\r
- * STGM_SHARE_EXCLUSIVE 0x00000010\r
- *\r
- * STGM_PRIORITY 0x00040000\r
- * STGM_DELETEONRELEASE 0x04000000\r
- *\r
- * STGM_CREATE 0x00001000\r
- * STGM_CONVERT 0x00020000\r
- * STGM_FAILIFTHERE 0x00000000\r
- *\r
- * STGM_NOSCRATCH 0x00100000\r
- * STGM_NOSNAPSHOT 0x00200000\r
- */\r
-static HRESULT validateSTGM(DWORD stgm)\r
-{\r
- DWORD access = STGM_ACCESS_MODE(stgm);\r
- DWORD share = STGM_SHARE_MODE(stgm);\r
- DWORD create = STGM_CREATE_MODE(stgm);\r
-\r
- if (stgm&~STGM_KNOWN_FLAGS)\r
- {\r
- ERR("unknown flags %08lx\n", stgm);\r
- return E_FAIL;\r
- }\r
-\r
- switch (access)\r
- {\r
- case STGM_READ:\r
- case STGM_WRITE:\r
- case STGM_READWRITE:\r
- break;\r
- default:\r
- return E_FAIL;\r
- }\r
-\r
- switch (share)\r
- {\r
- case STGM_SHARE_DENY_NONE:\r
- case STGM_SHARE_DENY_READ:\r
- case STGM_SHARE_DENY_WRITE:\r
- case STGM_SHARE_EXCLUSIVE:\r
- break;\r
- default:\r
- return E_FAIL;\r
- }\r
-\r
- switch (create)\r
- {\r
- case STGM_CREATE:\r
- case STGM_FAILIFTHERE:\r
- break;\r
- default:\r
- return E_FAIL;\r
- }\r
-\r
- /*\r
- * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE\r
- */\r
- if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )\r
- return E_FAIL;\r
-\r
- /*\r
- * STGM_CREATE | STGM_CONVERT\r
- * if both are false, STGM_FAILIFTHERE is set to TRUE\r
- */\r
- if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )\r
- return E_FAIL;\r
-\r
- /*\r
- * STGM_NOSCRATCH requires STGM_TRANSACTED\r
- */\r
- if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )\r
- return E_FAIL;\r
-\r
- /*\r
- * STGM_NOSNAPSHOT requires STGM_TRANSACTED and\r
- * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`\r
- */\r
- if ( (stgm & STGM_NOSNAPSHOT) &&\r
- (!(stgm & STGM_TRANSACTED) ||\r
- share == STGM_SHARE_EXCLUSIVE ||\r
- share == STGM_SHARE_DENY_WRITE) )\r
- return E_FAIL;\r
-\r
- return S_OK;\r
-}\r
-\r
-/****************************************************************************\r
- * GetShareModeFromSTGM\r
- *\r
- * This method will return a share mode flag from a STGM value.\r
- * The STGM value is assumed valid.\r
- */\r
-static DWORD GetShareModeFromSTGM(DWORD stgm)\r
-{\r
- switch (STGM_SHARE_MODE(stgm))\r
- {\r
- case STGM_SHARE_DENY_NONE:\r
- return FILE_SHARE_READ | FILE_SHARE_WRITE;\r
- case STGM_SHARE_DENY_READ:\r
- return FILE_SHARE_WRITE;\r
- case STGM_SHARE_DENY_WRITE:\r
- return FILE_SHARE_READ;\r
- case STGM_SHARE_EXCLUSIVE:\r
- return 0;\r
- }\r
- ERR("Invalid share mode!\n");\r
- assert(0);\r
- return 0;\r
-}\r
-\r
-/****************************************************************************\r
- * GetAccessModeFromSTGM\r
- *\r
- * This method will return an access mode flag from a STGM value.\r
- * The STGM value is assumed valid.\r
- */\r
-static DWORD GetAccessModeFromSTGM(DWORD stgm)\r
-{\r
- switch (STGM_ACCESS_MODE(stgm))\r
- {\r
- case STGM_READ:\r
- return GENERIC_READ;\r
- case STGM_WRITE:\r
- case STGM_READWRITE:\r
- return GENERIC_READ | GENERIC_WRITE;\r
- }\r
- ERR("Invalid access mode!\n");\r
- assert(0);\r
- return 0;\r
-}\r
-\r
-/****************************************************************************\r
- * GetCreationModeFromSTGM\r
- *\r
- * This method will return a creation mode flag from a STGM value.\r
- * The STGM value is assumed valid.\r
- */\r
-static DWORD GetCreationModeFromSTGM(DWORD stgm)\r
-{\r
- switch(STGM_CREATE_MODE(stgm))\r
- {\r
- case STGM_CREATE:\r
- return CREATE_ALWAYS;\r
- case STGM_CONVERT:\r
- FIXME("STGM_CONVERT not implemented!\n");\r
- return CREATE_NEW;\r
- case STGM_FAILIFTHERE:\r
- return CREATE_NEW;\r
- }\r
- ERR("Invalid create mode!\n");\r
- assert(0);\r
- return 0;\r
-}\r
-\r
-\r
-/*************************************************************************\r
- * OLECONVERT_LoadOLE10 [Internal]\r
- *\r
- * Loads the OLE10 STREAM to memory\r
- *\r
- * PARAMS\r
- * pOleStream [I] The OLESTREAM\r
- * pData [I] Data Structure for the OLESTREAM Data\r
- *\r
- * RETURNS\r
- * Success: S_OK\r
- * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get\r
- * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide\r
- *\r
- * NOTES\r
- * This function is used by OleConvertOLESTREAMToIStorage only.\r
- *\r
- * Memory allocated for pData must be freed by the caller\r
- */\r
-HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)\r
-{\r
- DWORD dwSize;\r
- HRESULT hRes = S_OK;\r
- int nTryCnt=0;\r
- int max_try = 6;\r
-\r
- pData->pData = NULL;\r
- pData->pstrOleObjFileName = (CHAR *) NULL;\r
-\r
- for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)\r
- {\r
- /* Get the OleID */\r
- dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));\r
- if(dwSize != sizeof(pData->dwOleID))\r
- {\r
- hRes = CONVERT10_E_OLESTREAM_GET;\r
- }\r
- else if(pData->dwOleID != OLESTREAM_ID)\r
- {\r
- hRes = CONVERT10_E_OLESTREAM_FMT;\r
- }\r
- else\r
- {\r
- hRes = S_OK;\r
- break;\r
- }\r
- }\r
-\r
- if(hRes == S_OK)\r
- {\r
- /* Get the TypeID...more info needed for this field */\r
- dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));\r
- if(dwSize != sizeof(pData->dwTypeID))\r
- {\r
- hRes = CONVERT10_E_OLESTREAM_GET;\r
- }\r
- }\r
- if(hRes == S_OK)\r
- {\r
- if(pData->dwTypeID != 0)\r
- {\r
- /* Get the length of the OleTypeName */\r
- dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));\r
- if(dwSize != sizeof(pData->dwOleTypeNameLength))\r
- {\r
- hRes = CONVERT10_E_OLESTREAM_GET;\r
- }\r
-\r
- if(hRes == S_OK)\r
- {\r
- if(pData->dwOleTypeNameLength > 0)\r
- {\r
- /* Get the OleTypeName */\r
- dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);\r
- if(dwSize != pData->dwOleTypeNameLength)\r
- {\r
- hRes = CONVERT10_E_OLESTREAM_GET;\r
- }\r
- }\r
- }\r
- if(bStrem1)\r
- {\r
- dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));\r
- if(dwSize != sizeof(pData->dwOleObjFileNameLength))\r
- {\r
- hRes = CONVERT10_E_OLESTREAM_GET;\r
- }\r
- if(hRes == S_OK)\r
- {\r
- if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */\r
- pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);\r
- pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);\r
- if(pData->pstrOleObjFileName)\r
- {\r
- dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);\r
- if(dwSize != pData->dwOleObjFileNameLength)\r
- {\r
- hRes = CONVERT10_E_OLESTREAM_GET;\r
- }\r
- }\r
- else\r
- hRes = CONVERT10_E_OLESTREAM_GET;\r
- }\r
- }\r
- else\r
- {\r
- /* Get the Width of the Metafile */\r
- dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));\r
- if(dwSize != sizeof(pData->dwMetaFileWidth))\r
- {\r
- hRes = CONVERT10_E_OLESTREAM_GET;\r
- }\r
- if(hRes == S_OK)\r
- {\r
- /* Get the Height of the Metafile */\r
- dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));\r
- if(dwSize != sizeof(pData->dwMetaFileHeight))\r
- {\r
- hRes = CONVERT10_E_OLESTREAM_GET;\r
- }\r
- }\r
- }\r
- if(hRes == S_OK)\r
- {\r
- /* Get the Length of the Data */\r
- dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));\r
- if(dwSize != sizeof(pData->dwDataLength))\r
- {\r
- hRes = CONVERT10_E_OLESTREAM_GET;\r
- }\r
- }\r
-\r
- if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */\r
- {\r
- if(!bStrem1) /* if it is a second OLE stream data */\r
- {\r
- pData->dwDataLength -= 8;\r
- dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));\r
- if(dwSize != sizeof(pData->strUnknown))\r
- {\r
- hRes = CONVERT10_E_OLESTREAM_GET;\r
- }\r
- }\r
- }\r
- if(hRes == S_OK)\r
- {\r
- if(pData->dwDataLength > 0)\r
- {\r
- pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);\r
-\r
- /* Get Data (ex. IStorage, Metafile, or BMP) */\r
- if(pData->pData)\r
- {\r
- dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);\r
- if(dwSize != pData->dwDataLength)\r
- {\r
- hRes = CONVERT10_E_OLESTREAM_GET;\r
- }\r
- }\r
- else\r
- {\r
- hRes = CONVERT10_E_OLESTREAM_GET;\r
- }\r
- }\r
- }\r
- }\r
- }\r
- return hRes;\r
-}\r
-\r
-/*************************************************************************\r
- * OLECONVERT_SaveOLE10 [Internal]\r
- *\r
- * Saves the OLE10 STREAM From memory\r
- *\r
- * PARAMS\r
- * pData [I] Data Structure for the OLESTREAM Data\r
- * pOleStream [I] The OLESTREAM to save\r
- *\r
- * RETURNS\r
- * Success: S_OK\r
- * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put\r
- *\r
- * NOTES\r
- * This function is used by OleConvertIStorageToOLESTREAM only.\r
- *\r
- */\r
-HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)\r
-{\r
- DWORD dwSize;\r
- HRESULT hRes = S_OK;\r
-\r
-\r
- /* Set the OleID */\r
- dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));\r
- if(dwSize != sizeof(pData->dwOleID))\r
- {\r
- hRes = CONVERT10_E_OLESTREAM_PUT;\r
- }\r
-\r
- if(hRes == S_OK)\r
- {\r
- /* Set the TypeID */\r
- dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));\r
- if(dwSize != sizeof(pData->dwTypeID))\r
- {\r
- hRes = CONVERT10_E_OLESTREAM_PUT;\r
- }\r
- }\r
-\r
- if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)\r
- {\r
- /* Set the Length of the OleTypeName */\r
- dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));\r
- if(dwSize != sizeof(pData->dwOleTypeNameLength))\r
- {\r
- hRes = CONVERT10_E_OLESTREAM_PUT;\r
- }\r
-\r
- if(hRes == S_OK)\r
- {\r
- if(pData->dwOleTypeNameLength > 0)\r
- {\r
- /* Set the OleTypeName */\r
- dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);\r
- if(dwSize != pData->dwOleTypeNameLength)\r
- {\r
- hRes = CONVERT10_E_OLESTREAM_PUT;\r
- }\r
- }\r
- }\r
-\r
- if(hRes == S_OK)\r
- {\r
- /* Set the width of the Metafile */\r
- dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));\r
- if(dwSize != sizeof(pData->dwMetaFileWidth))\r
- {\r
- hRes = CONVERT10_E_OLESTREAM_PUT;\r
- }\r
- }\r
-\r
- if(hRes == S_OK)\r
- {\r
- /* Set the height of the Metafile */\r
- dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));\r
- if(dwSize != sizeof(pData->dwMetaFileHeight))\r
- {\r
- hRes = CONVERT10_E_OLESTREAM_PUT;\r
- }\r
- }\r
-\r
- if(hRes == S_OK)\r
- {\r
- /* Set the length of the Data */\r
- dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));\r
- if(dwSize != sizeof(pData->dwDataLength))\r
- {\r
- hRes = CONVERT10_E_OLESTREAM_PUT;\r
- }\r
- }\r
-\r
- if(hRes == S_OK)\r
- {\r
- if(pData->dwDataLength > 0)\r
- {\r
- /* Set the Data (eg. IStorage, Metafile, Bitmap) */\r
- dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);\r
- if(dwSize != pData->dwDataLength)\r
- {\r
- hRes = CONVERT10_E_OLESTREAM_PUT;\r
- }\r
- }\r
- }\r
- }\r
- return hRes;\r
-}\r
-\r
-/*************************************************************************\r
- * OLECONVERT_GetOLE20FromOLE10[Internal]\r
- *\r
- * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,\r
- * opens it, and copies the content to the dest IStorage for\r
- * OleConvertOLESTREAMToIStorage\r
- *\r
- *\r
- * PARAMS\r
- * pDestStorage [I] The IStorage to copy the data to\r
- * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM\r
- * nBufferLength [I] The size of the buffer\r
- *\r
- * RETURNS\r
- * Nothing\r
- *\r
- * NOTES\r
- *\r
- *\r
- */\r
-void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)\r
-{\r
- HRESULT hRes;\r
- HANDLE hFile;\r
- IStorage *pTempStorage;\r
- DWORD dwNumOfBytesWritten;\r
- WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];\r
- static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};\r
-\r
- /* Create a temp File */\r
- GetTempPathW(MAX_PATH, wstrTempDir);\r
- GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);\r
- hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);\r
-\r
- if(hFile != INVALID_HANDLE_VALUE)\r
- {\r
- /* Write IStorage Data to File */\r
- WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);\r
- CloseHandle(hFile);\r
-\r
- /* Open and copy temp storage to the Dest Storage */\r
- hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);\r
- if(hRes == S_OK)\r
- {\r
- hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);\r
- StorageBaseImpl_Release(pTempStorage);\r
- }\r
- DeleteFileW(wstrTempFile);\r
- }\r
-}\r
-\r
-\r
-/*************************************************************************\r
- * OLECONVERT_WriteOLE20ToBuffer [Internal]\r
- *\r
- * Saves the OLE10 STREAM From memory\r
- *\r
- * PARAMS\r
- * pStorage [I] The Src IStorage to copy\r
- * pData [I] The Dest Memory to write to.\r
- *\r
- * RETURNS\r
- * The size in bytes allocated for pData\r
- *\r
- * NOTES\r
- * Memory allocated for pData must be freed by the caller\r
- *\r
- * Used by OleConvertIStorageToOLESTREAM only.\r
- *\r
- */\r
-DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)\r
-{\r
- HANDLE hFile;\r
- HRESULT hRes;\r
- DWORD nDataLength = 0;\r
- IStorage *pTempStorage;\r
- WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];\r
- static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};\r
-\r
- *pData = NULL;\r
-\r
- /* Create temp Storage */\r
- GetTempPathW(MAX_PATH, wstrTempDir);\r
- GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);\r
- hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);\r
-\r
- if(hRes == S_OK)\r
- {\r
- /* Copy Src Storage to the Temp Storage */\r
- StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);\r
- StorageBaseImpl_Release(pTempStorage);\r
-\r
- /* Open Temp Storage as a file and copy to memory */\r
- hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);\r
- if(hFile != INVALID_HANDLE_VALUE)\r
- {\r
- nDataLength = GetFileSize(hFile, NULL);\r
- *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);\r
- ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);\r
- CloseHandle(hFile);\r
- }\r
- DeleteFileW(wstrTempFile);\r
- }\r
- return nDataLength;\r
-}\r
-\r
-/*************************************************************************\r
- * OLECONVERT_CreateOleStream [Internal]\r
- *\r
- * Creates the "\001OLE" stream in the IStorage if necessary.\r
- *\r
- * PARAMS\r
- * pStorage [I] Dest storage to create the stream in\r
- *\r
- * RETURNS\r
- * Nothing\r
- *\r
- * NOTES\r
- * This function is used by OleConvertOLESTREAMToIStorage only.\r
- *\r
- * This stream is still unknown, MS Word seems to have extra data\r
- * but since the data is stored in the OLESTREAM there should be\r
- * no need to recreate the stream. If the stream is manually\r
- * deleted it will create it with this default data.\r
- *\r
- */\r
-void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)\r
-{\r
- HRESULT hRes;\r
- IStream *pStream;\r
- static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};\r
- BYTE pOleStreamHeader [] =\r
- {\r
- 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,\r
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
- 0x00, 0x00, 0x00, 0x00\r
- };\r
-\r
- /* Create stream if not present */\r
- hRes = IStorage_CreateStream(pStorage, wstrStreamName,\r
- STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );\r
-\r
- if(hRes == S_OK)\r
- {\r
- /* Write default Data */\r
- hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);\r
- IStream_Release(pStream);\r
- }\r
-}\r
-\r
-/* write a string to a stream, preceded by its length */\r
-static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )\r
-{\r
- HRESULT r;\r
- LPSTR str;\r
- DWORD len = 0;\r
-\r
- if( string )\r
- len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);\r
- r = IStream_Write( stm, &len, sizeof(len), NULL);\r
- if( FAILED( r ) )\r
- return r;\r
- if(len == 0)\r
- return r;\r
- str = CoTaskMemAlloc( len );\r
- WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);\r
- r = IStream_Write( stm, str, len, NULL);\r
- CoTaskMemFree( str );\r
- return r;\r
-}\r
-\r
-/* read a string preceded by its length from a stream */\r
-static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )\r
-{\r
- HRESULT r;\r
- DWORD len, count = 0;\r
- LPSTR str;\r
- LPWSTR wstr;\r
-\r
- r = IStream_Read( stm, &len, sizeof(len), &count );\r
- if( FAILED( r ) )\r
- return r;\r
- if( count != sizeof(len) )\r
- return E_OUTOFMEMORY;\r
-\r
- TRACE("%ld bytes\n",len);\r
- \r
- str = CoTaskMemAlloc( len );\r
- if( !str )\r
- return E_OUTOFMEMORY;\r
- count = 0;\r
- r = IStream_Read( stm, str, len, &count );\r
- if( FAILED( r ) )\r
- return r;\r
- if( count != len )\r
- {\r
- CoTaskMemFree( str );\r
- return E_OUTOFMEMORY;\r
- }\r
-\r
- TRACE("Read string %s\n",debugstr_an(str,len));\r
-\r
- len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );\r
- wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );\r
- if( wstr )\r
- MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );\r
- CoTaskMemFree( str );\r
-\r
- *string = wstr;\r
-\r
- return r;\r
-}\r
-\r
-\r
-static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,\r
- LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )\r
-{\r
- IStream *pstm;\r
- HRESULT r = S_OK;\r
- static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};\r
-\r
- static const BYTE unknown1[12] =\r
- { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,\r
- 0xFF, 0xFF, 0xFF, 0xFF};\r
- static const BYTE unknown2[16] =\r
- { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,\r
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };\r
-\r
- TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),\r
- debugstr_w(lpszUserType), debugstr_w(szClipName),\r
- debugstr_w(szProgIDName));\r
-\r
- /* Create a CompObj stream if it doesn't exist */\r
- r = IStorage_CreateStream(pstg, szwStreamName,\r
- STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );\r
- if( FAILED (r) )\r
- return r;\r
-\r
- /* Write CompObj Structure to stream */\r
- r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);\r
-\r
- if( SUCCEEDED( r ) )\r
- r = WriteClassStm( pstm, clsid );\r
-\r
- if( SUCCEEDED( r ) )\r
- r = STREAM_WriteString( pstm, lpszUserType );\r
- if( SUCCEEDED( r ) )\r
- r = STREAM_WriteString( pstm, szClipName );\r
- if( SUCCEEDED( r ) )\r
- r = STREAM_WriteString( pstm, szProgIDName );\r
- if( SUCCEEDED( r ) )\r
- r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);\r
-\r
- IStream_Release( pstm );\r
-\r
- return r;\r
-}\r
-\r
-/***********************************************************************\r
- * WriteFmtUserTypeStg (OLE32.@)\r
- */\r
-HRESULT WINAPI WriteFmtUserTypeStg(\r
- LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)\r
-{\r
- HRESULT r;\r
- WCHAR szwClipName[0x40];\r
- CLSID clsid = CLSID_NULL;\r
- LPWSTR wstrProgID = NULL;\r
- DWORD n;\r
-\r
- TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));\r
-\r
- /* get the clipboard format name */\r
- n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );\r
- szwClipName[n]=0;\r
-\r
- TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));\r
-\r
- /* FIXME: There's room to save a CLSID and its ProgID, but\r
- the CLSID is not looked up in the registry and in all the\r
- tests I wrote it was CLSID_NULL. Where does it come from?\r
- */\r
-\r
- /* get the real program ID. This may fail, but that's fine */\r
- ProgIDFromCLSID(&clsid, &wstrProgID);\r
-\r
- TRACE("progid is %s\n",debugstr_w(wstrProgID));\r
-\r
- r = STORAGE_WriteCompObj( pstg, &clsid, \r
- lpszUserType, szwClipName, wstrProgID );\r
-\r
- CoTaskMemFree(wstrProgID);\r
-\r
- return r;\r
-}\r
-\r
-\r
-/******************************************************************************\r
- * ReadFmtUserTypeStg [OLE32.@]\r
- */\r
-HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)\r
-{\r
- HRESULT r;\r
- IStream *stm = 0;\r
- static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };\r
- unsigned char unknown1[12];\r
- unsigned char unknown2[16];\r
- DWORD count;\r
- LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;\r
- CLSID clsid;\r
-\r
- TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);\r
-\r
- r = IStorage_OpenStream( pstg, szCompObj, NULL, \r
- STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );\r
- if( FAILED ( r ) )\r
- {\r
- WARN("Failed to open stream r = %08lx\n", r);\r
- return r;\r
- }\r
-\r
- /* read the various parts of the structure */\r
- r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );\r
- if( FAILED( r ) || ( count != sizeof(unknown1) ) )\r
- goto end;\r
- r = ReadClassStm( stm, &clsid );\r
- if( FAILED( r ) )\r
- goto end;\r
-\r
- r = STREAM_ReadString( stm, &szCLSIDName );\r
- if( FAILED( r ) )\r
- goto end;\r
-\r
- r = STREAM_ReadString( stm, &szOleTypeName );\r
- if( FAILED( r ) )\r
- goto end;\r
-\r
- r = STREAM_ReadString( stm, &szProgIDName );\r
- if( FAILED( r ) )\r
- goto end;\r
-\r
- r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );\r
- if( FAILED( r ) || ( count != sizeof(unknown2) ) )\r
- goto end;\r
-\r
- /* ok, success... now we just need to store what we found */\r
- if( pcf )\r
- *pcf = RegisterClipboardFormatW( szOleTypeName );\r
- CoTaskMemFree( szOleTypeName );\r
-\r
- if( lplpszUserType )\r
- *lplpszUserType = szCLSIDName;\r
- CoTaskMemFree( szProgIDName );\r
-\r
-end:\r
- IStream_Release( stm );\r
-\r
- return r;\r
-}\r
-\r
-\r
-/*************************************************************************\r
- * OLECONVERT_CreateCompObjStream [Internal]\r
- *\r
- * Creates a "\001CompObj" is the destination IStorage if necessary.\r
- *\r
- * PARAMS\r
- * pStorage [I] The dest IStorage to create the CompObj Stream\r
- * if necessary.\r
- * strOleTypeName [I] The ProgID\r
- *\r
- * RETURNS\r
- * Success: S_OK\r
- * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream\r
- *\r
- * NOTES\r
- * This function is used by OleConvertOLESTREAMToIStorage only.\r
- *\r
- * The stream data is stored in the OLESTREAM and there should be\r
- * no need to recreate the stream. If the stream is manually\r
- * deleted it will attempt to create it by querying the registry.\r
- *\r
- *\r
- */\r
-HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)\r
-{\r
- IStream *pStream;\r
- HRESULT hStorageRes, hRes = S_OK;\r
- OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;\r
- static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};\r
- WCHAR bufferW[OLESTREAM_MAX_STR_LEN];\r
-\r
- BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};\r
- BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};\r
-\r
- /* Initialize the CompObj structure */\r
- memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));\r
- memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));\r
- memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));\r
-\r
-\r
- /* Create a CompObj stream if it doesn't exist */\r
- hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,\r
- STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );\r
- if(hStorageRes == S_OK)\r
- {\r
- /* copy the OleTypeName to the compobj struct */\r
- IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;\r
- strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);\r
-\r
- /* copy the OleTypeName to the compobj struct */\r
- /* Note: in the test made, these were Identical */\r
- IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;\r
- strcpy(IStorageCompObj.strProgIDName, strOleTypeName);\r
-\r
- /* Get the CLSID */\r
- MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,\r
- bufferW, OLESTREAM_MAX_STR_LEN );\r
- hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));\r
-\r
- if(hRes == S_OK)\r
- {\r
- HKEY hKey;\r
- LONG hErr;\r
- /* Get the CLSID Default Name from the Registry */\r
- hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);\r
- if(hErr == ERROR_SUCCESS)\r
- {\r
- char strTemp[OLESTREAM_MAX_STR_LEN];\r
- IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;\r
- hErr = RegQueryValueA(hKey, NULL, strTemp, &(IStorageCompObj.dwCLSIDNameLength));\r
- if(hErr == ERROR_SUCCESS)\r
- {\r
- strcpy(IStorageCompObj.strCLSIDName, strTemp);\r
- }\r
- RegCloseKey(hKey);\r
- }\r
- }\r
-\r
- /* Write CompObj Structure to stream */\r
- hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);\r
-\r
- WriteClassStm(pStream,&(IStorageCompObj.clsid));\r
-\r
- hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);\r
- if(IStorageCompObj.dwCLSIDNameLength > 0)\r
- {\r
- hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);\r
- }\r
- hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);\r
- if(IStorageCompObj.dwOleTypeNameLength > 0)\r
- {\r
- hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);\r
- }\r
- hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);\r
- if(IStorageCompObj.dwProgIDNameLength > 0)\r
- {\r
- hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);\r
- }\r
- hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);\r
- IStream_Release(pStream);\r
- }\r
- return hRes;\r
-}\r
-\r
-\r
-/*************************************************************************\r
- * OLECONVERT_CreateOlePresStream[Internal]\r
- *\r
- * Creates the "\002OlePres000" Stream with the Metafile data\r
- *\r
- * PARAMS\r
- * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.\r
- * dwExtentX [I] Width of the Metafile\r
- * dwExtentY [I] Height of the Metafile\r
- * pData [I] Metafile data\r
- * dwDataLength [I] Size of the Metafile data\r
- *\r
- * RETURNS\r
- * Success: S_OK\r
- * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put\r
- *\r
- * NOTES\r
- * This function is used by OleConvertOLESTREAMToIStorage only.\r
- *\r
- */\r
-void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)\r
-{\r
- HRESULT hRes;\r
- IStream *pStream;\r
- static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};\r
- BYTE pOlePresStreamHeader [] =\r
- {\r
- 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,\r
- 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,\r
- 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,\r
- 0x00, 0x00, 0x00, 0x00\r
- };\r
-\r
- BYTE pOlePresStreamHeaderEmpty [] =\r
- {\r
- 0x00, 0x00, 0x00, 0x00,\r
- 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,\r
- 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,\r
- 0x00, 0x00, 0x00, 0x00\r
- };\r
-\r
- /* Create the OlePres000 Stream */\r
- hRes = IStorage_CreateStream(pStorage, wstrStreamName,\r
- STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );\r
-\r
- if(hRes == S_OK)\r
- {\r
- DWORD nHeaderSize;\r
- OLECONVERT_ISTORAGE_OLEPRES OlePres;\r
-\r
- memset(&OlePres, 0, sizeof(OlePres));\r
- /* Do we have any metafile data to save */\r
- if(dwDataLength > 0)\r
- {\r
- memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));\r
- nHeaderSize = sizeof(pOlePresStreamHeader);\r
- }\r
- else\r
- {\r
- memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));\r
- nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);\r
- }\r
- /* Set width and height of the metafile */\r
- OlePres.dwExtentX = dwExtentX;\r
- OlePres.dwExtentY = -dwExtentY;\r
-\r
- /* Set Data and Length */\r
- if(dwDataLength > sizeof(METAFILEPICT16))\r
- {\r
- OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);\r
- OlePres.pData = &(pData[8]);\r
- }\r
- /* Save OlePres000 Data to Stream */\r
- hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);\r
- hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);\r
- hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);\r
- hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);\r
- if(OlePres.dwSize > 0)\r
- {\r
- hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);\r
- }\r
- IStream_Release(pStream);\r
- }\r
-}\r
-\r
-/*************************************************************************\r
- * OLECONVERT_CreateOle10NativeStream [Internal]\r
- *\r
- * Creates the "\001Ole10Native" Stream (should contain a BMP)\r
- *\r
- * PARAMS\r
- * pStorage [I] Dest storage to create the stream in\r
- * pData [I] Ole10 Native Data (ex. bmp)\r
- * dwDataLength [I] Size of the Ole10 Native Data\r
- *\r
- * RETURNS\r
- * Nothing\r
- *\r
- * NOTES\r
- * This function is used by OleConvertOLESTREAMToIStorage only.\r
- *\r
- * Might need to verify the data and return appropriate error message\r
- *\r
- */\r
-void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)\r
-{\r
- HRESULT hRes;\r
- IStream *pStream;\r
- static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};\r
-\r
- /* Create the Ole10Native Stream */\r
- hRes = IStorage_CreateStream(pStorage, wstrStreamName,\r
- STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );\r
-\r
- if(hRes == S_OK)\r
- {\r
- /* Write info to stream */\r
- hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);\r
- hRes = IStream_Write(pStream, pData, dwDataLength, NULL);\r
- IStream_Release(pStream);\r
- }\r
-\r
-}\r
-\r
-/*************************************************************************\r
- * OLECONVERT_GetOLE10ProgID [Internal]\r
- *\r
- * Finds the ProgID (or OleTypeID) from the IStorage\r
- *\r
- * PARAMS\r
- * pStorage [I] The Src IStorage to get the ProgID\r
- * strProgID [I] the ProgID string to get\r
- * dwSize [I] the size of the string\r
- *\r
- * RETURNS\r
- * Success: S_OK\r
- * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream\r
- *\r
- * NOTES\r
- * This function is used by OleConvertIStorageToOLESTREAM only.\r
- *\r
- *\r
- */\r
-HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)\r
-{\r
- HRESULT hRes;\r
- IStream *pStream;\r
- LARGE_INTEGER iSeekPos;\r
- OLECONVERT_ISTORAGE_COMPOBJ CompObj;\r
- static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};\r
-\r
- /* Open the CompObj Stream */\r
- hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,\r
- STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );\r
- if(hRes == S_OK)\r
- {\r
-\r
- /*Get the OleType from the CompObj Stream */\r
- iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);\r
- iSeekPos.u.HighPart = 0;\r
-\r
- IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);\r
- IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);\r
- iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;\r
- IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);\r
- IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);\r
- iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;\r
- IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);\r
-\r
- IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);\r
- if(*dwSize > 0)\r
- {\r
- IStream_Read(pStream, strProgID, *dwSize, NULL);\r
- }\r
- IStream_Release(pStream);\r
- }\r
- else\r
- {\r
- STATSTG stat;\r
- LPOLESTR wstrProgID;\r
-\r
- /* Get the OleType from the registry */\r
- REFCLSID clsid = &(stat.clsid);\r
- IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);\r
- hRes = ProgIDFromCLSID(clsid, &wstrProgID);\r
- if(hRes == S_OK)\r
- {\r
- *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);\r
- }\r
-\r
- }\r
- return hRes;\r
-}\r
-\r
-/*************************************************************************\r
- * OLECONVERT_GetOle10PresData [Internal]\r
- *\r
- * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream\r
- *\r
- * PARAMS\r
- * pStorage [I] Src IStroage\r
- * pOleStream [I] Dest OleStream Mem Struct\r
- *\r
- * RETURNS\r
- * Nothing\r
- *\r
- * NOTES\r
- * This function is used by OleConvertIStorageToOLESTREAM only.\r
- *\r
- * Memory allocated for pData must be freed by the caller\r
- *\r
- *\r
- */\r
-void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)\r
-{\r
-\r
- HRESULT hRes;\r
- IStream *pStream;\r
- static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};\r
-\r
- /* Initialize Default data for OLESTREAM */\r
- pOleStreamData[0].dwOleID = OLESTREAM_ID;\r
- pOleStreamData[0].dwTypeID = 2;\r
- pOleStreamData[1].dwOleID = OLESTREAM_ID;\r
- pOleStreamData[1].dwTypeID = 0;\r
- pOleStreamData[0].dwMetaFileWidth = 0;\r
- pOleStreamData[0].dwMetaFileHeight = 0;\r
- pOleStreamData[0].pData = NULL;\r
- pOleStreamData[1].pData = NULL;\r
-\r
- /* Open Ole10Native Stream */\r
- hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,\r
- STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );\r
- if(hRes == S_OK)\r
- {\r
-\r
- /* Read Size and Data */\r
- IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);\r
- if(pOleStreamData->dwDataLength > 0)\r
- {\r
- pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);\r
- IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);\r
- }\r
- IStream_Release(pStream);\r
- }\r
-\r
-}\r
-\r
-\r
-/*************************************************************************\r
- * OLECONVERT_GetOle20PresData[Internal]\r
- *\r
- * Converts IStorage "/002OlePres000" stream to a OLE10 Stream\r
- *\r
- * PARAMS\r
- * pStorage [I] Src IStroage\r
- * pOleStreamData [I] Dest OleStream Mem Struct\r
- *\r
- * RETURNS\r
- * Nothing\r
- *\r
- * NOTES\r
- * This function is used by OleConvertIStorageToOLESTREAM only.\r
- *\r
- * Memory allocated for pData must be freed by the caller\r
- */\r
-void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)\r
-{\r
- HRESULT hRes;\r
- IStream *pStream;\r
- OLECONVERT_ISTORAGE_OLEPRES olePress;\r
- static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};\r
-\r
- /* Initialize Default data for OLESTREAM */\r
- pOleStreamData[0].dwOleID = OLESTREAM_ID;\r
- pOleStreamData[0].dwTypeID = 2;\r
- pOleStreamData[0].dwMetaFileWidth = 0;\r
- pOleStreamData[0].dwMetaFileHeight = 0;\r
- pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));\r
- pOleStreamData[1].dwOleID = OLESTREAM_ID;\r
- pOleStreamData[1].dwTypeID = 0;\r
- pOleStreamData[1].dwOleTypeNameLength = 0;\r
- pOleStreamData[1].strOleTypeName[0] = 0;\r
- pOleStreamData[1].dwMetaFileWidth = 0;\r
- pOleStreamData[1].dwMetaFileHeight = 0;\r
- pOleStreamData[1].pData = NULL;\r
- pOleStreamData[1].dwDataLength = 0;\r
-\r
-\r
- /* Open OlePress000 stream */\r
- hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,\r
- STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );\r
- if(hRes == S_OK)\r
- {\r
- LARGE_INTEGER iSeekPos;\r
- METAFILEPICT16 MetaFilePict;\r
- static const char strMetafilePictName[] = "METAFILEPICT";\r
-\r
- /* Set the TypeID for a Metafile */\r
- pOleStreamData[1].dwTypeID = 5;\r
-\r
- /* Set the OleTypeName to Metafile */\r
- pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;\r
- strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);\r
-\r
- iSeekPos.u.HighPart = 0;\r
- iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);\r
-\r
- /* Get Presentation Data */\r
- IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);\r
- IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);\r
- IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);\r
- IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);\r
-\r
- /*Set width and Height */\r
- pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;\r
- pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;\r
- if(olePress.dwSize > 0)\r
- {\r
- /* Set Length */\r
- pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);\r
-\r
- /* Set MetaFilePict struct */\r
- MetaFilePict.mm = 8;\r
- MetaFilePict.xExt = olePress.dwExtentX;\r
- MetaFilePict.yExt = olePress.dwExtentY;\r
- MetaFilePict.hMF = 0;\r
-\r
- /* Get Metafile Data */\r
- pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);\r
- memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));\r
- IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);\r
- }\r
- IStream_Release(pStream);\r
- }\r
-}\r
-\r
-/*************************************************************************\r
- * OleConvertOLESTREAMToIStorage [OLE32.@]\r
- *\r
- * Read info on MSDN\r
- *\r
- * TODO\r
- * DVTARGETDEVICE paramenter is not handled\r
- * Still unsure of some mem fields for OLE 10 Stream\r
- * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",\r
- * and "\001OLE" streams\r
- *\r
- */\r
-HRESULT WINAPI OleConvertOLESTREAMToIStorage (\r
- LPOLESTREAM pOleStream,\r
- LPSTORAGE pstg,\r
- const DVTARGETDEVICE* ptd)\r
-{\r
- int i;\r
- HRESULT hRes=S_OK;\r
- OLECONVERT_OLESTREAM_DATA pOleStreamData[2];\r
-\r
- memset(pOleStreamData, 0, sizeof(pOleStreamData));\r
-\r
- if(ptd != NULL)\r
- {\r
- FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");\r
- }\r
-\r
- if(pstg == NULL || pOleStream == NULL)\r
- {\r
- hRes = E_INVALIDARG;\r
- }\r
-\r
- if(hRes == S_OK)\r
- {\r
- /* Load the OLESTREAM to Memory */\r
- hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);\r
- }\r
-\r
- if(hRes == S_OK)\r
- {\r
- /* Load the OLESTREAM to Memory (part 2)*/\r
- hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);\r
- }\r
-\r
- if(hRes == S_OK)\r
- {\r
-\r
- if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))\r
- {\r
- /* Do we have the IStorage Data in the OLESTREAM */\r
- if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)\r
- {\r
- OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);\r
- OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);\r
- }\r
- else\r
- {\r
- /* It must be an original OLE 1.0 source */\r
- OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);\r
- }\r
- }\r
- else\r
- {\r
- /* It must be an original OLE 1.0 source */\r
- OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);\r
- }\r
-\r
- /* Create CompObj Stream if necessary */\r
- hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);\r
- if(hRes == S_OK)\r
- {\r
- /*Create the Ole Stream if necessary */\r
- OLECONVERT_CreateOleStream(pstg);\r
- }\r
- }\r
-\r
-\r
- /* Free allocated memory */\r
- for(i=0; i < 2; i++)\r
- {\r
- HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);\r
- HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);\r
- pOleStreamData[i].pstrOleObjFileName = NULL;\r
- }\r
- return hRes;\r
-}\r
-\r
-/*************************************************************************\r
- * OleConvertIStorageToOLESTREAM [OLE32.@]\r
- *\r
- * Read info on MSDN\r
- *\r
- * Read info on MSDN\r
- *\r
- * TODO\r
- * Still unsure of some mem fields for OLE 10 Stream\r
- * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",\r
- * and "\001OLE" streams.\r
- *\r
- */\r
-HRESULT WINAPI OleConvertIStorageToOLESTREAM (\r
- LPSTORAGE pstg,\r
- LPOLESTREAM pOleStream)\r
-{\r
- int i;\r
- HRESULT hRes = S_OK;\r
- IStream *pStream;\r
- OLECONVERT_OLESTREAM_DATA pOleStreamData[2];\r
- static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};\r
-\r
-\r
- memset(pOleStreamData, 0, sizeof(pOleStreamData));\r
-\r
- if(pstg == NULL || pOleStream == NULL)\r
- {\r
- hRes = E_INVALIDARG;\r
- }\r
- if(hRes == S_OK)\r
- {\r
- /* Get the ProgID */\r
- pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;\r
- hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));\r
- }\r
- if(hRes == S_OK)\r
- {\r
- /* Was it originally Ole10 */\r
- hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);\r
- if(hRes == S_OK)\r
- {\r
- IStream_Release(pStream);\r
- /* Get Presentation Data for Ole10Native */\r
- OLECONVERT_GetOle10PresData(pstg, pOleStreamData);\r
- }\r
- else\r
- {\r
- /* Get Presentation Data (OLE20) */\r
- OLECONVERT_GetOle20PresData(pstg, pOleStreamData);\r
- }\r
-\r
- /* Save OLESTREAM */\r
- hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);\r
- if(hRes == S_OK)\r
- {\r
- hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);\r
- }\r
-\r
- }\r
-\r
- /* Free allocated memory */\r
- for(i=0; i < 2; i++)\r
- {\r
- HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);\r
- }\r
-\r
- return hRes;\r
-}\r
-\r
-/***********************************************************************\r
- * GetConvertStg (OLE32.@)\r
- */\r
-HRESULT WINAPI GetConvertStg(IStorage *stg) {\r
- FIXME("unimplemented stub!\n");\r
- return E_FAIL;\r
-}\r
-\r
-/******************************************************************************\r
- * StgIsStorageFile [OLE32.@]\r
- */\r
-HRESULT WINAPI\r
-StgIsStorageFile(LPCOLESTR fn)\r
-{\r
- HANDLE hf;\r
- BYTE magic[8];\r
- DWORD bytes_read;\r
-\r
- TRACE("(\'%s\')\n", debugstr_w(fn));\r
- hf = CreateFileW(fn, GENERIC_READ,\r
- FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,\r
- NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);\r
-\r
- if (hf == INVALID_HANDLE_VALUE)\r
- return STG_E_FILENOTFOUND;\r
-\r
- if (!ReadFile(hf, magic, 8, &bytes_read, NULL))\r
- {\r
- WARN(" unable to read file\n");\r
- CloseHandle(hf);\r
- return S_FALSE;\r
- }\r
-\r
- CloseHandle(hf);\r
-\r
- if (bytes_read != 8) {\r
- WARN(" too short\n");\r
- return S_FALSE;\r
- }\r
-\r
- if (!memcmp(magic,STORAGE_magic,8)) {\r
- WARN(" -> YES\n");\r
- return S_OK;\r
- }\r
-\r
- WARN(" -> Invalid header.\n");\r
- return S_FALSE;\r
-}\r
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#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);
+
+#define FILE_BEGIN 0
+
+/* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
+#define OLESTREAM_ID 0x501
+#define OLESTREAM_MAX_STR_LEN 255
+
+static const char rootPropertyName[] = "Root Entry";
+
+
+/* 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 IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
+
+
+
+/************************************************************************
+** Storage32BaseImpl implementatiion
+*/
+
+/************************************************************************
+ * Storage32BaseImpl_QueryInterface (IUnknown)
+ *
+ * This method implements the common QueryInterface for all IStorage32
+ * implementations contained in this file.
+ *
+ * See Windows documentation for more details on IUnknown methods.
+ */
+HRESULT WINAPI 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 (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
+ {
+ *ppvObject = (IStorage*)This;
+ }
+ else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
+ {
+ *ppvObject = (IStorage*)This;
+ }
+ else if (memcmp(&IID_IPropertySetStorage, riid, sizeof(IID_IPropertySetStorage)) == 0)
+ {
+ *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.
+ */
+ULONG WINAPI StorageBaseImpl_AddRef(
+ IStorage* iface)
+{
+ StorageBaseImpl *This = (StorageBaseImpl *)iface;
+ ULONG ref = InterlockedIncrement(&This->ref);
+
+ TRACE("(%p) AddRef to %ld\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.
+ */
+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 %ld\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.
+ */
+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;
+ DWORD parent_grfMode;
+
+ TRACE("(%p, %s, %p, %lx, %ld, %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) ))
+ {
+ res = STG_E_INVALIDFLAG;
+ goto end;
+ }
+
+ /*
+ * As documented.
+ */
+ if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
+ (grfMode & STGM_DELETEONRELEASE) ||
+ (grfMode & STGM_TRANSACTED) )
+ {
+ res = STG_E_INVALIDFUNCTION;
+ goto end;
+ }
+
+ /*
+ * Check that we're compatible with the parent's storage mode
+ */
+ parent_grfMode = STGM_ACCESS_MODE( This->ancestorStorage->base.openFlags );
+ if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
+ {
+ 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,
+ ¤tProperty);
+
+ /*
+ * Delete the property enumeration since we don't need it anymore
+ */
+ IEnumSTATSTGImpl_Destroy(propertyEnumeration);
+
+ /*
+ * If it was found, construct the stream object and return a pointer to it.
+ */
+ if ( (foundPropertyIndex!=PROPERTY_NULL) &&
+ (currentProperty.propertyType==PROPTYPE_STREAM) )
+ {
+ newStream = StgStreamImpl_Construct(This, 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("<-- %08lx\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.
+ */
+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;
+ DWORD parent_grfMode;
+
+ TRACE("(%p, %s, %p, %lx, %p, %ld, %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
+ */
+ parent_grfMode = STGM_ACCESS_MODE( This->ancestorStorage->base.openFlags );
+ if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
+ {
+ 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,
+ ¤tProperty);
+
+ /*
+ * Delete the property enumeration since we don't need it anymore
+ */
+ IEnumSTATSTGImpl_Destroy(propertyEnumeration);
+
+ /*
+ * If it was found, construct the stream object and return a pointer to it.
+ */
+ if ( (foundPropertyIndex!=PROPERTY_NULL) &&
+ (currentProperty.propertyType==PROPTYPE_STORAGE) )
+ {
+ /*
+ * Construct a new Storage object
+ */
+ newStorage = 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("<-- %08lx\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.
+ */
+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, %ld, %p, %ld, %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.
+ */
+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, %lx)\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);
+
+ res = S_OK;
+ goto end;
+ }
+
+ res = E_FAIL;
+
+end:
+ if (res == S_OK)
+ {
+ TRACE("<-- STATSTG: pwcsName: %s, type: %ld, cbSize.Low/High: %ld/%ld, grfMode: %08lx, grfLocksSupported: %ld, grfStateBits: %08lx\n", debugstr_w(pstatstg->pwcsName), pstatstg->type, pstatstg->cbSize.u.LowPart, pstatstg->cbSize.u.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits);
+ }
+ TRACE("<-- %08lx\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.
+ */
+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,
+ ¤tProperty);
+
+ 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,
+ ¤tProperty);
+
+ /*
+ * Delete the property enumeration since we don't need it anymore
+ */
+ IEnumSTATSTGImpl_Destroy(propertyEnumeration);
+
+ if (foundPropertyIndex != PROPERTY_NULL)
+ {
+ StgProperty renamedProperty;
+ ULONG renamedPropertyIndex;
+
+ /*
+ * Setup a new property for the renamed property
+ */
+ renamedProperty.sizeOfNameString =
+ ( 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 to Destroy the old property we must zeroed it's dirProperty
+ * otherwise the DestroyProperty below will zap it all and we do not want
+ * this to happen.
+ * Also, we fake that the old property is a storage so the DestroyProperty
+ * will not do a SetSize(0) on the stream data.
+ *
+ * This means that we need to tweek the StgProperty if it is a stream or a
+ * non empty storage.
+ */
+ StorageImpl_ReadProperty(This->ancestorStorage,
+ foundPropertyIndex,
+ ¤tProperty);
+
+ currentProperty.dirProperty = PROPERTY_NULL;
+ currentProperty.propertyType = PROPTYPE_STORAGE;
+ StorageImpl_WriteProperty(
+ This->ancestorStorage,
+ foundPropertyIndex,
+ ¤tProperty);
+
+ /*
+ * Invoke Destroy to get rid of the ole property and automatically redo
+ * the linking of it's previous and next members...
+ */
+ 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.
+ */
+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;
+ DWORD parent_grfMode;
+
+ TRACE("(%p, %s, %lx, %ld, %ld, %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
+ */
+ parent_grfMode = STGM_ACCESS_MODE( This->ancestorStorage->base.openFlags );
+ if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
+ 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,
+ ¤tProperty);
+
+ 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;
+ }
+
+ /*
+ * 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.
+ */
+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.
+ */
+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;
+ DWORD parent_grfMode;
+
+ TRACE("(%p, %s, %lx, %ld, %ld, %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;
+
+ /*
+ * Validate the STGM flags
+ */
+ if ( FAILED( validateSTGM(grfMode) ) ||
+ (grfMode & STGM_DELETEONRELEASE) )
+ return STG_E_INVALIDFLAG;
+
+ /*
+ * Check that we're compatible with the parent's storage mode
+ */
+ parent_grfMode = STGM_ACCESS_MODE( This->base.ancestorStorage->base.openFlags );
+ if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
+ return STG_E_ACCESSDENIED;
+
+ /*
+ * Initialize the out parameter
+ */
+ *ppstg = 0;
+
+ /*
+ * Create a property enumeration and search the properties
+ */
+ propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
+ This->base.rootPropertySetIndex);
+
+ foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
+ pwcsName,
+ ¤tProperty);
+ 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;
+ }
+
+ /*
+ * memset the empty property
+ */
+ memset(&newProperty, 0, sizeof(StgProperty));
+
+ newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
+
+ if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
+ 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,
+ ¤tProperty);
+ 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,
+ ¤tProperty);
+
+ 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,
+ ¤tProperty);
+
+ previous = currentProperty.previousProperty;
+ next = currentProperty.nextProperty;
+ current = currentPropertyId;
+
+ while (found == 0)
+ {
+ LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
+
+ if (diff < 0)
+ {
+ if (previous != PROPERTY_NULL)
+ {
+ StorageImpl_ReadProperty(storage->base.ancestorStorage,
+ previous,
+ ¤tProperty);
+ current = previous;
+ }
+ else
+ {
+ currentProperty.previousProperty = newPropertyIndex;
+ StorageImpl_WriteProperty(storage->base.ancestorStorage,
+ current,
+ ¤tProperty);
+ found = 1;
+ }
+ }
+ else if (diff > 0)
+ {
+ if (next != PROPERTY_NULL)
+ {
+ StorageImpl_ReadProperty(storage->base.ancestorStorage,
+ next,
+ ¤tProperty);
+ current = next;
+ }
+ else
+ {
+ currentProperty.nextProperty = newPropertyIndex;
+ StorageImpl_WriteProperty(storage->base.ancestorStorage,
+ current,
+ ¤tProperty);
+ 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 it's dir property
+ */
+ currentProperty.dirProperty = newPropertyIndex;
+ StorageImpl_WriteProperty(storage->base.ancestorStorage,
+ storage->base.rootPropertySetIndex,
+ ¤tProperty);
+ }
+}
+
+
+/*************************************************************************
+ * CopyTo (IStorage)
+ */
+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, %ld, %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: %ld\n", curElement.type);
+ }
+
+ } while (hr == S_OK);
+
+ /*
+ * Clean-up
+ */
+ IEnumSTATSTG_Release(elements);
+
+ return hr;
+}
+
+/*************************************************************************
+ * MoveElementTo (IStorage)
+ */
+HRESULT WINAPI StorageImpl_MoveElementTo(
+ IStorage* iface,
+ const OLECHAR *pwcsName, /* [string][in] */
+ IStorage *pstgDest, /* [unique][in] */
+ const OLECHAR *pwcsNewName,/* [string][in] */
+ DWORD grfFlags) /* [in] */
+{
+ FIXME("not implemented!\n");
+ return E_NOTIMPL;
+}
+
+/*************************************************************************
+ * Commit (IStorage)
+ */
+HRESULT WINAPI StorageImpl_Commit(
+ IStorage* iface,
+ DWORD grfCommitFlags)/* [in] */
+{
+ FIXME("(%ld): stub!\n", grfCommitFlags);
+ return S_OK;
+}
+
+/*************************************************************************
+ * Revert (IStorage)
+ */
+HRESULT WINAPI StorageImpl_Revert(
+ IStorage* iface)
+{
+ FIXME("not implemented!\n");
+ return E_NOTIMPL;
+}
+
+/*************************************************************************
+ * DestroyElement (IStorage)
+ *
+ * Stategy: This implementation is build this way for simplicity not for speed.
+ * I always delete the top most element of the enumeration and adjust
+ * the deleted element pointer all the time. This takes longer to
+ * do but allow to reinvoke DestroyElement whenever we encounter a
+ * storage object. The optimisation reside in the usage of another
+ * enumeration stategy that would give all the leaves of a storage
+ * first. (postfix order)
+ */
+HRESULT WINAPI 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;
+
+ 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 it's 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.
+ */
+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 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_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, ¤tElement, 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 it's 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 it's name member.
+ */
+ propertyToDelete.sizeOfNameString = 0;
+
+ /*
+ * Here we should re-read the property so we get the updated pointer
+ * but since we are here to zap it, I don't do it...
+ */
+ 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)
+ */
+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)
+ */
+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 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
+};
+
+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 the virtual function table.
+ */
+ This->base.lpVtbl = &Storage32Impl_Vtbl;
+ This->base.pssVtbl = &IPropertySetStorage_Vtbl;
+ This->base.v_destructor = &StorageImpl_Destroy;
+ This->base.openFlags = openFlags;
+
+ /*
+ * 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;
+
+ /*
+ * 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
+ */
+ bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
+ memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
+ StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
+ StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
+ StorageImpl_ReleaseBigBlock(This, 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
+ */
+ 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,
+ ¤tProperty);
+
+ 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;
+}
+
+void StorageImpl_Destroy(StorageBaseImpl* iface)
+{
+ StorageImpl *This = (StorageImpl*) iface;
+ TRACE("(%p)\n", This);
+
+ 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.
+ *
+ */
+ULONG StorageImpl_GetNextFreeBigBlock(
+ StorageImpl* This)
+{
+ ULONG depotBlockIndexPos;
+ void *depotBuffer;
+ 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);
+ }
+ }
+
+ depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
+
+ if (depotBuffer != 0)
+ {
+ 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);
+ }
+
+ StorageImpl_ReleaseBigBlock(This, depotBuffer);
+ }
+
+ depotIndex++;
+ depotBlockOffset = 0;
+ }
+
+ This->prevFreeBlock = freeBlock;
+
+ return freeBlock;
+}
+
+/******************************************************************************
+ * Storage32Impl_AddBlockDepot
+ *
+ * This will create a depot block, essentially it is a block initialized
+ * to BLOCK_UNUSEDs.
+ */
+void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
+{
+ BYTE* blockBuffer;
+
+ blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
+
+ /*
+ * Initialize blocks as free
+ */
+ memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
+
+ StorageImpl_ReleaseBigBlock(This, 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.
+ */
+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)
+ {
+ BYTE* depotBuffer;
+
+ depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
+
+ if (depotBuffer != 0)
+ {
+ StorageUtl_ReadDWord(depotBuffer,
+ extBlockOffset * sizeof(ULONG),
+ &blockIndex);
+
+ StorageImpl_ReleaseBigBlock(This, depotBuffer);
+ }
+ }
+
+ 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.
+ */
+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)
+ {
+ BYTE* depotBuffer;
+
+ depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
+
+ if (depotBuffer != 0)
+ {
+ StorageUtl_WriteDWord(depotBuffer,
+ extBlockOffset * sizeof(ULONG),
+ blockIndex);
+
+ StorageImpl_ReleaseBigBlock(This, depotBuffer);
+ }
+ }
+}
+
+/******************************************************************************
+ * Storage32Impl_AddExtBlockDepot
+ *
+ * Creates an extended depot block.
+ */
+ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
+{
+ ULONG numExtBlocks = This->extBigBlockDepotCount;
+ ULONG nextExtBlock = This->extBigBlockDepotStart;
+ BYTE* depotBuffer = NULL;
+ 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.
+ */
+ depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
+ StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
+ StorageImpl_ReleaseBigBlock(This, depotBuffer);
+ }
+
+ /*
+ * Initialize this block.
+ */
+ depotBuffer = StorageImpl_GetBigBlock(This, index);
+ memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
+ StorageImpl_ReleaseBigBlock(This, depotBuffer);
+
+ return index;
+}
+
+/******************************************************************************
+ * Storage32Impl_FreeBigBlock
+ *
+ * This method will flag the specified block as free in the big block depot.
+ */
+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.
+ */
+HRESULT StorageImpl_GetNextBlockInChain(
+ StorageImpl* This,
+ ULONG blockIndex,
+ ULONG* nextBlockIndex)
+{
+ ULONG offsetInDepot = blockIndex * sizeof (ULONG);
+ ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
+ ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
+ void* depotBuffer;
+ ULONG depotBlockIndexPos;
+ int index;
+
+ *nextBlockIndex = BLOCK_SPECIAL;
+
+ if(depotBlockCount >= This->bigBlockDepotCount)
+ {
+ WARN("depotBlockCount %ld, bigBlockDepotCount %ld\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);
+ }
+
+ depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
+
+ if (!depotBuffer)
+ 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;
+ }
+ StorageImpl_ReleaseBigBlock(This, depotBuffer);
+ }
+
+ *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.
+ */
+ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
+{
+ ULONG nextBlockIndex = BLOCK_SPECIAL;
+ ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
+ void* depotBuffer;
+
+ depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
+
+ if (depotBuffer!=0)
+ {
+ StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
+
+ StorageImpl_ReleaseBigBlock(This, depotBuffer);
+ }
+
+ return nextBlockIndex;
+}
+
+/******************************************************************************
+ * Storage32Impl_SetNextBlockInChain
+ *
+ * This method will write the index of the specified block's next block
+ * in the big block depot.
+ *
+ * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
+ * do the following
+ *
+ * Storage32Impl_SetNextBlockInChain(This, 3, 1);
+ * Storage32Impl_SetNextBlockInChain(This, 1, 7);
+ * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
+ *
+ */
+void 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;
+ void* depotBuffer;
+
+ 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);
+ }
+
+ depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
+
+ if (depotBuffer!=0)
+ {
+ StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
+ StorageImpl_ReleaseBigBlock(This, depotBuffer);
+ }
+
+ /*
+ * 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.
+ */
+HRESULT StorageImpl_LoadFileHeader(
+ StorageImpl* This)
+{
+ HRESULT hr = STG_E_FILENOTFOUND;
+ void* headerBigBlock = NULL;
+ int index;
+
+ /*
+ * Get a pointer to the big block of data containing the header.
+ */
+ headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
+
+ /*
+ * Extract the information from the header.
+ */
+ if (headerBigBlock!=0)
+ {
+ /*
+ * Check for the "magic number" signature and return an error if it is not
+ * found.
+ */
+ if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
+ {
+ StorageImpl_ReleaseBigBlock(This, headerBigBlock);
+ return STG_E_OLDFORMAT;
+ }
+
+ if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
+ {
+ StorageImpl_ReleaseBigBlock(This, headerBigBlock);
+ return STG_E_INVALIDHEADER;
+ }
+
+ StorageUtl_ReadWord(
+ headerBigBlock,
+ OFFSET_BIGBLOCKSIZEBITS,
+ &This->bigBlockSizeBits);
+
+ StorageUtl_ReadWord(
+ headerBigBlock,
+ OFFSET_SMALLBLOCKSIZEBITS,
+ &This->smallBlockSizeBits);
+
+ StorageUtl_ReadDWord(
+ headerBigBlock,
+ OFFSET_BBDEPOTCOUNT,
+ &This->bigBlockDepotCount);
+
+ StorageUtl_ReadDWord(
+ headerBigBlock,
+ OFFSET_ROOTSTARTBLOCK,
+ &This->rootStartBlock);
+
+ StorageUtl_ReadDWord(
+ headerBigBlock,
+ OFFSET_SBDEPOTSTART,
+ &This->smallBlockDepotStart);
+
+ StorageUtl_ReadDWord(
+ headerBigBlock,
+ OFFSET_EXTBBDEPOTSTART,
+ &This->extBigBlockDepotStart);
+
+ StorageUtl_ReadDWord(
+ headerBigBlock,
+ OFFSET_EXTBBDEPOTCOUNT,
+ &This->extBigBlockDepotCount);
+
+ for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
+ {
+ StorageUtl_ReadDWord(
+ headerBigBlock,
+ OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
+ &(This->bigBlockDepotStart[index]));
+ }
+
+ /*
+ * Make the bitwise arithmetic to get the size of the blocks in bytes.
+ */
+ if ((1 << 2) == 4)
+ {
+ This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
+ This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
+ }
+ else
+ {
+ This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
+ This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
+ }
+
+ /*
+ * Right now, the code is making some assumptions about the size of the
+ * blocks, just make sure they are what we're expecting.
+ */
+ 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;
+
+ /*
+ * Release the block.
+ */
+ StorageImpl_ReleaseBigBlock(This, headerBigBlock);
+ }
+
+ return hr;
+}
+
+/******************************************************************************
+ * Storage32Impl_SaveFileHeader
+ *
+ * This method will save to the file the header, i.e. big block -1.
+ */
+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;
+ BOOL readSuccessful;
+ ULONG bytesRead;
+
+ offsetInPropSet.u.HighPart = 0;
+ offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
+
+ readSuccessful = BlockChainStream_ReadAt(
+ This->rootBlockChain,
+ offsetInPropSet,
+ PROPSET_BLOCK_SIZE,
+ currentProperty,
+ &bytesRead);
+
+ if (readSuccessful)
+ {
+ /* 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 readSuccessful;
+}
+
+/*********************************************************************
+ * 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;
+ BOOL writeSuccessful;
+ 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);
+
+ writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
+ offsetInPropSet,
+ PROPSET_BLOCK_SIZE,
+ currentProperty,
+ &bytesWritten);
+ return writeSuccessful;
+}
+
+BOOL StorageImpl_ReadBigBlock(
+ StorageImpl* This,
+ ULONG blockIndex,
+ void* buffer)
+{
+ void* bigBlockBuffer;
+
+ bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
+
+ if (bigBlockBuffer!=0)
+ {
+ memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
+
+ StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+BOOL StorageImpl_WriteBigBlock(
+ StorageImpl* This,
+ ULONG blockIndex,
+ void* buffer)
+{
+ void* bigBlockBuffer;
+
+ bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
+
+ if (bigBlockBuffer!=0)
+ {
+ memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
+
+ StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void* StorageImpl_GetROBigBlock(
+ StorageImpl* This,
+ ULONG blockIndex)
+{
+ return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
+}
+
+void* StorageImpl_GetBigBlock(
+ StorageImpl* This,
+ ULONG blockIndex)
+{
+ return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
+}
+
+void StorageImpl_ReleaseBigBlock(
+ StorageImpl* This,
+ void* pBigBlock)
+{
+ BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
+}
+
+/******************************************************************************
+ * Storage32Impl_SmallBlocksToBigBlocks
+ *
+ * This method will convert a small block chain to a big block chain.
+ * The small block chain will be destroyed.
+ */
+BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
+ StorageImpl* This,
+ SmallBlockChainStream** ppsbChain)
+{
+ ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
+ ULARGE_INTEGER size, offset;
+ ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
+ ULONG propertyIndex;
+ BOOL successRead, successWrite;
+ 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 = 0;
+ cbTotalWritten = 0;
+
+ buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
+ do
+ {
+ successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
+ offset,
+ DEF_SMALL_BLOCK_SIZE,
+ buffer,
+ &cbRead);
+ cbTotalRead += cbRead;
+
+ successWrite = BlockChainStream_WriteAt(bbTempChain,
+ offset,
+ cbRead,
+ buffer,
+ &cbWritten);
+ cbTotalWritten += cbWritten;
+
+ offset.u.LowPart += This->smallBlockSize;
+
+ } while (successRead && successWrite);
+ HeapFree(GetProcessHeap(),0,buffer);
+
+ assert(cbTotalRead == cbTotalWritten);
+
+ /*
+ * 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;
+}
+
+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.
+*/
+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.
+*/
+HRESULT WINAPI StorageInternalImpl_Revert(
+ IStorage* iface)
+{
+ return S_OK;
+}
+
+void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
+{
+ IStorage_Release((IStorage*)This->parentStorage);
+ HeapFree(GetProcessHeap(), 0, This->stackToVisit);
+ HeapFree(GetProcessHeap(), 0, This);
+}
+
+HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
+ IEnumSTATSTG* iface,
+ REFIID riid,
+ void** ppvObject)
+{
+ IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
+
+ /*
+ * Perform a sanity check on the parameters.
+ */
+ if (ppvObject==0)
+ return E_INVALIDARG;
+
+ /*
+ * Initialize the return parameter.
+ */
+ *ppvObject = 0;
+
+ /*
+ * Compare the riid with the interface IDs implemented by this object.
+ */
+ if (IsEqualGUID(&IID_IUnknown, riid) ||
+ IsEqualGUID(&IID_IStorage, riid))
+ {
+ *ppvObject = (IEnumSTATSTG*)This;
+ IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
+ return S_OK;
+ }
+
+ return E_NOINTERFACE;
+}
+
+ULONG WINAPI IEnumSTATSTGImpl_AddRef(
+ IEnumSTATSTG* iface)
+{
+ IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
+ return InterlockedIncrement(&This->ref);
+}
+
+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;
+}
+
+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,
+ ¤tProperty);
+
+ /*
+ * Copy the information to the return buffer.
+ */
+ StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
+ ¤tProperty,
+ STATFLAG_DEFAULT);
+
+ /*
+ * Step to the next item in the iteration
+ */
+ (*pceltFetched)++;
+ currentReturnStruct++;
+
+ /*
+ * Push the next search node in the search stack.
+ */
+ IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
+
+ /*
+ * continue the iteration.
+ */
+ currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
+ }
+
+ if (*pceltFetched == celt)
+ return S_OK;
+
+ return S_FALSE;
+}
+
+
+HRESULT WINAPI IEnumSTATSTGImpl_Skip(
+ IEnumSTATSTG* iface,
+ ULONG celt)
+{
+ IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
+
+ StgProperty currentProperty;
+ ULONG objectFetched = 0;
+ ULONG currentSearchNode;
+
+ /*
+ * Start with the node at the top of the stack.
+ */
+ currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
+
+ while ( (objectFetched < celt) &&
+ (currentSearchNode!=PROPERTY_NULL) )
+ {
+ /*
+ * Remove the top node from the stack
+ */
+ IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
+
+ /*
+ * Read the property from the storage.
+ */
+ StorageImpl_ReadProperty(This->parentStorage,
+ currentSearchNode,
+ ¤tProperty);
+
+ /*
+ * Step to the next item in the iteration
+ */
+ objectFetched++;
+
+ /*
+ * Push the next search node in the search stack.
+ */
+ IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
+
+ /*
+ * continue the iteration.
+ */
+ currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
+ }
+
+ if (objectFetched == celt)
+ return S_OK;
+
+ return S_FALSE;
+}
+
+HRESULT WINAPI IEnumSTATSTGImpl_Reset(
+ IEnumSTATSTG* iface)
+{
+ IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
+
+ StgProperty rootProperty;
+ 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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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);
+ }
+}
+
+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 IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
+{
+ IEnumSTATSTGImpl_QueryInterface,
+ IEnumSTATSTGImpl_AddRef,
+ IEnumSTATSTGImpl_Release,
+ IEnumSTATSTGImpl_Next,
+ IEnumSTATSTGImpl_Skip,
+ IEnumSTATSTGImpl_Reset,
+ IEnumSTATSTGImpl_Clone
+};
+
+/******************************************************************************
+** IEnumSTATSTGImpl implementation
+*/
+
+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 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
+*/
+
+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 virtual function table.
+ */
+ newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
+ newStorage->base.v_destructor = &StorageInternalImpl_Destroy;
+ newStorage->base.openFlags = openFlags;
+
+ /*
+ * 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.
+ *
+ */
+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!
+ *
+ */
+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.
+ */
+BOOL 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;
+ BYTE* bigBlockBuffer;
+
+ /*
+ * 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 FALSE;
+ blockNoInSequence--;
+ }
+
+ This->lastBlockNoInSequenceIndex = blockIndex;
+
+ /*
+ * Start reading the buffer.
+ */
+ *bytesRead = 0;
+ bufferWalker = buffer;
+
+ while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
+ {
+ /*
+ * Calculate how many bytes we can copy from this big block.
+ */
+ bytesToReadInBuffer =
+ min(This->parentStorage->bigBlockSize - offsetInBlock, size);
+
+ /*
+ * Copy those bytes to the buffer
+ */
+ bigBlockBuffer =
+ StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
+
+ memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
+
+ StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
+
+ /*
+ * Step to the next big block.
+ */
+ if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
+ return FALSE;
+
+ bufferWalker += bytesToReadInBuffer;
+ size -= bytesToReadInBuffer;
+ *bytesRead += bytesToReadInBuffer;
+ offsetInBlock = 0; /* There is no offset on the next block */
+
+ }
+
+ return (size == 0);
+}
+
+/******************************************************************************
+ * BlockChainStream_WriteAt
+ *
+ * Writes the specified number of bytes to this chain at the specified offset.
+ * bytesWritten may be NULL.
+ * Will fail if not all specified number of bytes have been written.
+ */
+BOOL 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;
+ BYTE* bigBlockBuffer;
+
+ /*
+ * 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 FALSE;
+ blockNoInSequence--;
+ }
+
+ This->lastBlockNoInSequenceIndex = blockIndex;
+
+ /*
+ * 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 from this big block.
+ */
+ bytesToWrite =
+ min(This->parentStorage->bigBlockSize - offsetInBlock, size);
+
+ /*
+ * Copy those bytes to the buffer
+ */
+ bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
+
+ memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
+
+ StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
+
+ /*
+ * Step to the next big block.
+ */
+ if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
+ &blockIndex)))
+ return FALSE;
+ bufferWalker += bytesToWrite;
+ size -= bytesToWrite;
+ *bytesWritten += bytesToWrite;
+ offsetInBlock = 0; /* There is no offset on the next block */
+ }
+
+ return (size == 0);
+}
+
+/******************************************************************************
+ * BlockChainStream_Shrink
+ *
+ * Shrinks this chain in the big block depot.
+ */
+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.
+ */
+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
+ {
+ ULARGE_INTEGER fileSize =
+ BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
+
+ ULONG diff = newSize.u.LowPart - size.u.LowPart;
+
+ /*
+ * Make sure the file stays a multiple of blocksize
+ */
+ if ((diff % This->parentStorage->bigBlockSize) != 0)
+ diff += (This->parentStorage->bigBlockSize -
+ (diff % This->parentStorage->bigBlockSize) );
+
+ fileSize.u.LowPart += diff;
+ BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
+
+ BlockChainStream_Enlarge(This, newSize);
+ }
+
+ return TRUE;
+}
+
+/******************************************************************************
+ * BlockChainStream_GetSize
+ *
+ * Returns the size of this chain.
+ * Will return the block count if this chain doesn't have a property.
+ */
+ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
+{
+ StgProperty chainProperty;
+
+ if(This->headOfStreamPlaceHolder == NULL)
+ {
+ /*
+ * This chain is a data stream read the property and return
+ * the appropriate size
+ */
+ 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.
+ */
+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
+ */
+HRESULT SmallBlockChainStream_GetNextBlockInChain(
+ SmallBlockChainStream* This,
+ ULONG blockIndex,
+ ULONG* nextBlockInChain)
+{
+ ULARGE_INTEGER offsetOfBlockInDepot;
+ DWORD buffer;
+ ULONG bytesRead;
+ BOOL success;
+
+ *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.
+ */
+ success = BlockChainStream_ReadAt(
+ This->parentStorage->smallBlockDepotChain,
+ offsetOfBlockInDepot,
+ sizeof(DWORD),
+ &buffer,
+ &bytesRead);
+
+ if (success)
+ {
+ StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
+ return S_OK;
+ }
+
+ return STG_E_READFAULT;
+}
+
+/******************************************************************************
+ * SmallBlockChainStream_SetNextBlockInChain
+ *
+ * Writes the index of the next block of the specified block in the small
+ * block depot.
+ * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
+ * To flag a block as free use BLOCK_UNUSED as nextBlock.
+ */
+void SmallBlockChainStream_SetNextBlockInChain(
+ SmallBlockChainStream* This,
+ ULONG blockIndex,
+ ULONG nextBlock)
+{
+ ULARGE_INTEGER offsetOfBlockInDepot;
+ DWORD buffer;
+ ULONG bytesWritten;
+
+ offsetOfBlockInDepot.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.
+ */
+void SmallBlockChainStream_FreeBlock(
+ SmallBlockChainStream* This,
+ ULONG blockIndex)
+{
+ SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
+}
+
+/******************************************************************************
+ * SmallBlockChainStream_GetNextFreeBlock
+ *
+ * Returns the index of a free small block. The small block depot will be
+ * enlarged if necessary. The small block chain will also be enlarged if
+ * necessary.
+ */
+ULONG SmallBlockChainStream_GetNextFreeBlock(
+ SmallBlockChainStream* This)
+{
+ ULARGE_INTEGER offsetOfBlockInDepot;
+ DWORD buffer;
+ ULONG bytesRead;
+ ULONG blockIndex = 0;
+ ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
+ BOOL success = TRUE;
+ 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);
+
+ success = BlockChainStream_ReadAt(
+ This->parentStorage->smallBlockDepotChain,
+ offsetOfBlockInDepot,
+ sizeof(DWORD),
+ &buffer,
+ &bytesRead);
+
+ /*
+ * If we run out of space for the small block depot, enlarge it
+ */
+ if (success)
+ {
+ StorageUtl_ReadDWord((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;
+
+ 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
+ */
+ smallBlockDepot =
+ StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
+
+ memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
+ StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
+
+ if (count == 0)
+ {
+ /*
+ * We have just created the small block depot.
+ */
+ StgProperty rootProp;
+ ULONG sbStartIndex;
+
+ /*
+ * Save it in the header
+ */
+ This->parentStorage->smallBlockDepotStart = newsbdIndex;
+ 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.
+ */
+BOOL SmallBlockChainStream_ReadAt(
+ SmallBlockChainStream* This,
+ ULARGE_INTEGER offset,
+ ULONG size,
+ void* buffer,
+ ULONG* bytesRead)
+{
+ 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))
+ {
+ if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
+ &blockIndex)))
+ return FALSE;
+ 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.
+ */
+ BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
+ offsetInBigBlockFile,
+ bytesToReadInBuffer,
+ bufferWalker,
+ &bytesReadFromBigBlockFile);
+
+ assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
+
+ /*
+ * Step to the next big block.
+ */
+ if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
+ return FALSE;
+ bufferWalker += bytesToReadInBuffer;
+ size -= bytesToReadInBuffer;
+ *bytesRead += bytesToReadInBuffer;
+ offsetInBlock = 0; /* There is no offset on the next block */
+ }
+
+ return (size == 0);
+}
+
+/******************************************************************************
+ * SmallBlockChainStream_WriteAt
+ *
+ * Writes the specified number of bytes to this chain at the specified offset.
+ * bytesWritten may be NULL.
+ * Will fail if not all specified number of bytes have been written.
+ */
+BOOL 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 bytesWrittenFromBigBlockFile;
+ const 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))
+ {
+ if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
+ return FALSE;
+ 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.
+ */
+ BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
+ offsetInBigBlockFile,
+ bytesToWriteInBuffer,
+ bufferWalker,
+ &bytesWrittenFromBigBlockFile);
+
+ assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
+
+ /*
+ * Step to the next big block.
+ */
+ if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
+ &blockIndex)))
+ return FALSE;
+ bufferWalker += bytesToWriteInBuffer;
+ size -= bytesToWriteInBuffer;
+ *bytesWritten += bytesToWriteInBuffer;
+ offsetInBlock = 0; /* There is no offset on the next block */
+ }
+
+ return (size == 0);
+}
+
+/******************************************************************************
+ * SmallBlockChainStream_Shrink
+ *
+ * Shrinks this chain in the small block depot.
+ */
+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.
+ */
+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_GetCount
+ *
+ * Returns the number of blocks that comprises this chain.
+ * This is not the size of this chain as the last block may not be full!
+ */
+ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
+{
+ ULONG blockIndex;
+ ULONG count = 0;
+
+ blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
+
+ while (blockIndex != BLOCK_END_OF_CHAIN)
+ {
+ count++;
+
+ if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
+ return 0;
+ }
+
+ return count;
+}
+
+/******************************************************************************
+ * SmallBlockChainStream_SetSize
+ *
+ * Sets the size of this stream.
+ * The file will grow if we grow the chain.
+ *
+ * TODO: Free the actual blocks in the file when we shrink the chain.
+ * Currently, the blocks are still in the file. So the file size
+ * doesn't shrink even if we shrink streams.
+ */
+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.
+ */
+ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
+{
+ StgProperty chainProperty;
+
+ StorageImpl_ReadProperty(
+ This->parentStorage,
+ This->ownerPropertyIndex,
+ &chainProperty);
+
+ return chainProperty.size;
+}
+
+/******************************************************************************
+ * StgCreateDocfile [OLE32.@]
+ */
+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, %lx, %ld, %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 always opens for write */
+ switch(STGM_ACCESS_MODE(grfMode))
+ {
+ case STGM_WRITE:
+ case STGM_READWRITE:
+ break;
+ default:
+ goto end;
+ }
+
+ /* can't share write */
+ switch(STGM_SHARE_MODE(grfMode))
+ {
+ case STGM_SHARE_EXCLUSIVE:
+ case STGM_SHARE_DENY_WRITE:
+ break;
+ default:
+ goto end;
+ }
+
+ /* shared reading requires transacted mode */
+ if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
+ !(grfMode&STGM_TRANSACTED) )
+ goto end;
+
+ /*
+ * Generate a unique name.
+ */
+ if (pwcsName == 0)
+ {
+ WCHAR tempPath[MAX_PATH];
+ static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
+
+ if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
+ goto end;
+
+ 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 = %08lx\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, %lx, %lx, %lx, %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%lx, %p): stub\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, %lx, %lx, %lx, %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;
+ }
+
+ if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
+ {
+ ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\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 || stgfmt == STGFMT_ANY)
+ {
+ if (stgfmt == STGFMT_ANY)
+ WARN("STGFMT_ANY assuming storage\n");
+ FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
+ return StgOpenStorage(pwcsName, NULL, grfMode, (SNB)NULL, 0, (IStorage **)ppObjectOpen);
+ }
+
+ ERR("Invalid stgfmt argument\n");
+ return STG_E_INVALIDPARAMETER;
+}
+
+
+/******************************************************************************
+ * 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];
+ DWORD length;
+
+ TRACE("(%s, %p, %lx, %p, %ld, %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;
+ }
+
+ /*
+ * Validate the sharing mode
+ */
+ 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
+ */
+ length = GetFileSize(hFile, NULL);
+ if (length < 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;
+ }
+
+ /* if the file's length was zero, 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("<-- %08lx, 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;
+
+ assert(pStg != 0);
+
+ hRes = IStorage_SetClass(pStg, rclsid);
+
+ return hRes;
+}
+
+/***********************************************************************
+ * ReadClassStg (OLE32.@)
+ *
+ * This method reads the CLSID previously written to a storage object with the WriteClassStg.
+ */
+HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
+
+ STATSTG pstatstg;
+ HRESULT hRes;
+
+ TRACE("()\n");
+
+ if(pclsid==NULL)
+ return E_POINTER;
+ /*
+ * 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 %08lx\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
+ */
+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.
+ *
+ */
+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
+ *
+ *
+ */
+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.
+ *
+ */
+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("%ld 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 = %08lx\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, &(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.
+ *
+ */
+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
+ *
+ */
+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.
+ *
+ *
+ */
+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
+ *
+ *
+ */
+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
+ */
+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];
+
+ 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};
+
+
+ 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.@]
+ */
+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;
+}