| /* |
| * 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; |
| } |
| |