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