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