2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
33 * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/stg/stg/istorage_compound_file_implementation.asp
43 #define NONAMELESSUNION
44 #define NONAMELESSSTRUCT
50 #include "wine/unicode.h"
51 #include "wine/debug.h"
53 #include "storage32.h"
54 #include "ole2.h" /* For Write/ReadClassStm */
57 #include "wine/wingdi16.h"
59 WINE_DEFAULT_DEBUG_CHANNEL(storage);
61 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
62 #define OLESTREAM_ID 0x501
63 #define OLESTREAM_MAX_STR_LEN 255
65 static const char rootPropertyName[] = "Root Entry";
67 /****************************************************************************
68 * Storage32InternalImpl definitions.
70 * Definition of the implementation structure for the IStorage32 interface.
71 * This one implements the IStorage32 interface for storage that are
72 * inside another storage.
74 struct StorageInternalImpl
76 struct StorageBaseImpl base;
78 * There is no specific data for this class.
81 typedef struct StorageInternalImpl StorageInternalImpl;
83 /* Method definitions for the Storage32InternalImpl class. */
84 static StorageInternalImpl* StorageInternalImpl_Construct(StorageImpl* ancestorStorage,
85 DWORD openFlags, ULONG rootTropertyIndex);
86 static void StorageImpl_Destroy(StorageBaseImpl* iface);
87 static void* StorageImpl_GetBigBlock(StorageImpl* This, ULONG blockIndex);
88 static void* StorageImpl_GetROBigBlock(StorageImpl* This, ULONG blockIndex);
89 static void StorageImpl_ReleaseBigBlock(StorageImpl* This, void* pBigBlock);
90 static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
91 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
92 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
93 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
94 static void StorageImpl_SaveFileHeader(StorageImpl* This);
96 static void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm);
98 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
99 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
100 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
101 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
102 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
104 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
105 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
106 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
108 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
111 /* OLESTREAM memory structure to use for Get and Put Routines */
112 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
117 DWORD dwOleTypeNameLength;
118 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
119 CHAR *pstrOleObjFileName;
120 DWORD dwOleObjFileNameLength;
121 DWORD dwMetaFileWidth;
122 DWORD dwMetaFileHeight;
123 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
126 }OLECONVERT_OLESTREAM_DATA;
128 /* CompObj Stream structure */
129 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
134 DWORD dwCLSIDNameLength;
135 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
136 DWORD dwOleTypeNameLength;
137 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
138 DWORD dwProgIDNameLength;
139 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
141 }OLECONVERT_ISTORAGE_COMPOBJ;
144 /* Ole Presention Stream structure */
145 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
153 }OLECONVERT_ISTORAGE_OLEPRES;
157 /***********************************************************************
158 * Forward declaration of internal functions used by the method DestroyElement
160 static HRESULT deleteStorageProperty(
161 StorageImpl *parentStorage,
162 ULONG foundPropertyIndexToDelete,
163 StgProperty propertyToDelete);
165 static HRESULT deleteStreamProperty(
166 StorageImpl *parentStorage,
167 ULONG foundPropertyIndexToDelete,
168 StgProperty propertyToDelete);
170 static HRESULT findPlaceholder(
171 StorageImpl *storage,
172 ULONG propertyIndexToStore,
173 ULONG storagePropertyIndex,
176 static HRESULT adjustPropertyChain(
178 StgProperty propertyToDelete,
179 StgProperty parentProperty,
180 ULONG parentPropertyId,
183 /***********************************************************************
184 * Declaration of the functions used to manipulate StgProperty
187 static ULONG getFreeProperty(
188 StorageImpl *storage);
190 static void updatePropertyChain(
191 StorageImpl *storage,
192 ULONG newPropertyIndex,
193 StgProperty newProperty);
195 static LONG propertyNameCmp(
196 const OLECHAR *newProperty,
197 const OLECHAR *currentProperty);
200 /***********************************************************************
201 * Declaration of miscellaneous functions...
203 static HRESULT validateSTGM(DWORD stgmValue);
205 static DWORD GetShareModeFromSTGM(DWORD stgm);
206 static DWORD GetAccessModeFromSTGM(DWORD stgm);
207 static DWORD GetCreationModeFromSTGM(DWORD stgm);
209 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
212 /****************************************************************************
213 * IEnumSTATSTGImpl definitions.
215 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
216 * This class allows iterating through the content of a storage and to find
217 * specific items inside it.
219 struct IEnumSTATSTGImpl
221 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
222 * since we want to cast this in an IEnumSTATSTG pointer */
224 LONG ref; /* Reference count */
225 StorageImpl* parentStorage; /* Reference to the parent storage */
226 ULONG firstPropertyNode; /* Index of the root of the storage to enumerate */
229 * The current implementation of the IEnumSTATSTGImpl class uses a stack
230 * to walk the property sets to get the content of a storage. This stack
231 * is implemented by the following 3 data members
237 #define ENUMSTATSGT_SIZE_INCREMENT 10
241 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageImpl* This, ULONG firstPropertyNode);
242 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
243 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl* This, ULONG nodeToPush);
244 static ULONG IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl* This, BOOL remove);
245 static ULONG IEnumSTATSTGImpl_FindProperty(IEnumSTATSTGImpl* This, const OLECHAR* lpszPropName,
246 StgProperty* buffer);
247 static INT IEnumSTATSTGImpl_FindParentProperty(IEnumSTATSTGImpl *This, ULONG childProperty,
248 StgProperty *currentProperty, ULONG *propertyId);
251 /************************************************************************
252 ** Storage32BaseImpl implementatiion
255 /************************************************************************
256 * Storage32BaseImpl_QueryInterface (IUnknown)
258 * This method implements the common QueryInterface for all IStorage32
259 * implementations contained in this file.
261 * See Windows documentation for more details on IUnknown methods.
263 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
268 StorageBaseImpl *This = (StorageBaseImpl *)iface;
270 * Perform a sanity check on the parameters.
272 if ( (This==0) || (ppvObject==0) )
276 * Initialize the return parameter.
281 * Compare the riid with the interface IDs implemented by this object.
283 if (IsEqualGUID(&IID_IUnknown, riid) ||
284 IsEqualGUID(&IID_IStorage, riid))
286 *ppvObject = (IStorage*)This;
288 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
290 *ppvObject = (IStorage*)&This->pssVtbl;
294 * Check that we obtained an interface.
297 return E_NOINTERFACE;
300 * Query Interface always increases the reference count by one when it is
303 IStorage_AddRef(iface);
308 /************************************************************************
309 * Storage32BaseImpl_AddRef (IUnknown)
311 * This method implements the common AddRef for all IStorage32
312 * implementations contained in this file.
314 * See Windows documentation for more details on IUnknown methods.
316 static ULONG WINAPI StorageBaseImpl_AddRef(
319 StorageBaseImpl *This = (StorageBaseImpl *)iface;
320 ULONG ref = InterlockedIncrement(&This->ref);
322 TRACE("(%p) AddRef to %d\n", This, ref);
327 /************************************************************************
328 * Storage32BaseImpl_Release (IUnknown)
330 * This method implements the common Release for all IStorage32
331 * implementations contained in this file.
333 * See Windows documentation for more details on IUnknown methods.
335 static ULONG WINAPI StorageBaseImpl_Release(
338 StorageBaseImpl *This = (StorageBaseImpl *)iface;
340 * Decrease the reference count on this object.
342 ULONG ref = InterlockedDecrement(&This->ref);
344 TRACE("(%p) ReleaseRef to %d\n", This, ref);
347 * If the reference count goes down to 0, perform suicide.
352 * Since we are using a system of base-classes, we want to call the
353 * destructor of the appropriate derived class. To do this, we are
354 * using virtual functions to implement the destructor.
356 This->v_destructor(This);
362 /************************************************************************
363 * Storage32BaseImpl_OpenStream (IStorage)
365 * This method will open the specified stream object from the current storage.
367 * See Windows documentation for more details on IStorage methods.
369 static HRESULT WINAPI StorageBaseImpl_OpenStream(
371 const OLECHAR* pwcsName, /* [string][in] */
372 void* reserved1, /* [unique][in] */
373 DWORD grfMode, /* [in] */
374 DWORD reserved2, /* [in] */
375 IStream** ppstm) /* [out] */
377 StorageBaseImpl *This = (StorageBaseImpl *)iface;
378 IEnumSTATSTGImpl* propertyEnumeration;
379 StgStreamImpl* newStream;
380 StgProperty currentProperty;
381 ULONG foundPropertyIndex;
382 HRESULT res = STG_E_UNKNOWN;
384 TRACE("(%p, %s, %p, %x, %d, %p)\n",
385 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
388 * Perform a sanity check on the parameters.
390 if ( (pwcsName==NULL) || (ppstm==0) )
397 * Initialize the out parameter
402 * Validate the STGM flags
404 if ( FAILED( validateSTGM(grfMode) ) ||
405 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
407 res = STG_E_INVALIDFLAG;
414 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
416 res = STG_E_INVALIDFUNCTION;
421 * Check that we're compatible with the parent's storage mode, but
422 * only if we are not in transacted mode
424 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
425 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
427 res = STG_E_ACCESSDENIED;
433 * Create a property enumeration to search the properties
435 propertyEnumeration = IEnumSTATSTGImpl_Construct(
436 This->ancestorStorage,
437 This->rootPropertySetIndex);
440 * Search the enumeration for the property with the given name
442 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
448 * Delete the property enumeration since we don't need it anymore
450 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
453 * If it was found, construct the stream object and return a pointer to it.
455 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
456 (currentProperty.propertyType==PROPTYPE_STREAM) )
458 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
462 newStream->grfMode = grfMode;
463 *ppstm = (IStream*)newStream;
466 * Since we are returning a pointer to the interface, we have to
467 * nail down the reference.
469 IStream_AddRef(*ppstm);
472 * add us to the storage's list of active streams
475 StorageBaseImpl_AddStream(This,newStream);
485 res = STG_E_FILENOTFOUND;
489 TRACE("<-- IStream %p\n", *ppstm);
490 TRACE("<-- %08x\n", res);
494 /************************************************************************
495 * Storage32BaseImpl_OpenStorage (IStorage)
497 * This method will open a new storage object from the current storage.
499 * See Windows documentation for more details on IStorage methods.
501 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
503 const OLECHAR* pwcsName, /* [string][unique][in] */
504 IStorage* pstgPriority, /* [unique][in] */
505 DWORD grfMode, /* [in] */
506 SNB snbExclude, /* [unique][in] */
507 DWORD reserved, /* [in] */
508 IStorage** ppstg) /* [out] */
510 StorageBaseImpl *This = (StorageBaseImpl *)iface;
511 StorageInternalImpl* newStorage;
512 IEnumSTATSTGImpl* propertyEnumeration;
513 StgProperty currentProperty;
514 ULONG foundPropertyIndex;
515 HRESULT res = STG_E_UNKNOWN;
517 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
518 iface, debugstr_w(pwcsName), pstgPriority,
519 grfMode, snbExclude, reserved, ppstg);
522 * Perform a sanity check on the parameters.
524 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
531 if (snbExclude != NULL)
533 res = STG_E_INVALIDPARAMETER;
538 * Validate the STGM flags
540 if ( FAILED( validateSTGM(grfMode) ))
542 res = STG_E_INVALIDFLAG;
549 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
550 (grfMode & STGM_DELETEONRELEASE) ||
551 (grfMode & STGM_PRIORITY) )
553 res = STG_E_INVALIDFUNCTION;
558 * Check that we're compatible with the parent's storage mode,
559 * but only if we are not transacted
561 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
562 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
564 res = STG_E_ACCESSDENIED;
570 * Initialize the out parameter
575 * Create a property enumeration to search the properties
577 propertyEnumeration = IEnumSTATSTGImpl_Construct(
578 This->ancestorStorage,
579 This->rootPropertySetIndex);
582 * Search the enumeration for the property with the given name
584 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
590 * Delete the property enumeration since we don't need it anymore
592 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
595 * If it was found, construct the stream object and return a pointer to it.
597 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
598 (currentProperty.propertyType==PROPTYPE_STORAGE) )
601 * Construct a new Storage object
603 newStorage = StorageInternalImpl_Construct(
604 This->ancestorStorage,
610 *ppstg = (IStorage*)newStorage;
613 * Since we are returning a pointer to the interface,
614 * we have to nail down the reference.
616 StorageBaseImpl_AddRef(*ppstg);
622 res = STG_E_INSUFFICIENTMEMORY;
626 res = STG_E_FILENOTFOUND;
629 TRACE("<-- %08x\n", res);
633 /************************************************************************
634 * Storage32BaseImpl_EnumElements (IStorage)
636 * This method will create an enumerator object that can be used to
637 * retrieve informatino about all the properties in the storage object.
639 * See Windows documentation for more details on IStorage methods.
641 static HRESULT WINAPI StorageBaseImpl_EnumElements(
643 DWORD reserved1, /* [in] */
644 void* reserved2, /* [size_is][unique][in] */
645 DWORD reserved3, /* [in] */
646 IEnumSTATSTG** ppenum) /* [out] */
648 StorageBaseImpl *This = (StorageBaseImpl *)iface;
649 IEnumSTATSTGImpl* newEnum;
651 TRACE("(%p, %d, %p, %d, %p)\n",
652 iface, reserved1, reserved2, reserved3, ppenum);
655 * Perform a sanity check on the parameters.
657 if ( (This==0) || (ppenum==0))
661 * Construct the enumerator.
663 newEnum = IEnumSTATSTGImpl_Construct(
664 This->ancestorStorage,
665 This->rootPropertySetIndex);
669 *ppenum = (IEnumSTATSTG*)newEnum;
672 * Don't forget to nail down a reference to the new object before
675 IEnumSTATSTG_AddRef(*ppenum);
680 return E_OUTOFMEMORY;
683 /************************************************************************
684 * Storage32BaseImpl_Stat (IStorage)
686 * This method will retrieve information about this storage object.
688 * See Windows documentation for more details on IStorage methods.
690 static HRESULT WINAPI StorageBaseImpl_Stat(
692 STATSTG* pstatstg, /* [out] */
693 DWORD grfStatFlag) /* [in] */
695 StorageBaseImpl *This = (StorageBaseImpl *)iface;
696 StgProperty curProperty;
698 HRESULT res = STG_E_UNKNOWN;
700 TRACE("(%p, %p, %x)\n",
701 iface, pstatstg, grfStatFlag);
704 * Perform a sanity check on the parameters.
706 if ( (This==0) || (pstatstg==0))
713 * Read the information from the property.
715 readSuccessful = StorageImpl_ReadProperty(
716 This->ancestorStorage,
717 This->rootPropertySetIndex,
722 StorageUtl_CopyPropertyToSTATSTG(
727 pstatstg->grfMode = This->openFlags;
738 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);
740 TRACE("<-- %08x\n", res);
744 /************************************************************************
745 * Storage32BaseImpl_RenameElement (IStorage)
747 * This method will rename the specified element.
749 * See Windows documentation for more details on IStorage methods.
751 * Implementation notes: The method used to rename consists of creating a clone
752 * of the deleted StgProperty object setting it with the new name and to
753 * perform a DestroyElement of the old StgProperty.
755 static HRESULT WINAPI StorageBaseImpl_RenameElement(
757 const OLECHAR* pwcsOldName, /* [in] */
758 const OLECHAR* pwcsNewName) /* [in] */
760 StorageBaseImpl *This = (StorageBaseImpl *)iface;
761 IEnumSTATSTGImpl* propertyEnumeration;
762 StgProperty currentProperty;
763 ULONG foundPropertyIndex;
765 TRACE("(%p, %s, %s)\n",
766 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
769 * Create a property enumeration to search the properties
771 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
772 This->rootPropertySetIndex);
775 * Search the enumeration for the new property name
777 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
781 if (foundPropertyIndex != PROPERTY_NULL)
784 * There is already a property with the new name
786 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
787 return STG_E_FILEALREADYEXISTS;
790 IEnumSTATSTG_Reset((IEnumSTATSTG*)propertyEnumeration);
793 * Search the enumeration for the old property name
795 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
800 * Delete the property enumeration since we don't need it anymore
802 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
804 if (foundPropertyIndex != PROPERTY_NULL)
806 StgProperty renamedProperty;
807 ULONG renamedPropertyIndex;
810 * Setup a new property for the renamed property
812 renamedProperty.sizeOfNameString =
813 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
815 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
816 return STG_E_INVALIDNAME;
818 strcpyW(renamedProperty.name, pwcsNewName);
820 renamedProperty.propertyType = currentProperty.propertyType;
821 renamedProperty.startingBlock = currentProperty.startingBlock;
822 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
823 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
825 renamedProperty.previousProperty = PROPERTY_NULL;
826 renamedProperty.nextProperty = PROPERTY_NULL;
829 * Bring the dirProperty link in case it is a storage and in which
830 * case the renamed storage elements don't require to be reorganized.
832 renamedProperty.dirProperty = currentProperty.dirProperty;
834 /* call CoFileTime to get the current time
835 renamedProperty.timeStampS1
836 renamedProperty.timeStampD1
837 renamedProperty.timeStampS2
838 renamedProperty.timeStampD2
839 renamedProperty.propertyUniqueID
843 * Obtain a free property in the property chain
845 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
848 * Save the new property into the new property spot
850 StorageImpl_WriteProperty(
851 This->ancestorStorage,
852 renamedPropertyIndex,
856 * Find a spot in the property chain for our newly created property.
860 renamedPropertyIndex,
864 * At this point the renamed property has been inserted in the tree,
865 * now, before Destroying the old property we must zero its dirProperty
866 * otherwise the DestroyProperty below will zap it all and we do not want
868 * Also, we fake that the old property is a storage so the DestroyProperty
869 * will not do a SetSize(0) on the stream data.
871 * This means that we need to tweak the StgProperty if it is a stream or a
874 StorageImpl_ReadProperty(This->ancestorStorage,
878 currentProperty.dirProperty = PROPERTY_NULL;
879 currentProperty.propertyType = PROPTYPE_STORAGE;
880 StorageImpl_WriteProperty(
881 This->ancestorStorage,
886 * Invoke Destroy to get rid of the ole property and automatically redo
887 * the linking of its previous and next members...
889 IStorage_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
895 * There is no property with the old name
897 return STG_E_FILENOTFOUND;
903 /************************************************************************
904 * Storage32BaseImpl_CreateStream (IStorage)
906 * This method will create a stream object within this storage
908 * See Windows documentation for more details on IStorage methods.
910 static HRESULT WINAPI StorageBaseImpl_CreateStream(
912 const OLECHAR* pwcsName, /* [string][in] */
913 DWORD grfMode, /* [in] */
914 DWORD reserved1, /* [in] */
915 DWORD reserved2, /* [in] */
916 IStream** ppstm) /* [out] */
918 StorageBaseImpl *This = (StorageBaseImpl *)iface;
919 IEnumSTATSTGImpl* propertyEnumeration;
920 StgStreamImpl* newStream;
921 StgProperty currentProperty, newStreamProperty;
922 ULONG foundPropertyIndex, newPropertyIndex;
924 TRACE("(%p, %s, %x, %d, %d, %p)\n",
925 iface, debugstr_w(pwcsName), grfMode,
926 reserved1, reserved2, ppstm);
929 * Validate parameters
932 return STG_E_INVALIDPOINTER;
935 return STG_E_INVALIDNAME;
937 if (reserved1 || reserved2)
938 return STG_E_INVALIDPARAMETER;
941 * Validate the STGM flags
943 if ( FAILED( validateSTGM(grfMode) ))
944 return STG_E_INVALIDFLAG;
946 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
947 return STG_E_INVALIDFLAG;
952 if ((grfMode & STGM_DELETEONRELEASE) ||
953 (grfMode & STGM_TRANSACTED))
954 return STG_E_INVALIDFUNCTION;
957 * Check that we're compatible with the parent's storage mode
958 * if not in transacted mode
960 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
961 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
962 return STG_E_ACCESSDENIED;
966 * Initialize the out parameter
971 * Create a property enumeration to search the properties
973 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
974 This->rootPropertySetIndex);
976 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
980 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
982 if (foundPropertyIndex != PROPERTY_NULL)
985 * An element with this name already exists
987 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
989 IStorage_DestroyElement(iface, pwcsName);
992 return STG_E_FILEALREADYEXISTS;
994 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
996 WARN("read-only storage\n");
997 return STG_E_ACCESSDENIED;
1001 * memset the empty property
1003 memset(&newStreamProperty, 0, sizeof(StgProperty));
1005 newStreamProperty.sizeOfNameString =
1006 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
1008 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1009 return STG_E_INVALIDNAME;
1011 strcpyW(newStreamProperty.name, pwcsName);
1013 newStreamProperty.propertyType = PROPTYPE_STREAM;
1014 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
1015 newStreamProperty.size.u.LowPart = 0;
1016 newStreamProperty.size.u.HighPart = 0;
1018 newStreamProperty.previousProperty = PROPERTY_NULL;
1019 newStreamProperty.nextProperty = PROPERTY_NULL;
1020 newStreamProperty.dirProperty = PROPERTY_NULL;
1022 /* call CoFileTime to get the current time
1023 newStreamProperty.timeStampS1
1024 newStreamProperty.timeStampD1
1025 newStreamProperty.timeStampS2
1026 newStreamProperty.timeStampD2
1029 /* newStreamProperty.propertyUniqueID */
1032 * Get a free property or create a new one
1034 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1037 * Save the new property into the new property spot
1039 StorageImpl_WriteProperty(
1040 This->ancestorStorage,
1042 &newStreamProperty);
1045 * Find a spot in the property chain for our newly created property.
1047 updatePropertyChain(
1053 * Open the stream to return it.
1055 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
1059 *ppstm = (IStream*)newStream;
1062 * Since we are returning a pointer to the interface, we have to nail down
1065 IStream_AddRef(*ppstm);
1067 /* add us to the storage's list of active streams
1069 StorageBaseImpl_AddStream(This,newStream);
1074 return STG_E_INSUFFICIENTMEMORY;
1080 /************************************************************************
1081 * Storage32BaseImpl_SetClass (IStorage)
1083 * This method will write the specified CLSID in the property of this
1086 * See Windows documentation for more details on IStorage methods.
1088 static HRESULT WINAPI StorageBaseImpl_SetClass(
1090 REFCLSID clsid) /* [in] */
1092 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1093 HRESULT hRes = E_FAIL;
1094 StgProperty curProperty;
1097 TRACE("(%p, %p)\n", iface, clsid);
1099 success = StorageImpl_ReadProperty(This->ancestorStorage,
1100 This->rootPropertySetIndex,
1104 curProperty.propertyUniqueID = *clsid;
1106 success = StorageImpl_WriteProperty(This->ancestorStorage,
1107 This->rootPropertySetIndex,
1116 /************************************************************************
1117 ** Storage32Impl implementation
1120 /************************************************************************
1121 * Storage32Impl_CreateStorage (IStorage)
1123 * This method will create the storage object within the provided storage.
1125 * See Windows documentation for more details on IStorage methods.
1127 static HRESULT WINAPI StorageImpl_CreateStorage(
1129 const OLECHAR *pwcsName, /* [string][in] */
1130 DWORD grfMode, /* [in] */
1131 DWORD reserved1, /* [in] */
1132 DWORD reserved2, /* [in] */
1133 IStorage **ppstg) /* [out] */
1135 StorageImpl* const This=(StorageImpl*)iface;
1137 IEnumSTATSTGImpl *propertyEnumeration;
1138 StgProperty currentProperty;
1139 StgProperty newProperty;
1140 ULONG foundPropertyIndex;
1141 ULONG newPropertyIndex;
1144 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1145 iface, debugstr_w(pwcsName), grfMode,
1146 reserved1, reserved2, ppstg);
1149 * Validate parameters
1152 return STG_E_INVALIDPOINTER;
1155 return STG_E_INVALIDNAME;
1158 * Initialize the out parameter
1163 * Validate the STGM flags
1165 if ( FAILED( validateSTGM(grfMode) ) ||
1166 (grfMode & STGM_DELETEONRELEASE) )
1168 WARN("bad grfMode: 0x%x\n", grfMode);
1169 return STG_E_INVALIDFLAG;
1173 * Check that we're compatible with the parent's storage mode
1175 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->base.openFlags ) )
1177 WARN("access denied\n");
1178 return STG_E_ACCESSDENIED;
1182 * Create a property enumeration and search the properties
1184 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1185 This->base.rootPropertySetIndex);
1187 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1190 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1192 if (foundPropertyIndex != PROPERTY_NULL)
1195 * An element with this name already exists
1197 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1198 IStorage_DestroyElement(iface, pwcsName);
1201 WARN("file already exists\n");
1202 return STG_E_FILEALREADYEXISTS;
1205 else if (STGM_ACCESS_MODE(This->base.openFlags) == STGM_READ)
1207 WARN("read-only storage\n");
1208 return STG_E_ACCESSDENIED;
1212 * memset the empty property
1214 memset(&newProperty, 0, sizeof(StgProperty));
1216 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1218 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1220 FIXME("name too long\n");
1221 return STG_E_INVALIDNAME;
1224 strcpyW(newProperty.name, pwcsName);
1226 newProperty.propertyType = PROPTYPE_STORAGE;
1227 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1228 newProperty.size.u.LowPart = 0;
1229 newProperty.size.u.HighPart = 0;
1231 newProperty.previousProperty = PROPERTY_NULL;
1232 newProperty.nextProperty = PROPERTY_NULL;
1233 newProperty.dirProperty = PROPERTY_NULL;
1235 /* call CoFileTime to get the current time
1236 newProperty.timeStampS1
1237 newProperty.timeStampD1
1238 newProperty.timeStampS2
1239 newProperty.timeStampD2
1242 /* newStorageProperty.propertyUniqueID */
1245 * Obtain a free property in the property chain
1247 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1250 * Save the new property into the new property spot
1252 StorageImpl_WriteProperty(
1253 This->base.ancestorStorage,
1258 * Find a spot in the property chain for our newly created property.
1260 updatePropertyChain(
1266 * Open it to get a pointer to return.
1268 hr = IStorage_OpenStorage(
1270 (const OLECHAR*)pwcsName,
1277 if( (hr != S_OK) || (*ppstg == NULL))
1287 /***************************************************************************
1291 * Get a free property or create a new one.
1293 static ULONG getFreeProperty(
1294 StorageImpl *storage)
1296 ULONG currentPropertyIndex = 0;
1297 ULONG newPropertyIndex = PROPERTY_NULL;
1298 BOOL readSuccessful = TRUE;
1299 StgProperty currentProperty;
1304 * Start by reading the root property
1306 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1307 currentPropertyIndex,
1311 if (currentProperty.sizeOfNameString == 0)
1314 * The property existis and is available, we found it.
1316 newPropertyIndex = currentPropertyIndex;
1322 * We exhausted the property list, we will create more space below
1324 newPropertyIndex = currentPropertyIndex;
1326 currentPropertyIndex++;
1328 } while (newPropertyIndex == PROPERTY_NULL);
1331 * grow the property chain
1333 if (! readSuccessful)
1335 StgProperty emptyProperty;
1336 ULARGE_INTEGER newSize;
1337 ULONG propertyIndex;
1338 ULONG lastProperty = 0;
1339 ULONG blockCount = 0;
1342 * obtain the new count of property blocks
1344 blockCount = BlockChainStream_GetCount(
1345 storage->base.ancestorStorage->rootBlockChain)+1;
1348 * initialize the size used by the property stream
1350 newSize.u.HighPart = 0;
1351 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1354 * add a property block to the property chain
1356 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1359 * memset the empty property in order to initialize the unused newly
1362 memset(&emptyProperty, 0, sizeof(StgProperty));
1367 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1370 propertyIndex = newPropertyIndex;
1371 propertyIndex < lastProperty;
1374 StorageImpl_WriteProperty(
1375 storage->base.ancestorStorage,
1381 return newPropertyIndex;
1384 /****************************************************************************
1388 * Case insensitive comparaison of StgProperty.name by first considering
1391 * Returns <0 when newPrpoerty < currentProperty
1392 * >0 when newPrpoerty > currentProperty
1393 * 0 when newPrpoerty == currentProperty
1395 static LONG propertyNameCmp(
1396 const OLECHAR *newProperty,
1397 const OLECHAR *currentProperty)
1399 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1404 * We compare the string themselves only when they are of the same length
1406 diff = lstrcmpiW( newProperty, currentProperty);
1412 /****************************************************************************
1416 * Properly link this new element in the property chain.
1418 static void updatePropertyChain(
1419 StorageImpl *storage,
1420 ULONG newPropertyIndex,
1421 StgProperty newProperty)
1423 StgProperty currentProperty;
1426 * Read the root property
1428 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1429 storage->base.rootPropertySetIndex,
1432 if (currentProperty.dirProperty != PROPERTY_NULL)
1435 * The root storage contains some element, therefore, start the research
1436 * for the appropriate location.
1439 ULONG current, next, previous, currentPropertyId;
1442 * Keep the StgProperty sequence number of the storage first property
1444 currentPropertyId = currentProperty.dirProperty;
1449 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1450 currentProperty.dirProperty,
1453 previous = currentProperty.previousProperty;
1454 next = currentProperty.nextProperty;
1455 current = currentPropertyId;
1459 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1463 if (previous != PROPERTY_NULL)
1465 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1472 currentProperty.previousProperty = newPropertyIndex;
1473 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1481 if (next != PROPERTY_NULL)
1483 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1490 currentProperty.nextProperty = newPropertyIndex;
1491 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1500 * Trying to insert an item with the same name in the
1501 * subtree structure.
1506 previous = currentProperty.previousProperty;
1507 next = currentProperty.nextProperty;
1513 * The root storage is empty, link the new property to its dir property
1515 currentProperty.dirProperty = newPropertyIndex;
1516 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1517 storage->base.rootPropertySetIndex,
1523 /*************************************************************************
1526 static HRESULT WINAPI StorageImpl_CopyTo(
1528 DWORD ciidExclude, /* [in] */
1529 const IID* rgiidExclude, /* [size_is][unique][in] */
1530 SNB snbExclude, /* [unique][in] */
1531 IStorage* pstgDest) /* [unique][in] */
1533 IEnumSTATSTG *elements = 0;
1534 STATSTG curElement, strStat;
1536 IStorage *pstgTmp, *pstgChild;
1537 IStream *pstrTmp, *pstrChild;
1539 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1540 FIXME("Exclude option not implemented\n");
1542 TRACE("(%p, %d, %p, %p, %p)\n",
1543 iface, ciidExclude, rgiidExclude,
1544 snbExclude, pstgDest);
1547 * Perform a sanity check
1549 if ( pstgDest == 0 )
1550 return STG_E_INVALIDPOINTER;
1553 * Enumerate the elements
1555 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1563 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1564 IStorage_SetClass( pstgDest, &curElement.clsid );
1569 * Obtain the next element
1571 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1573 if ( hr == S_FALSE )
1575 hr = S_OK; /* done, every element has been copied */
1579 if (curElement.type == STGTY_STORAGE)
1582 * open child source storage
1584 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1585 STGM_READ|STGM_SHARE_EXCLUSIVE,
1586 NULL, 0, &pstgChild );
1592 * Check if destination storage is not a child of the source
1593 * storage, which will cause an infinite loop
1595 if (pstgChild == pstgDest)
1597 IEnumSTATSTG_Release(elements);
1599 return STG_E_ACCESSDENIED;
1603 * create a new storage in destination storage
1605 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1606 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1610 * if it already exist, don't create a new one use this one
1612 if (hr == STG_E_FILEALREADYEXISTS)
1614 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1615 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1616 NULL, 0, &pstgTmp );
1624 * do the copy recursively
1626 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1627 snbExclude, pstgTmp );
1629 IStorage_Release( pstgTmp );
1630 IStorage_Release( pstgChild );
1632 else if (curElement.type == STGTY_STREAM)
1635 * create a new stream in destination storage. If the stream already
1636 * exist, it will be deleted and a new one will be created.
1638 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1639 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1646 * open child stream storage
1648 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1649 STGM_READ|STGM_SHARE_EXCLUSIVE,
1656 * Get the size of the source stream
1658 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1661 * Set the size of the destination stream.
1663 IStream_SetSize(pstrTmp, strStat.cbSize);
1668 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1671 IStream_Release( pstrTmp );
1672 IStream_Release( pstrChild );
1676 WARN("unknown element type: %d\n", curElement.type);
1679 } while (hr == S_OK);
1684 IEnumSTATSTG_Release(elements);
1689 /*************************************************************************
1690 * MoveElementTo (IStorage)
1692 static HRESULT WINAPI StorageImpl_MoveElementTo(
1694 const OLECHAR *pwcsName, /* [string][in] */
1695 IStorage *pstgDest, /* [unique][in] */
1696 const OLECHAR *pwcsNewName,/* [string][in] */
1697 DWORD grfFlags) /* [in] */
1699 FIXME("(%p %s %p %s %u): stub\n", iface,
1700 debugstr_w(pwcsName), pstgDest,
1701 debugstr_w(pwcsNewName), grfFlags);
1705 /*************************************************************************
1708 * Ensures that any changes made to a storage object open in transacted mode
1709 * are reflected in the parent storage
1712 * Wine doesn't implement transacted mode, which seems to be a basic
1713 * optimization, so we can ignore this stub for now.
1715 static HRESULT WINAPI StorageImpl_Commit(
1717 DWORD grfCommitFlags)/* [in] */
1719 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1723 /*************************************************************************
1726 * Discard all changes that have been made since the last commit operation
1728 static HRESULT WINAPI StorageImpl_Revert(
1731 FIXME("(%p): stub\n", iface);
1735 /*************************************************************************
1736 * DestroyElement (IStorage)
1738 * Strategy: This implementation is built this way for simplicity not for speed.
1739 * I always delete the topmost element of the enumeration and adjust
1740 * the deleted element pointer all the time. This takes longer to
1741 * do but allow to reinvoke DestroyElement whenever we encounter a
1742 * storage object. The optimisation resides in the usage of another
1743 * enumeration strategy that would give all the leaves of a storage
1744 * first. (postfix order)
1746 static HRESULT WINAPI StorageImpl_DestroyElement(
1748 const OLECHAR *pwcsName)/* [string][in] */
1750 StorageImpl* const This=(StorageImpl*)iface;
1752 IEnumSTATSTGImpl* propertyEnumeration;
1755 StgProperty propertyToDelete;
1756 StgProperty parentProperty;
1757 ULONG foundPropertyIndexToDelete;
1758 ULONG typeOfRelation;
1759 ULONG parentPropertyId = 0;
1762 iface, debugstr_w(pwcsName));
1765 * Perform a sanity check on the parameters.
1768 return STG_E_INVALIDPOINTER;
1771 * Create a property enumeration to search the property with the given name
1773 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1774 This->base.ancestorStorage,
1775 This->base.rootPropertySetIndex);
1777 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1778 propertyEnumeration,
1782 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1784 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1786 return STG_E_FILENOTFOUND;
1790 * Find the parent property of the property to delete (the one that
1791 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1792 * the parent is This. Otherwise, the parent is one of its sibling...
1796 * First, read This's StgProperty..
1798 res = StorageImpl_ReadProperty(
1799 This->base.ancestorStorage,
1800 This->base.rootPropertySetIndex,
1806 * Second, check to see if by any chance the actual storage (This) is not
1807 * the parent of the property to delete... We never know...
1809 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1812 * Set data as it would have been done in the else part...
1814 typeOfRelation = PROPERTY_RELATION_DIR;
1815 parentPropertyId = This->base.rootPropertySetIndex;
1820 * Create a property enumeration to search the parent properties, and
1821 * delete it once done.
1823 IEnumSTATSTGImpl* propertyEnumeration2;
1825 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1826 This->base.ancestorStorage,
1827 This->base.rootPropertySetIndex);
1829 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1830 propertyEnumeration2,
1831 foundPropertyIndexToDelete,
1835 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1838 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1840 hr = deleteStorageProperty(
1842 foundPropertyIndexToDelete,
1845 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1847 hr = deleteStreamProperty(
1849 foundPropertyIndexToDelete,
1857 * Adjust the property chain
1859 hr = adjustPropertyChain(
1870 /************************************************************************
1871 * StorageImpl_Stat (IStorage)
1873 * This method will retrieve information about this storage object.
1875 * See Windows documentation for more details on IStorage methods.
1877 static HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1878 STATSTG* pstatstg, /* [out] */
1879 DWORD grfStatFlag) /* [in] */
1881 StorageImpl* const This = (StorageImpl*)iface;
1882 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1884 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1886 CoTaskMemFree(pstatstg->pwcsName);
1887 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1888 strcpyW(pstatstg->pwcsName, This->pwcsName);
1894 /******************************************************************************
1895 * Internal stream list handlers
1898 static void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1900 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1901 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1904 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1906 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1907 list_remove(&(strm->StrmListEntry));
1910 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1912 struct list *cur, *cur2;
1913 StgStreamImpl *strm=NULL;
1915 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1916 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1917 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1918 strm->parentStorage = NULL;
1924 /*********************************************************************
1928 * Perform the deletion of a complete storage node
1931 static HRESULT deleteStorageProperty(
1932 StorageImpl *parentStorage,
1933 ULONG indexOfPropertyToDelete,
1934 StgProperty propertyToDelete)
1936 IEnumSTATSTG *elements = 0;
1937 IStorage *childStorage = 0;
1938 STATSTG currentElement;
1940 HRESULT destroyHr = S_OK;
1943 * Open the storage and enumerate it
1945 hr = StorageBaseImpl_OpenStorage(
1946 (IStorage*)parentStorage,
1947 propertyToDelete.name,
1949 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1960 * Enumerate the elements
1962 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1967 * Obtain the next element
1969 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
1972 destroyHr = StorageImpl_DestroyElement(
1973 (IStorage*)childStorage,
1974 (OLECHAR*)currentElement.pwcsName);
1976 CoTaskMemFree(currentElement.pwcsName);
1980 * We need to Reset the enumeration every time because we delete elements
1981 * and the enumeration could be invalid
1983 IEnumSTATSTG_Reset(elements);
1985 } while ((hr == S_OK) && (destroyHr == S_OK));
1988 * Invalidate the property by zeroing its name member.
1990 propertyToDelete.sizeOfNameString = 0;
1992 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
1993 indexOfPropertyToDelete,
1996 IStorage_Release(childStorage);
1997 IEnumSTATSTG_Release(elements);
2002 /*********************************************************************
2006 * Perform the deletion of a stream node
2009 static HRESULT deleteStreamProperty(
2010 StorageImpl *parentStorage,
2011 ULONG indexOfPropertyToDelete,
2012 StgProperty propertyToDelete)
2016 ULARGE_INTEGER size;
2018 size.u.HighPart = 0;
2021 hr = StorageBaseImpl_OpenStream(
2022 (IStorage*)parentStorage,
2023 (OLECHAR*)propertyToDelete.name,
2025 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2037 hr = IStream_SetSize(pis, size);
2045 * Release the stream object.
2047 IStream_Release(pis);
2050 * Invalidate the property by zeroing its name member.
2052 propertyToDelete.sizeOfNameString = 0;
2055 * Here we should re-read the property so we get the updated pointer
2056 * but since we are here to zap it, I don't do it...
2058 StorageImpl_WriteProperty(
2059 parentStorage->base.ancestorStorage,
2060 indexOfPropertyToDelete,
2066 /*********************************************************************
2070 * Finds a placeholder for the StgProperty within the Storage
2073 static HRESULT findPlaceholder(
2074 StorageImpl *storage,
2075 ULONG propertyIndexToStore,
2076 ULONG storePropertyIndex,
2079 StgProperty storeProperty;
2084 * Read the storage property
2086 res = StorageImpl_ReadProperty(
2087 storage->base.ancestorStorage,
2096 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2098 if (storeProperty.previousProperty != PROPERTY_NULL)
2100 return findPlaceholder(
2102 propertyIndexToStore,
2103 storeProperty.previousProperty,
2108 storeProperty.previousProperty = propertyIndexToStore;
2111 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2113 if (storeProperty.nextProperty != PROPERTY_NULL)
2115 return findPlaceholder(
2117 propertyIndexToStore,
2118 storeProperty.nextProperty,
2123 storeProperty.nextProperty = propertyIndexToStore;
2126 else if (typeOfRelation == PROPERTY_RELATION_DIR)
2128 if (storeProperty.dirProperty != PROPERTY_NULL)
2130 return findPlaceholder(
2132 propertyIndexToStore,
2133 storeProperty.dirProperty,
2138 storeProperty.dirProperty = propertyIndexToStore;
2142 hr = StorageImpl_WriteProperty(
2143 storage->base.ancestorStorage,
2155 /*************************************************************************
2159 * This method takes the previous and the next property link of a property
2160 * to be deleted and find them a place in the Storage.
2162 static HRESULT adjustPropertyChain(
2164 StgProperty propertyToDelete,
2165 StgProperty parentProperty,
2166 ULONG parentPropertyId,
2169 ULONG newLinkProperty = PROPERTY_NULL;
2170 BOOL needToFindAPlaceholder = FALSE;
2171 ULONG storeNode = PROPERTY_NULL;
2172 ULONG toStoreNode = PROPERTY_NULL;
2173 INT relationType = 0;
2177 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2179 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2182 * Set the parent previous to the property to delete previous
2184 newLinkProperty = propertyToDelete.previousProperty;
2186 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2189 * We also need to find a storage for the other link, setup variables
2190 * to do this at the end...
2192 needToFindAPlaceholder = TRUE;
2193 storeNode = propertyToDelete.previousProperty;
2194 toStoreNode = propertyToDelete.nextProperty;
2195 relationType = PROPERTY_RELATION_NEXT;
2198 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2201 * Set the parent previous to the property to delete next
2203 newLinkProperty = propertyToDelete.nextProperty;
2207 * Link it for real...
2209 parentProperty.previousProperty = newLinkProperty;
2212 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2214 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2217 * Set the parent next to the property to delete next previous
2219 newLinkProperty = propertyToDelete.previousProperty;
2221 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2224 * We also need to find a storage for the other link, setup variables
2225 * to do this at the end...
2227 needToFindAPlaceholder = TRUE;
2228 storeNode = propertyToDelete.previousProperty;
2229 toStoreNode = propertyToDelete.nextProperty;
2230 relationType = PROPERTY_RELATION_NEXT;
2233 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2236 * Set the parent next to the property to delete next
2238 newLinkProperty = propertyToDelete.nextProperty;
2242 * Link it for real...
2244 parentProperty.nextProperty = newLinkProperty;
2246 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2248 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2251 * Set the parent dir to the property to delete previous
2253 newLinkProperty = propertyToDelete.previousProperty;
2255 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2258 * We also need to find a storage for the other link, setup variables
2259 * to do this at the end...
2261 needToFindAPlaceholder = TRUE;
2262 storeNode = propertyToDelete.previousProperty;
2263 toStoreNode = propertyToDelete.nextProperty;
2264 relationType = PROPERTY_RELATION_NEXT;
2267 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2270 * Set the parent dir to the property to delete next
2272 newLinkProperty = propertyToDelete.nextProperty;
2276 * Link it for real...
2278 parentProperty.dirProperty = newLinkProperty;
2282 * Write back the parent property
2284 res = StorageImpl_WriteProperty(
2285 This->base.ancestorStorage,
2294 * If a placeholder is required for the other link, then, find one and
2295 * get out of here...
2297 if (needToFindAPlaceholder)
2299 hr = findPlaceholder(
2310 /******************************************************************************
2311 * SetElementTimes (IStorage)
2313 static HRESULT WINAPI StorageImpl_SetElementTimes(
2315 const OLECHAR *pwcsName,/* [string][in] */
2316 const FILETIME *pctime, /* [in] */
2317 const FILETIME *patime, /* [in] */
2318 const FILETIME *pmtime) /* [in] */
2320 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2324 /******************************************************************************
2325 * SetStateBits (IStorage)
2327 static HRESULT WINAPI StorageImpl_SetStateBits(
2329 DWORD grfStateBits,/* [in] */
2330 DWORD grfMask) /* [in] */
2332 FIXME("not implemented!\n");
2337 * Virtual function table for the IStorage32Impl class.
2339 static const IStorageVtbl Storage32Impl_Vtbl =
2341 StorageBaseImpl_QueryInterface,
2342 StorageBaseImpl_AddRef,
2343 StorageBaseImpl_Release,
2344 StorageBaseImpl_CreateStream,
2345 StorageBaseImpl_OpenStream,
2346 StorageImpl_CreateStorage,
2347 StorageBaseImpl_OpenStorage,
2349 StorageImpl_MoveElementTo,
2352 StorageBaseImpl_EnumElements,
2353 StorageImpl_DestroyElement,
2354 StorageBaseImpl_RenameElement,
2355 StorageImpl_SetElementTimes,
2356 StorageBaseImpl_SetClass,
2357 StorageImpl_SetStateBits,
2361 static HRESULT StorageImpl_Construct(
2371 StgProperty currentProperty;
2372 BOOL readSuccessful;
2373 ULONG currentPropertyIndex;
2375 if ( FAILED( validateSTGM(openFlags) ))
2376 return STG_E_INVALIDFLAG;
2378 memset(This, 0, sizeof(StorageImpl));
2381 * Initialize stream list
2384 list_init(&This->base.strmHead);
2387 * Initialize the virtual function table.
2389 This->base.lpVtbl = &Storage32Impl_Vtbl;
2390 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2391 This->base.v_destructor = &StorageImpl_Destroy;
2392 This->base.openFlags = (openFlags & ~STGM_CREATE);
2395 * This is the top-level storage so initialize the ancestor pointer
2398 This->base.ancestorStorage = This;
2401 * Initialize the physical support of the storage.
2403 This->hFile = hFile;
2406 * Store copy of file path.
2409 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2410 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2411 if (!This->pwcsName)
2412 return STG_E_INSUFFICIENTMEMORY;
2413 strcpyW(This->pwcsName, pwcsName);
2417 * Initialize the big block cache.
2419 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2420 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2421 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2427 if (This->bigBlockFile == 0)
2432 ULARGE_INTEGER size;
2433 BYTE* bigBlockBuffer;
2436 * Initialize all header variables:
2437 * - The big block depot consists of one block and it is at block 0
2438 * - The properties start at block 1
2439 * - There is no small block depot
2441 memset( This->bigBlockDepotStart,
2443 sizeof(This->bigBlockDepotStart));
2445 This->bigBlockDepotCount = 1;
2446 This->bigBlockDepotStart[0] = 0;
2447 This->rootStartBlock = 1;
2448 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2449 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2450 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2451 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2452 This->extBigBlockDepotCount = 0;
2454 StorageImpl_SaveFileHeader(This);
2457 * Add one block for the big block depot and one block for the properties
2459 size.u.HighPart = 0;
2460 size.u.LowPart = This->bigBlockSize * 3;
2461 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2464 * Initialize the big block depot
2466 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2467 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2468 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2469 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2470 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2475 * Load the header for the file.
2477 hr = StorageImpl_LoadFileHeader(This);
2481 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2488 * There is no block depot cached yet.
2490 This->indexBlockDepotCached = 0xFFFFFFFF;
2493 * Start searching for free blocks with block 0.
2495 This->prevFreeBlock = 0;
2498 * Create the block chain abstractions.
2500 if(!(This->rootBlockChain =
2501 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2502 return STG_E_READFAULT;
2504 if(!(This->smallBlockDepotChain =
2505 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2507 return STG_E_READFAULT;
2510 * Write the root property (memory only)
2514 StgProperty rootProp;
2516 * Initialize the property chain
2518 memset(&rootProp, 0, sizeof(rootProp));
2519 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2520 sizeof(rootProp.name)/sizeof(WCHAR) );
2521 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2522 rootProp.propertyType = PROPTYPE_ROOT;
2523 rootProp.previousProperty = PROPERTY_NULL;
2524 rootProp.nextProperty = PROPERTY_NULL;
2525 rootProp.dirProperty = PROPERTY_NULL;
2526 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2527 rootProp.size.u.HighPart = 0;
2528 rootProp.size.u.LowPart = 0;
2530 StorageImpl_WriteProperty(This, 0, &rootProp);
2534 * Find the ID of the root in the property sets.
2536 currentPropertyIndex = 0;
2540 readSuccessful = StorageImpl_ReadProperty(
2542 currentPropertyIndex,
2547 if ( (currentProperty.sizeOfNameString != 0 ) &&
2548 (currentProperty.propertyType == PROPTYPE_ROOT) )
2550 This->base.rootPropertySetIndex = currentPropertyIndex;
2554 currentPropertyIndex++;
2556 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2558 if (!readSuccessful)
2561 return STG_E_READFAULT;
2565 * Create the block chain abstraction for the small block root chain.
2567 if(!(This->smallBlockRootChain =
2568 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2569 return STG_E_READFAULT;
2574 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2576 StorageImpl *This = (StorageImpl*) iface;
2577 TRACE("(%p)\n", This);
2579 StorageBaseImpl_DeleteAll(&This->base);
2581 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2583 BlockChainStream_Destroy(This->smallBlockRootChain);
2584 BlockChainStream_Destroy(This->rootBlockChain);
2585 BlockChainStream_Destroy(This->smallBlockDepotChain);
2587 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2588 HeapFree(GetProcessHeap(), 0, This);
2591 /******************************************************************************
2592 * Storage32Impl_GetNextFreeBigBlock
2594 * Returns the index of the next free big block.
2595 * If the big block depot is filled, this method will enlarge it.
2598 static ULONG StorageImpl_GetNextFreeBigBlock(
2601 ULONG depotBlockIndexPos;
2603 ULONG depotBlockOffset;
2604 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2605 ULONG nextBlockIndex = BLOCK_SPECIAL;
2607 ULONG freeBlock = BLOCK_UNUSED;
2609 depotIndex = This->prevFreeBlock / blocksPerDepot;
2610 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2613 * Scan the entire big block depot until we find a block marked free
2615 while (nextBlockIndex != BLOCK_UNUSED)
2617 if (depotIndex < COUNT_BBDEPOTINHEADER)
2619 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2622 * Grow the primary depot.
2624 if (depotBlockIndexPos == BLOCK_UNUSED)
2626 depotBlockIndexPos = depotIndex*blocksPerDepot;
2629 * Add a block depot.
2631 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2632 This->bigBlockDepotCount++;
2633 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2636 * Flag it as a block depot.
2638 StorageImpl_SetNextBlockInChain(This,
2642 /* Save new header information.
2644 StorageImpl_SaveFileHeader(This);
2649 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2651 if (depotBlockIndexPos == BLOCK_UNUSED)
2654 * Grow the extended depot.
2656 ULONG extIndex = BLOCK_UNUSED;
2657 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2658 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2660 if (extBlockOffset == 0)
2662 /* We need an extended block.
2664 extIndex = Storage32Impl_AddExtBlockDepot(This);
2665 This->extBigBlockDepotCount++;
2666 depotBlockIndexPos = extIndex + 1;
2669 depotBlockIndexPos = depotIndex * blocksPerDepot;
2672 * Add a block depot and mark it in the extended block.
2674 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2675 This->bigBlockDepotCount++;
2676 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2678 /* Flag the block depot.
2680 StorageImpl_SetNextBlockInChain(This,
2684 /* If necessary, flag the extended depot block.
2686 if (extIndex != BLOCK_UNUSED)
2687 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2689 /* Save header information.
2691 StorageImpl_SaveFileHeader(This);
2695 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2697 if (depotBuffer != 0)
2699 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2700 ( nextBlockIndex != BLOCK_UNUSED))
2702 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2704 if (nextBlockIndex == BLOCK_UNUSED)
2706 freeBlock = (depotIndex * blocksPerDepot) +
2707 (depotBlockOffset/sizeof(ULONG));
2710 depotBlockOffset += sizeof(ULONG);
2713 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2717 depotBlockOffset = 0;
2721 * make sure that the block physically exists before using it
2723 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2725 This->prevFreeBlock = freeBlock;
2730 /******************************************************************************
2731 * Storage32Impl_AddBlockDepot
2733 * This will create a depot block, essentially it is a block initialized
2736 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2740 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2743 * Initialize blocks as free
2745 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2747 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2750 /******************************************************************************
2751 * Storage32Impl_GetExtDepotBlock
2753 * Returns the index of the block that corresponds to the specified depot
2754 * index. This method is only for depot indexes equal or greater than
2755 * COUNT_BBDEPOTINHEADER.
2757 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2759 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2760 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2761 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2762 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2763 ULONG blockIndex = BLOCK_UNUSED;
2764 ULONG extBlockIndex = This->extBigBlockDepotStart;
2766 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2768 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2769 return BLOCK_UNUSED;
2771 while (extBlockCount > 0)
2773 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2777 if (extBlockIndex != BLOCK_UNUSED)
2781 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2783 if (depotBuffer != 0)
2785 StorageUtl_ReadDWord(depotBuffer,
2786 extBlockOffset * sizeof(ULONG),
2789 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2796 /******************************************************************************
2797 * Storage32Impl_SetExtDepotBlock
2799 * Associates the specified block index to the specified depot index.
2800 * This method is only for depot indexes equal or greater than
2801 * COUNT_BBDEPOTINHEADER.
2803 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2805 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2806 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2807 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2808 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2809 ULONG extBlockIndex = This->extBigBlockDepotStart;
2811 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2813 while (extBlockCount > 0)
2815 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2819 if (extBlockIndex != BLOCK_UNUSED)
2823 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2825 if (depotBuffer != 0)
2827 StorageUtl_WriteDWord(depotBuffer,
2828 extBlockOffset * sizeof(ULONG),
2831 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2836 /******************************************************************************
2837 * Storage32Impl_AddExtBlockDepot
2839 * Creates an extended depot block.
2841 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2843 ULONG numExtBlocks = This->extBigBlockDepotCount;
2844 ULONG nextExtBlock = This->extBigBlockDepotStart;
2845 BYTE* depotBuffer = NULL;
2846 ULONG index = BLOCK_UNUSED;
2847 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2848 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2849 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2851 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2852 blocksPerDepotBlock;
2854 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2857 * The first extended block.
2859 This->extBigBlockDepotStart = index;
2865 * Follow the chain to the last one.
2867 for (i = 0; i < (numExtBlocks - 1); i++)
2869 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2873 * Add the new extended block to the chain.
2875 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2876 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2877 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2881 * Initialize this block.
2883 depotBuffer = StorageImpl_GetBigBlock(This, index);
2884 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2885 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2890 /******************************************************************************
2891 * Storage32Impl_FreeBigBlock
2893 * This method will flag the specified block as free in the big block depot.
2895 static void StorageImpl_FreeBigBlock(
2899 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2901 if (blockIndex < This->prevFreeBlock)
2902 This->prevFreeBlock = blockIndex;
2905 /************************************************************************
2906 * Storage32Impl_GetNextBlockInChain
2908 * This method will retrieve the block index of the next big block in
2911 * Params: This - Pointer to the Storage object.
2912 * blockIndex - Index of the block to retrieve the chain
2914 * nextBlockIndex - receives the return value.
2916 * Returns: This method returns the index of the next block in the chain.
2917 * It will return the constants:
2918 * BLOCK_SPECIAL - If the block given was not part of a
2920 * BLOCK_END_OF_CHAIN - If the block given was the last in
2922 * BLOCK_UNUSED - If the block given was not past of a chain
2924 * BLOCK_EXTBBDEPOT - This block is part of the extended
2927 * See Windows documentation for more details on IStorage methods.
2929 static HRESULT StorageImpl_GetNextBlockInChain(
2932 ULONG* nextBlockIndex)
2934 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2935 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2936 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2938 ULONG depotBlockIndexPos;
2941 *nextBlockIndex = BLOCK_SPECIAL;
2943 if(depotBlockCount >= This->bigBlockDepotCount)
2945 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
2946 This->bigBlockDepotCount);
2947 return STG_E_READFAULT;
2951 * Cache the currently accessed depot block.
2953 if (depotBlockCount != This->indexBlockDepotCached)
2955 This->indexBlockDepotCached = depotBlockCount;
2957 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2959 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2964 * We have to look in the extended depot.
2966 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2969 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2972 return STG_E_READFAULT;
2974 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2976 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2977 This->blockDepotCached[index] = *nextBlockIndex;
2979 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2982 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2987 /******************************************************************************
2988 * Storage32Impl_GetNextExtendedBlock
2990 * Given an extended block this method will return the next extended block.
2993 * The last ULONG of an extended block is the block index of the next
2994 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2998 * - The index of the next extended block
2999 * - BLOCK_UNUSED: there is no next extended block.
3000 * - Any other return values denotes failure.
3002 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3004 ULONG nextBlockIndex = BLOCK_SPECIAL;
3005 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3008 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3012 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
3014 StorageImpl_ReleaseBigBlock(This, depotBuffer);
3017 return nextBlockIndex;
3020 /******************************************************************************
3021 * Storage32Impl_SetNextBlockInChain
3023 * This method will write the index of the specified block's next block
3024 * in the big block depot.
3026 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3029 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3030 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3031 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3034 static void StorageImpl_SetNextBlockInChain(
3039 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3040 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3041 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3042 ULONG depotBlockIndexPos;
3045 assert(depotBlockCount < This->bigBlockDepotCount);
3046 assert(blockIndex != nextBlock);
3048 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3050 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3055 * We have to look in the extended depot.
3057 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3060 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
3064 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
3065 StorageImpl_ReleaseBigBlock(This, depotBuffer);
3069 * Update the cached block depot, if necessary.
3071 if (depotBlockCount == This->indexBlockDepotCached)
3073 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3077 /******************************************************************************
3078 * Storage32Impl_LoadFileHeader
3080 * This method will read in the file header, i.e. big block index -1.
3082 static HRESULT StorageImpl_LoadFileHeader(
3085 HRESULT hr = STG_E_FILENOTFOUND;
3086 void* headerBigBlock = NULL;
3091 * Get a pointer to the big block of data containing the header.
3093 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
3096 * Extract the information from the header.
3098 if (headerBigBlock!=0)
3101 * Check for the "magic number" signature and return an error if it is not
3104 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3106 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3107 return STG_E_OLDFORMAT;
3110 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3112 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3113 return STG_E_INVALIDHEADER;
3116 StorageUtl_ReadWord(
3118 OFFSET_BIGBLOCKSIZEBITS,
3119 &This->bigBlockSizeBits);
3121 StorageUtl_ReadWord(
3123 OFFSET_SMALLBLOCKSIZEBITS,
3124 &This->smallBlockSizeBits);
3126 StorageUtl_ReadDWord(
3128 OFFSET_BBDEPOTCOUNT,
3129 &This->bigBlockDepotCount);
3131 StorageUtl_ReadDWord(
3133 OFFSET_ROOTSTARTBLOCK,
3134 &This->rootStartBlock);
3136 StorageUtl_ReadDWord(
3138 OFFSET_SBDEPOTSTART,
3139 &This->smallBlockDepotStart);
3141 StorageUtl_ReadDWord(
3143 OFFSET_EXTBBDEPOTSTART,
3144 &This->extBigBlockDepotStart);
3146 StorageUtl_ReadDWord(
3148 OFFSET_EXTBBDEPOTCOUNT,
3149 &This->extBigBlockDepotCount);
3151 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3153 StorageUtl_ReadDWord(
3155 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3156 &(This->bigBlockDepotStart[index]));
3160 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3164 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3165 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3169 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
3170 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
3174 * Right now, the code is making some assumptions about the size of the
3175 * blocks, just make sure they are what we're expecting.
3177 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3178 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3180 WARN("Broken OLE storage file\n");
3181 hr = STG_E_INVALIDHEADER;
3187 * Release the block.
3189 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3195 /******************************************************************************
3196 * Storage32Impl_SaveFileHeader
3198 * This method will save to the file the header, i.e. big block -1.
3200 static void StorageImpl_SaveFileHeader(
3203 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3208 * Get a pointer to the big block of data containing the header.
3210 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3213 * If the block read failed, the file is probably new.
3218 * Initialize for all unknown fields.
3220 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3223 * Initialize the magic number.
3225 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3228 * And a bunch of things we don't know what they mean
3230 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3231 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3232 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3233 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3237 * Write the information to the header.
3239 StorageUtl_WriteWord(
3241 OFFSET_BIGBLOCKSIZEBITS,
3242 This->bigBlockSizeBits);
3244 StorageUtl_WriteWord(
3246 OFFSET_SMALLBLOCKSIZEBITS,
3247 This->smallBlockSizeBits);
3249 StorageUtl_WriteDWord(
3251 OFFSET_BBDEPOTCOUNT,
3252 This->bigBlockDepotCount);
3254 StorageUtl_WriteDWord(
3256 OFFSET_ROOTSTARTBLOCK,
3257 This->rootStartBlock);
3259 StorageUtl_WriteDWord(
3261 OFFSET_SBDEPOTSTART,
3262 This->smallBlockDepotStart);
3264 StorageUtl_WriteDWord(
3266 OFFSET_SBDEPOTCOUNT,
3267 This->smallBlockDepotChain ?
3268 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3270 StorageUtl_WriteDWord(
3272 OFFSET_EXTBBDEPOTSTART,
3273 This->extBigBlockDepotStart);
3275 StorageUtl_WriteDWord(
3277 OFFSET_EXTBBDEPOTCOUNT,
3278 This->extBigBlockDepotCount);
3280 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3282 StorageUtl_WriteDWord(
3284 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3285 (This->bigBlockDepotStart[index]));
3289 * Write the big block back to the file.
3291 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3294 /******************************************************************************
3295 * Storage32Impl_ReadProperty
3297 * This method will read the specified property from the property chain.
3299 BOOL StorageImpl_ReadProperty(
3302 StgProperty* buffer)
3304 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3305 ULARGE_INTEGER offsetInPropSet;
3309 offsetInPropSet.u.HighPart = 0;
3310 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3312 readRes = BlockChainStream_ReadAt(
3313 This->rootBlockChain,
3319 if (SUCCEEDED(readRes))
3321 /* replace the name of root entry (often "Root Entry") by the file name */
3322 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3323 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3325 memset(buffer->name, 0, sizeof(buffer->name));
3329 PROPERTY_NAME_BUFFER_LEN );
3330 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3332 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3334 StorageUtl_ReadWord(
3336 OFFSET_PS_NAMELENGTH,
3337 &buffer->sizeOfNameString);
3339 StorageUtl_ReadDWord(
3341 OFFSET_PS_PREVIOUSPROP,
3342 &buffer->previousProperty);
3344 StorageUtl_ReadDWord(
3347 &buffer->nextProperty);
3349 StorageUtl_ReadDWord(
3352 &buffer->dirProperty);
3354 StorageUtl_ReadGUID(
3357 &buffer->propertyUniqueID);
3359 StorageUtl_ReadDWord(
3362 &buffer->timeStampS1);
3364 StorageUtl_ReadDWord(
3367 &buffer->timeStampD1);
3369 StorageUtl_ReadDWord(
3372 &buffer->timeStampS2);
3374 StorageUtl_ReadDWord(
3377 &buffer->timeStampD2);
3379 StorageUtl_ReadDWord(
3381 OFFSET_PS_STARTBLOCK,
3382 &buffer->startingBlock);
3384 StorageUtl_ReadDWord(
3387 &buffer->size.u.LowPart);
3389 buffer->size.u.HighPart = 0;
3392 return SUCCEEDED(readRes) ? TRUE : FALSE;
3395 /*********************************************************************
3396 * Write the specified property into the property chain
3398 BOOL StorageImpl_WriteProperty(
3401 StgProperty* buffer)
3403 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3404 ULARGE_INTEGER offsetInPropSet;
3408 offsetInPropSet.u.HighPart = 0;
3409 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3411 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3414 currentProperty + OFFSET_PS_NAME,
3416 PROPERTY_NAME_BUFFER_LEN );
3418 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3420 StorageUtl_WriteWord(
3422 OFFSET_PS_NAMELENGTH,
3423 buffer->sizeOfNameString);
3425 StorageUtl_WriteDWord(
3427 OFFSET_PS_PREVIOUSPROP,
3428 buffer->previousProperty);
3430 StorageUtl_WriteDWord(
3433 buffer->nextProperty);
3435 StorageUtl_WriteDWord(
3438 buffer->dirProperty);
3440 StorageUtl_WriteGUID(
3443 &buffer->propertyUniqueID);
3445 StorageUtl_WriteDWord(
3448 buffer->timeStampS1);
3450 StorageUtl_WriteDWord(
3453 buffer->timeStampD1);
3455 StorageUtl_WriteDWord(
3458 buffer->timeStampS2);
3460 StorageUtl_WriteDWord(
3463 buffer->timeStampD2);
3465 StorageUtl_WriteDWord(
3467 OFFSET_PS_STARTBLOCK,
3468 buffer->startingBlock);
3470 StorageUtl_WriteDWord(
3473 buffer->size.u.LowPart);
3475 writeRes = BlockChainStream_WriteAt(This->rootBlockChain,
3480 return SUCCEEDED(writeRes) ? TRUE : FALSE;
3483 static BOOL StorageImpl_ReadBigBlock(
3488 void* bigBlockBuffer;
3490 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3492 if (bigBlockBuffer!=0)
3494 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3496 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3504 static BOOL StorageImpl_WriteBigBlock(
3509 void* bigBlockBuffer;
3511 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3513 if (bigBlockBuffer!=0)
3515 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3517 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3525 static void* StorageImpl_GetROBigBlock(
3529 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3532 static void* StorageImpl_GetBigBlock(
3536 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3539 static void StorageImpl_ReleaseBigBlock(
3543 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3546 /******************************************************************************
3547 * Storage32Impl_SmallBlocksToBigBlocks
3549 * This method will convert a small block chain to a big block chain.
3550 * The small block chain will be destroyed.
3552 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3554 SmallBlockChainStream** ppsbChain)
3556 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3557 ULARGE_INTEGER size, offset;
3558 ULONG cbRead, cbWritten;
3559 ULARGE_INTEGER cbTotalRead;
3560 ULONG propertyIndex;
3561 HRESULT resWrite = S_OK;
3563 StgProperty chainProperty;
3565 BlockChainStream *bbTempChain = NULL;
3566 BlockChainStream *bigBlockChain = NULL;
3569 * Create a temporary big block chain that doesn't have
3570 * an associated property. This temporary chain will be
3571 * used to copy data from small blocks to big blocks.
3573 bbTempChain = BlockChainStream_Construct(This,
3576 if(!bbTempChain) return NULL;
3578 * Grow the big block chain.
3580 size = SmallBlockChainStream_GetSize(*ppsbChain);
3581 BlockChainStream_SetSize(bbTempChain, size);
3584 * Copy the contents of the small block chain to the big block chain
3585 * by small block size increments.
3587 offset.u.LowPart = 0;
3588 offset.u.HighPart = 0;
3589 cbTotalRead.QuadPart = 0;
3591 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3594 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3596 This->smallBlockSize,
3599 if (FAILED(resRead))
3604 cbTotalRead.QuadPart += cbRead;
3606 resWrite = BlockChainStream_WriteAt(bbTempChain,
3612 if (FAILED(resWrite))
3615 offset.u.LowPart += This->smallBlockSize;
3617 } while (cbTotalRead.QuadPart < size.QuadPart);
3618 HeapFree(GetProcessHeap(),0,buffer);
3620 if (FAILED(resRead) || FAILED(resWrite))
3622 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3623 BlockChainStream_Destroy(bbTempChain);
3628 * Destroy the small block chain.
3630 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3631 size.u.HighPart = 0;
3633 SmallBlockChainStream_SetSize(*ppsbChain, size);
3634 SmallBlockChainStream_Destroy(*ppsbChain);
3638 * Change the property information. This chain is now a big block chain
3639 * and it doesn't reside in the small blocks chain anymore.
3641 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3643 chainProperty.startingBlock = bbHeadOfChain;
3645 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3648 * Destroy the temporary propertyless big block chain.
3649 * Create a new big block chain associated with this property.
3651 BlockChainStream_Destroy(bbTempChain);
3652 bigBlockChain = BlockChainStream_Construct(This,
3656 return bigBlockChain;
3659 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3661 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3663 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3664 HeapFree(GetProcessHeap(), 0, This);
3667 /******************************************************************************
3669 ** Storage32InternalImpl_Commit
3671 ** The non-root storages cannot be opened in transacted mode thus this function
3674 static HRESULT WINAPI StorageInternalImpl_Commit(
3676 DWORD grfCommitFlags) /* [in] */
3681 /******************************************************************************
3683 ** Storage32InternalImpl_Revert
3685 ** The non-root storages cannot be opened in transacted mode thus this function
3688 static HRESULT WINAPI StorageInternalImpl_Revert(
3694 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3696 IStorage_Release((IStorage*)This->parentStorage);
3697 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3698 HeapFree(GetProcessHeap(), 0, This);
3701 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3702 IEnumSTATSTG* iface,
3706 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3709 * Perform a sanity check on the parameters.
3712 return E_INVALIDARG;
3715 * Initialize the return parameter.
3720 * Compare the riid with the interface IDs implemented by this object.
3722 if (IsEqualGUID(&IID_IUnknown, riid) ||
3723 IsEqualGUID(&IID_IEnumSTATSTG, riid))
3725 *ppvObject = (IEnumSTATSTG*)This;
3726 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3730 return E_NOINTERFACE;
3733 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3734 IEnumSTATSTG* iface)
3736 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3737 return InterlockedIncrement(&This->ref);
3740 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3741 IEnumSTATSTG* iface)
3743 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3747 newRef = InterlockedDecrement(&This->ref);
3750 * If the reference count goes down to 0, perform suicide.
3754 IEnumSTATSTGImpl_Destroy(This);
3760 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3761 IEnumSTATSTG* iface,
3764 ULONG* pceltFetched)
3766 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3768 StgProperty currentProperty;
3769 STATSTG* currentReturnStruct = rgelt;
3770 ULONG objectFetched = 0;
3771 ULONG currentSearchNode;
3774 * Perform a sanity check on the parameters.
3776 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3777 return E_INVALIDARG;
3780 * To avoid the special case, get another pointer to a ULONG value if
3781 * the caller didn't supply one.
3783 if (pceltFetched==0)
3784 pceltFetched = &objectFetched;
3787 * Start the iteration, we will iterate until we hit the end of the
3788 * linked list or until we hit the number of items to iterate through
3793 * Start with the node at the top of the stack.
3795 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3797 while ( ( *pceltFetched < celt) &&
3798 ( currentSearchNode!=PROPERTY_NULL) )
3801 * Remove the top node from the stack
3803 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3806 * Read the property from the storage.
3808 StorageImpl_ReadProperty(This->parentStorage,
3813 * Copy the information to the return buffer.
3815 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3820 * Step to the next item in the iteration
3823 currentReturnStruct++;
3826 * Push the next search node in the search stack.
3828 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3831 * continue the iteration.
3833 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3836 if (*pceltFetched == celt)
3843 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3844 IEnumSTATSTG* iface,
3847 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3849 StgProperty currentProperty;
3850 ULONG objectFetched = 0;
3851 ULONG currentSearchNode;
3854 * Start with the node at the top of the stack.
3856 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3858 while ( (objectFetched < celt) &&
3859 (currentSearchNode!=PROPERTY_NULL) )
3862 * Remove the top node from the stack
3864 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3867 * Read the property from the storage.
3869 StorageImpl_ReadProperty(This->parentStorage,
3874 * Step to the next item in the iteration
3879 * Push the next search node in the search stack.
3881 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3884 * continue the iteration.
3886 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3889 if (objectFetched == celt)
3895 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3896 IEnumSTATSTG* iface)
3898 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3900 StgProperty rootProperty;
3901 BOOL readSuccessful;
3904 * Re-initialize the search stack to an empty stack
3906 This->stackSize = 0;
3909 * Read the root property from the storage.
3911 readSuccessful = StorageImpl_ReadProperty(
3912 This->parentStorage,
3913 This->firstPropertyNode,
3918 assert(rootProperty.sizeOfNameString!=0);
3921 * Push the search node in the search stack.
3923 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3929 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3930 IEnumSTATSTG* iface,
3931 IEnumSTATSTG** ppenum)
3933 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3935 IEnumSTATSTGImpl* newClone;
3938 * Perform a sanity check on the parameters.
3941 return E_INVALIDARG;
3943 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3944 This->firstPropertyNode);
3948 * The new clone enumeration must point to the same current node as
3951 newClone->stackSize = This->stackSize ;
3952 newClone->stackMaxSize = This->stackMaxSize ;
3953 newClone->stackToVisit =
3954 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3957 newClone->stackToVisit,
3959 sizeof(ULONG) * newClone->stackSize);
3961 *ppenum = (IEnumSTATSTG*)newClone;
3964 * Don't forget to nail down a reference to the clone before
3967 IEnumSTATSTGImpl_AddRef(*ppenum);
3972 static INT IEnumSTATSTGImpl_FindParentProperty(
3973 IEnumSTATSTGImpl *This,
3974 ULONG childProperty,
3975 StgProperty *currentProperty,
3978 ULONG currentSearchNode;
3982 * To avoid the special case, get another pointer to a ULONG value if
3983 * the caller didn't supply one.
3986 thisNodeId = &foundNode;
3989 * Start with the node at the top of the stack.
3991 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3994 while (currentSearchNode!=PROPERTY_NULL)
3997 * Store the current node in the returned parameters
3999 *thisNodeId = currentSearchNode;
4002 * Remove the top node from the stack
4004 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4007 * Read the property from the storage.
4009 StorageImpl_ReadProperty(
4010 This->parentStorage,
4014 if (currentProperty->previousProperty == childProperty)
4015 return PROPERTY_RELATION_PREVIOUS;
4017 else if (currentProperty->nextProperty == childProperty)
4018 return PROPERTY_RELATION_NEXT;
4020 else if (currentProperty->dirProperty == childProperty)
4021 return PROPERTY_RELATION_DIR;
4024 * Push the next search node in the search stack.
4026 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4029 * continue the iteration.
4031 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4034 return PROPERTY_NULL;
4037 static ULONG IEnumSTATSTGImpl_FindProperty(
4038 IEnumSTATSTGImpl* This,
4039 const OLECHAR* lpszPropName,
4040 StgProperty* currentProperty)
4042 ULONG currentSearchNode;
4045 * Start with the node at the top of the stack.
4047 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4049 while (currentSearchNode!=PROPERTY_NULL)
4052 * Remove the top node from the stack
4054 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4057 * Read the property from the storage.
4059 StorageImpl_ReadProperty(This->parentStorage,
4063 if ( propertyNameCmp(
4064 (const OLECHAR*)currentProperty->name,
4065 (const OLECHAR*)lpszPropName) == 0)
4066 return currentSearchNode;
4069 * Push the next search node in the search stack.
4071 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4074 * continue the iteration.
4076 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4079 return PROPERTY_NULL;
4082 static void IEnumSTATSTGImpl_PushSearchNode(
4083 IEnumSTATSTGImpl* This,
4086 StgProperty rootProperty;
4087 BOOL readSuccessful;
4090 * First, make sure we're not trying to push an unexisting node.
4092 if (nodeToPush==PROPERTY_NULL)
4096 * First push the node to the stack
4098 if (This->stackSize == This->stackMaxSize)
4100 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4102 This->stackToVisit = HeapReAlloc(
4106 sizeof(ULONG) * This->stackMaxSize);
4109 This->stackToVisit[This->stackSize] = nodeToPush;
4113 * Read the root property from the storage.
4115 readSuccessful = StorageImpl_ReadProperty(
4116 This->parentStorage,
4122 assert(rootProperty.sizeOfNameString!=0);
4125 * Push the previous search node in the search stack.
4127 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4131 static ULONG IEnumSTATSTGImpl_PopSearchNode(
4132 IEnumSTATSTGImpl* This,
4137 if (This->stackSize == 0)
4138 return PROPERTY_NULL;
4140 topNode = This->stackToVisit[This->stackSize-1];
4149 * Virtual function table for the IEnumSTATSTGImpl class.
4151 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4153 IEnumSTATSTGImpl_QueryInterface,
4154 IEnumSTATSTGImpl_AddRef,
4155 IEnumSTATSTGImpl_Release,
4156 IEnumSTATSTGImpl_Next,
4157 IEnumSTATSTGImpl_Skip,
4158 IEnumSTATSTGImpl_Reset,
4159 IEnumSTATSTGImpl_Clone
4162 /******************************************************************************
4163 ** IEnumSTATSTGImpl implementation
4166 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4167 StorageImpl* parentStorage,
4168 ULONG firstPropertyNode)
4170 IEnumSTATSTGImpl* newEnumeration;
4172 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4174 if (newEnumeration!=0)
4177 * Set-up the virtual function table and reference count.
4179 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4180 newEnumeration->ref = 0;
4183 * We want to nail-down the reference to the storage in case the
4184 * enumeration out-lives the storage in the client application.
4186 newEnumeration->parentStorage = parentStorage;
4187 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4189 newEnumeration->firstPropertyNode = firstPropertyNode;
4192 * Initialize the search stack
4194 newEnumeration->stackSize = 0;
4195 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4196 newEnumeration->stackToVisit =
4197 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4200 * Make sure the current node of the iterator is the first one.
4202 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4205 return newEnumeration;
4209 * Virtual function table for the Storage32InternalImpl class.
4211 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4213 StorageBaseImpl_QueryInterface,
4214 StorageBaseImpl_AddRef,
4215 StorageBaseImpl_Release,
4216 StorageBaseImpl_CreateStream,
4217 StorageBaseImpl_OpenStream,
4218 StorageImpl_CreateStorage,
4219 StorageBaseImpl_OpenStorage,
4221 StorageImpl_MoveElementTo,
4222 StorageInternalImpl_Commit,
4223 StorageInternalImpl_Revert,
4224 StorageBaseImpl_EnumElements,
4225 StorageImpl_DestroyElement,
4226 StorageBaseImpl_RenameElement,
4227 StorageImpl_SetElementTimes,
4228 StorageBaseImpl_SetClass,
4229 StorageImpl_SetStateBits,
4230 StorageBaseImpl_Stat
4233 /******************************************************************************
4234 ** Storage32InternalImpl implementation
4237 static StorageInternalImpl* StorageInternalImpl_Construct(
4238 StorageImpl* ancestorStorage,
4240 ULONG rootPropertyIndex)
4242 StorageInternalImpl* newStorage;
4245 * Allocate space for the new storage object
4247 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
4251 memset(newStorage, 0, sizeof(StorageInternalImpl));
4254 * Initialize the stream list
4257 list_init(&newStorage->base.strmHead);
4260 * Initialize the virtual function table.
4262 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4263 newStorage->base.v_destructor = &StorageInternalImpl_Destroy;
4264 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4267 * Keep the ancestor storage pointer and nail a reference to it.
4269 newStorage->base.ancestorStorage = ancestorStorage;
4270 StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
4273 * Keep the index of the root property set for this storage,
4275 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4283 /******************************************************************************
4284 ** StorageUtl implementation
4287 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4291 memcpy(&tmp, buffer+offset, sizeof(WORD));
4292 *value = le16toh(tmp);
4295 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4297 value = htole16(value);
4298 memcpy(buffer+offset, &value, sizeof(WORD));
4301 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4305 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4306 *value = le32toh(tmp);
4309 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4311 value = htole32(value);
4312 memcpy(buffer+offset, &value, sizeof(DWORD));
4315 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4316 ULARGE_INTEGER* value)
4318 #ifdef WORDS_BIGENDIAN
4321 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4322 value->u.LowPart = htole32(tmp.u.HighPart);
4323 value->u.HighPart = htole32(tmp.u.LowPart);
4325 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4329 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4330 const ULARGE_INTEGER *value)
4332 #ifdef WORDS_BIGENDIAN
4335 tmp.u.LowPart = htole32(value->u.HighPart);
4336 tmp.u.HighPart = htole32(value->u.LowPart);
4337 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4339 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4343 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4345 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4346 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4347 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4349 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4352 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4354 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4355 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4356 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4358 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4361 void StorageUtl_CopyPropertyToSTATSTG(
4362 STATSTG* destination,
4363 StgProperty* source,
4367 * The copy of the string occurs only when the flag is not set
4369 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4370 (source->name == NULL) ||
4371 (source->name[0] == 0) )
4373 destination->pwcsName = 0;
4377 destination->pwcsName =
4378 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4380 strcpyW((LPWSTR)destination->pwcsName, source->name);
4383 switch (source->propertyType)
4385 case PROPTYPE_STORAGE:
4387 destination->type = STGTY_STORAGE;
4389 case PROPTYPE_STREAM:
4390 destination->type = STGTY_STREAM;
4393 destination->type = STGTY_STREAM;
4397 destination->cbSize = source->size;
4399 currentReturnStruct->mtime = {0}; TODO
4400 currentReturnStruct->ctime = {0};
4401 currentReturnStruct->atime = {0};
4403 destination->grfMode = 0;
4404 destination->grfLocksSupported = 0;
4405 destination->clsid = source->propertyUniqueID;
4406 destination->grfStateBits = 0;
4407 destination->reserved = 0;
4410 /******************************************************************************
4411 ** BlockChainStream implementation
4414 BlockChainStream* BlockChainStream_Construct(
4415 StorageImpl* parentStorage,
4416 ULONG* headOfStreamPlaceHolder,
4417 ULONG propertyIndex)
4419 BlockChainStream* newStream;
4422 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4424 newStream->parentStorage = parentStorage;
4425 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4426 newStream->ownerPropertyIndex = propertyIndex;
4427 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4428 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4429 newStream->numBlocks = 0;
4431 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4433 while (blockIndex != BLOCK_END_OF_CHAIN)
4435 newStream->numBlocks++;
4436 newStream->tailIndex = blockIndex;
4438 if(FAILED(StorageImpl_GetNextBlockInChain(
4443 HeapFree(GetProcessHeap(), 0, newStream);
4451 void BlockChainStream_Destroy(BlockChainStream* This)
4453 HeapFree(GetProcessHeap(), 0, This);
4456 /******************************************************************************
4457 * BlockChainStream_GetHeadOfChain
4459 * Returns the head of this stream chain.
4460 * Some special chains don't have properties, their heads are kept in
4461 * This->headOfStreamPlaceHolder.
4464 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4466 StgProperty chainProperty;
4467 BOOL readSuccessful;
4469 if (This->headOfStreamPlaceHolder != 0)
4470 return *(This->headOfStreamPlaceHolder);
4472 if (This->ownerPropertyIndex != PROPERTY_NULL)
4474 readSuccessful = StorageImpl_ReadProperty(
4475 This->parentStorage,
4476 This->ownerPropertyIndex,
4481 return chainProperty.startingBlock;
4485 return BLOCK_END_OF_CHAIN;
4488 /******************************************************************************
4489 * BlockChainStream_GetCount
4491 * Returns the number of blocks that comprises this chain.
4492 * This is not the size of the stream as the last block may not be full!
4495 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
4500 blockIndex = BlockChainStream_GetHeadOfChain(This);
4502 while (blockIndex != BLOCK_END_OF_CHAIN)
4506 if(FAILED(StorageImpl_GetNextBlockInChain(
4507 This->parentStorage,
4516 /******************************************************************************
4517 * BlockChainStream_ReadAt
4519 * Reads a specified number of bytes from this chain at the specified offset.
4520 * bytesRead may be NULL.
4521 * Failure will be returned if the specified number of bytes has not been read.
4523 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
4524 ULARGE_INTEGER offset,
4529 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4530 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4531 ULONG bytesToReadInBuffer;
4534 BYTE* bigBlockBuffer;
4537 * Find the first block in the stream that contains part of the buffer.
4539 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4540 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4541 (blockNoInSequence < This->lastBlockNoInSequence) )
4543 blockIndex = BlockChainStream_GetHeadOfChain(This);
4544 This->lastBlockNoInSequence = blockNoInSequence;
4548 ULONG temp = blockNoInSequence;
4550 blockIndex = This->lastBlockNoInSequenceIndex;
4551 blockNoInSequence -= This->lastBlockNoInSequence;
4552 This->lastBlockNoInSequence = temp;
4555 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4557 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4558 return STG_E_DOCFILECORRUPT;
4559 blockNoInSequence--;
4562 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4563 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
4565 This->lastBlockNoInSequenceIndex = blockIndex;
4568 * Start reading the buffer.
4571 bufferWalker = buffer;
4573 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4576 * Calculate how many bytes we can copy from this big block.
4578 bytesToReadInBuffer =
4579 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4582 * Copy those bytes to the buffer
4585 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4586 if (!bigBlockBuffer)
4587 return STG_E_READFAULT;
4589 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4591 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4594 * Step to the next big block.
4596 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4597 return STG_E_DOCFILECORRUPT;
4599 bufferWalker += bytesToReadInBuffer;
4600 size -= bytesToReadInBuffer;
4601 *bytesRead += bytesToReadInBuffer;
4602 offsetInBlock = 0; /* There is no offset on the next block */
4606 return (size == 0) ? S_OK : STG_E_READFAULT;
4609 /******************************************************************************
4610 * BlockChainStream_WriteAt
4612 * Writes the specified number of bytes to this chain at the specified offset.
4613 * bytesWritten may be NULL.
4614 * Will fail if not all specified number of bytes have been written.
4616 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
4617 ULARGE_INTEGER offset,
4620 ULONG* bytesWritten)
4622 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4623 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4626 const BYTE* bufferWalker;
4627 BYTE* bigBlockBuffer;
4630 * Find the first block in the stream that contains part of the buffer.
4632 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4633 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4634 (blockNoInSequence < This->lastBlockNoInSequence) )
4636 blockIndex = BlockChainStream_GetHeadOfChain(This);
4637 This->lastBlockNoInSequence = blockNoInSequence;
4641 ULONG temp = blockNoInSequence;
4643 blockIndex = This->lastBlockNoInSequenceIndex;
4644 blockNoInSequence -= This->lastBlockNoInSequence;
4645 This->lastBlockNoInSequence = temp;
4648 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4650 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4652 return STG_E_DOCFILECORRUPT;
4653 blockNoInSequence--;
4656 This->lastBlockNoInSequenceIndex = blockIndex;
4658 /* BlockChainStream_SetSize should have already been called to ensure we have
4659 * enough blocks in the chain to write into */
4660 if (blockIndex == BLOCK_END_OF_CHAIN)
4662 ERR("not enough blocks in chain to write data\n");
4663 return STG_E_DOCFILECORRUPT;
4667 * Here, I'm casting away the constness on the buffer variable
4668 * This is OK since we don't intend to modify that buffer.
4671 bufferWalker = (const BYTE*)buffer;
4673 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4676 * Calculate how many bytes we can copy from this big block.
4679 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4682 * Copy those bytes to the buffer
4684 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4686 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4688 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4691 * Step to the next big block.
4693 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4695 return STG_E_DOCFILECORRUPT;
4696 bufferWalker += bytesToWrite;
4697 size -= bytesToWrite;
4698 *bytesWritten += bytesToWrite;
4699 offsetInBlock = 0; /* There is no offset on the next block */
4702 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
4705 /******************************************************************************
4706 * BlockChainStream_Shrink
4708 * Shrinks this chain in the big block depot.
4710 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
4711 ULARGE_INTEGER newSize)
4713 ULONG blockIndex, extraBlock;
4718 * Reset the last accessed block cache.
4720 This->lastBlockNoInSequence = 0xFFFFFFFF;
4721 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4724 * Figure out how many blocks are needed to contain the new size
4726 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4728 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4731 blockIndex = BlockChainStream_GetHeadOfChain(This);
4734 * Go to the new end of chain
4736 while (count < numBlocks)
4738 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4744 /* Get the next block before marking the new end */
4745 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4749 /* Mark the new end of chain */
4750 StorageImpl_SetNextBlockInChain(
4751 This->parentStorage,
4753 BLOCK_END_OF_CHAIN);
4755 This->tailIndex = blockIndex;
4756 This->numBlocks = numBlocks;
4759 * Mark the extra blocks as free
4761 while (extraBlock != BLOCK_END_OF_CHAIN)
4763 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4766 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4767 extraBlock = blockIndex;
4773 /******************************************************************************
4774 * BlockChainStream_Enlarge
4776 * Grows this chain in the big block depot.
4778 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4779 ULARGE_INTEGER newSize)
4781 ULONG blockIndex, currentBlock;
4783 ULONG oldNumBlocks = 0;
4785 blockIndex = BlockChainStream_GetHeadOfChain(This);
4788 * Empty chain. Create the head.
4790 if (blockIndex == BLOCK_END_OF_CHAIN)
4792 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4793 StorageImpl_SetNextBlockInChain(This->parentStorage,
4795 BLOCK_END_OF_CHAIN);
4797 if (This->headOfStreamPlaceHolder != 0)
4799 *(This->headOfStreamPlaceHolder) = blockIndex;
4803 StgProperty chainProp;
4804 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4806 StorageImpl_ReadProperty(
4807 This->parentStorage,
4808 This->ownerPropertyIndex,
4811 chainProp.startingBlock = blockIndex;
4813 StorageImpl_WriteProperty(
4814 This->parentStorage,
4815 This->ownerPropertyIndex,
4819 This->tailIndex = blockIndex;
4820 This->numBlocks = 1;
4824 * Figure out how many blocks are needed to contain this stream
4826 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4828 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4832 * Go to the current end of chain
4834 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4836 currentBlock = blockIndex;
4838 while (blockIndex != BLOCK_END_OF_CHAIN)
4841 currentBlock = blockIndex;
4843 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4848 This->tailIndex = currentBlock;
4851 currentBlock = This->tailIndex;
4852 oldNumBlocks = This->numBlocks;
4855 * Add new blocks to the chain
4857 if (oldNumBlocks < newNumBlocks)
4859 while (oldNumBlocks < newNumBlocks)
4861 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4863 StorageImpl_SetNextBlockInChain(
4864 This->parentStorage,
4868 StorageImpl_SetNextBlockInChain(
4869 This->parentStorage,
4871 BLOCK_END_OF_CHAIN);
4873 currentBlock = blockIndex;
4877 This->tailIndex = blockIndex;
4878 This->numBlocks = newNumBlocks;
4884 /******************************************************************************
4885 * BlockChainStream_SetSize
4887 * Sets the size of this stream. The big block depot will be updated.
4888 * The file will grow if we grow the chain.
4890 * TODO: Free the actual blocks in the file when we shrink the chain.
4891 * Currently, the blocks are still in the file. So the file size
4892 * doesn't shrink even if we shrink streams.
4894 BOOL BlockChainStream_SetSize(
4895 BlockChainStream* This,
4896 ULARGE_INTEGER newSize)
4898 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4900 if (newSize.u.LowPart == size.u.LowPart)
4903 if (newSize.u.LowPart < size.u.LowPart)
4905 BlockChainStream_Shrink(This, newSize);
4909 BlockChainStream_Enlarge(This, newSize);
4915 /******************************************************************************
4916 * BlockChainStream_GetSize
4918 * Returns the size of this chain.
4919 * Will return the block count if this chain doesn't have a property.
4921 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4923 StgProperty chainProperty;
4925 if(This->headOfStreamPlaceHolder == NULL)
4928 * This chain is a data stream read the property and return
4929 * the appropriate size
4931 StorageImpl_ReadProperty(
4932 This->parentStorage,
4933 This->ownerPropertyIndex,
4936 return chainProperty.size;
4941 * this chain is a chain that does not have a property, figure out the
4942 * size by making the product number of used blocks times the
4945 ULARGE_INTEGER result;
4946 result.u.HighPart = 0;
4949 BlockChainStream_GetCount(This) *
4950 This->parentStorage->bigBlockSize;
4956 /******************************************************************************
4957 ** SmallBlockChainStream implementation
4960 SmallBlockChainStream* SmallBlockChainStream_Construct(
4961 StorageImpl* parentStorage,
4962 ULONG propertyIndex)
4964 SmallBlockChainStream* newStream;
4966 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4968 newStream->parentStorage = parentStorage;
4969 newStream->ownerPropertyIndex = propertyIndex;
4974 void SmallBlockChainStream_Destroy(
4975 SmallBlockChainStream* This)
4977 HeapFree(GetProcessHeap(), 0, This);
4980 /******************************************************************************
4981 * SmallBlockChainStream_GetHeadOfChain
4983 * Returns the head of this chain of small blocks.
4985 static ULONG SmallBlockChainStream_GetHeadOfChain(
4986 SmallBlockChainStream* This)
4988 StgProperty chainProperty;
4989 BOOL readSuccessful;
4991 if (This->ownerPropertyIndex)
4993 readSuccessful = StorageImpl_ReadProperty(
4994 This->parentStorage,
4995 This->ownerPropertyIndex,
5000 return chainProperty.startingBlock;
5005 return BLOCK_END_OF_CHAIN;
5008 /******************************************************************************
5009 * SmallBlockChainStream_GetNextBlockInChain
5011 * Returns the index of the next small block in this chain.
5014 * - BLOCK_END_OF_CHAIN: end of this chain
5015 * - BLOCK_UNUSED: small block 'blockIndex' is free
5017 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
5018 SmallBlockChainStream* This,
5020 ULONG* nextBlockInChain)
5022 ULARGE_INTEGER offsetOfBlockInDepot;
5027 *nextBlockInChain = BLOCK_END_OF_CHAIN;
5029 offsetOfBlockInDepot.u.HighPart = 0;
5030 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5033 * Read those bytes in the buffer from the small block file.
5035 res = BlockChainStream_ReadAt(
5036 This->parentStorage->smallBlockDepotChain,
5037 offsetOfBlockInDepot,
5044 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
5051 /******************************************************************************
5052 * SmallBlockChainStream_SetNextBlockInChain
5054 * Writes the index of the next block of the specified block in the small
5056 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5057 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5059 static void SmallBlockChainStream_SetNextBlockInChain(
5060 SmallBlockChainStream* This,
5064 ULARGE_INTEGER offsetOfBlockInDepot;
5068 offsetOfBlockInDepot.u.HighPart = 0;
5069 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5071 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
5074 * Read those bytes in the buffer from the small block file.
5076 BlockChainStream_WriteAt(
5077 This->parentStorage->smallBlockDepotChain,
5078 offsetOfBlockInDepot,
5084 /******************************************************************************
5085 * SmallBlockChainStream_FreeBlock
5087 * Flag small block 'blockIndex' as free in the small block depot.
5089 static void SmallBlockChainStream_FreeBlock(
5090 SmallBlockChainStream* This,
5093 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5096 /******************************************************************************
5097 * SmallBlockChainStream_GetNextFreeBlock
5099 * Returns the index of a free small block. The small block depot will be
5100 * enlarged if necessary. The small block chain will also be enlarged if
5103 static ULONG SmallBlockChainStream_GetNextFreeBlock(
5104 SmallBlockChainStream* This)
5106 ULARGE_INTEGER offsetOfBlockInDepot;
5109 ULONG blockIndex = 0;
5110 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5112 ULONG smallBlocksPerBigBlock;
5114 offsetOfBlockInDepot.u.HighPart = 0;
5117 * Scan the small block depot for a free block
5119 while (nextBlockIndex != BLOCK_UNUSED)
5121 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5123 res = BlockChainStream_ReadAt(
5124 This->parentStorage->smallBlockDepotChain,
5125 offsetOfBlockInDepot,
5131 * If we run out of space for the small block depot, enlarge it
5135 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5137 if (nextBlockIndex != BLOCK_UNUSED)
5143 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5145 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5146 ULONG nextBlock, newsbdIndex;
5147 BYTE* smallBlockDepot;
5149 nextBlock = sbdIndex;
5150 while (nextBlock != BLOCK_END_OF_CHAIN)
5152 sbdIndex = nextBlock;
5153 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5156 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5157 if (sbdIndex != BLOCK_END_OF_CHAIN)
5158 StorageImpl_SetNextBlockInChain(
5159 This->parentStorage,
5163 StorageImpl_SetNextBlockInChain(
5164 This->parentStorage,
5166 BLOCK_END_OF_CHAIN);
5169 * Initialize all the small blocks to free
5172 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
5174 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5175 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
5180 * We have just created the small block depot.
5182 StgProperty rootProp;
5186 * Save it in the header
5188 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5189 StorageImpl_SaveFileHeader(This->parentStorage);
5192 * And allocate the first big block that will contain small blocks
5195 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5197 StorageImpl_SetNextBlockInChain(
5198 This->parentStorage,
5200 BLOCK_END_OF_CHAIN);
5202 StorageImpl_ReadProperty(
5203 This->parentStorage,
5204 This->parentStorage->base.rootPropertySetIndex,
5207 rootProp.startingBlock = sbStartIndex;
5208 rootProp.size.u.HighPart = 0;
5209 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
5211 StorageImpl_WriteProperty(
5212 This->parentStorage,
5213 This->parentStorage->base.rootPropertySetIndex,
5219 smallBlocksPerBigBlock =
5220 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5223 * Verify if we have to allocate big blocks to contain small blocks
5225 if (blockIndex % smallBlocksPerBigBlock == 0)
5227 StgProperty rootProp;
5228 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5230 StorageImpl_ReadProperty(
5231 This->parentStorage,
5232 This->parentStorage->base.rootPropertySetIndex,
5235 if (rootProp.size.u.LowPart <
5236 (blocksRequired * This->parentStorage->bigBlockSize))
5238 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5240 BlockChainStream_SetSize(
5241 This->parentStorage->smallBlockRootChain,
5244 StorageImpl_WriteProperty(
5245 This->parentStorage,
5246 This->parentStorage->base.rootPropertySetIndex,
5254 /******************************************************************************
5255 * SmallBlockChainStream_ReadAt
5257 * Reads a specified number of bytes from this chain at the specified offset.
5258 * bytesRead may be NULL.
5259 * Failure will be returned if the specified number of bytes has not been read.
5261 HRESULT SmallBlockChainStream_ReadAt(
5262 SmallBlockChainStream* This,
5263 ULARGE_INTEGER offset,
5269 ULARGE_INTEGER offsetInBigBlockFile;
5270 ULONG blockNoInSequence =
5271 offset.u.LowPart / This->parentStorage->smallBlockSize;
5273 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5274 ULONG bytesToReadInBuffer;
5276 ULONG bytesReadFromBigBlockFile;
5280 * This should never happen on a small block file.
5282 assert(offset.u.HighPart==0);
5285 * Find the first block in the stream that contains part of the buffer.
5287 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5289 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5291 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5294 blockNoInSequence--;
5298 * Start reading the buffer.
5301 bufferWalker = buffer;
5303 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5306 * Calculate how many bytes we can copy from this small block.
5308 bytesToReadInBuffer =
5309 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5312 * Calculate the offset of the small block in the small block file.
5314 offsetInBigBlockFile.u.HighPart = 0;
5315 offsetInBigBlockFile.u.LowPart =
5316 blockIndex * This->parentStorage->smallBlockSize;
5318 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5321 * Read those bytes in the buffer from the small block file.
5322 * The small block has already been identified so it shouldn't fail
5323 * unless the file is corrupt.
5325 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5326 offsetInBigBlockFile,
5327 bytesToReadInBuffer,
5329 &bytesReadFromBigBlockFile);
5335 * Step to the next big block.
5337 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5339 return STG_E_DOCFILECORRUPT;
5341 bufferWalker += bytesReadFromBigBlockFile;
5342 size -= bytesReadFromBigBlockFile;
5343 *bytesRead += bytesReadFromBigBlockFile;
5344 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5347 return (size == 0) ? S_OK : STG_E_READFAULT;
5350 /******************************************************************************
5351 * SmallBlockChainStream_WriteAt
5353 * Writes the specified number of bytes to this chain at the specified offset.
5354 * bytesWritten may be NULL.
5355 * Will fail if not all specified number of bytes have been written.
5357 HRESULT SmallBlockChainStream_WriteAt(
5358 SmallBlockChainStream* This,
5359 ULARGE_INTEGER offset,
5362 ULONG* bytesWritten)
5364 ULARGE_INTEGER offsetInBigBlockFile;
5365 ULONG blockNoInSequence =
5366 offset.u.LowPart / This->parentStorage->smallBlockSize;
5368 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5369 ULONG bytesToWriteInBuffer;
5371 ULONG bytesWrittenToBigBlockFile;
5372 const BYTE* bufferWalker;
5376 * This should never happen on a small block file.
5378 assert(offset.u.HighPart==0);
5381 * Find the first block in the stream that contains part of the buffer.
5383 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5385 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5387 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5388 return STG_E_DOCFILECORRUPT;
5389 blockNoInSequence--;
5393 * Start writing the buffer.
5395 * Here, I'm casting away the constness on the buffer variable
5396 * This is OK since we don't intend to modify that buffer.
5399 bufferWalker = (const BYTE*)buffer;
5400 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5403 * Calculate how many bytes we can copy to this small block.
5405 bytesToWriteInBuffer =
5406 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5409 * Calculate the offset of the small block in the small block file.
5411 offsetInBigBlockFile.u.HighPart = 0;
5412 offsetInBigBlockFile.u.LowPart =
5413 blockIndex * This->parentStorage->smallBlockSize;
5415 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5418 * Write those bytes in the buffer to the small block file.
5420 res = BlockChainStream_WriteAt(
5421 This->parentStorage->smallBlockRootChain,
5422 offsetInBigBlockFile,
5423 bytesToWriteInBuffer,
5425 &bytesWrittenToBigBlockFile);
5430 * Step to the next big block.
5432 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5435 bufferWalker += bytesWrittenToBigBlockFile;
5436 size -= bytesWrittenToBigBlockFile;
5437 *bytesWritten += bytesWrittenToBigBlockFile;
5438 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
5441 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5444 /******************************************************************************
5445 * SmallBlockChainStream_Shrink
5447 * Shrinks this chain in the small block depot.
5449 static BOOL SmallBlockChainStream_Shrink(
5450 SmallBlockChainStream* This,
5451 ULARGE_INTEGER newSize)
5453 ULONG blockIndex, extraBlock;
5457 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5459 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5462 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5465 * Go to the new end of chain
5467 while (count < numBlocks)
5469 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5476 * If the count is 0, we have a special case, the head of the chain was
5481 StgProperty chainProp;
5483 StorageImpl_ReadProperty(This->parentStorage,
5484 This->ownerPropertyIndex,
5487 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5489 StorageImpl_WriteProperty(This->parentStorage,
5490 This->ownerPropertyIndex,
5494 * We start freeing the chain at the head block.
5496 extraBlock = blockIndex;
5500 /* Get the next block before marking the new end */
5501 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5505 /* Mark the new end of chain */
5506 SmallBlockChainStream_SetNextBlockInChain(
5509 BLOCK_END_OF_CHAIN);
5513 * Mark the extra blocks as free
5515 while (extraBlock != BLOCK_END_OF_CHAIN)
5517 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5520 SmallBlockChainStream_FreeBlock(This, extraBlock);
5521 extraBlock = blockIndex;
5527 /******************************************************************************
5528 * SmallBlockChainStream_Enlarge
5530 * Grows this chain in the small block depot.
5532 static BOOL SmallBlockChainStream_Enlarge(
5533 SmallBlockChainStream* This,
5534 ULARGE_INTEGER newSize)
5536 ULONG blockIndex, currentBlock;
5538 ULONG oldNumBlocks = 0;
5540 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5545 if (blockIndex == BLOCK_END_OF_CHAIN)
5548 StgProperty chainProp;
5550 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5553 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5555 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5558 blockIndex = chainProp.startingBlock;
5559 SmallBlockChainStream_SetNextBlockInChain(
5562 BLOCK_END_OF_CHAIN);
5565 currentBlock = blockIndex;
5568 * Figure out how many blocks are needed to contain this stream
5570 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5572 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5576 * Go to the current end of chain
5578 while (blockIndex != BLOCK_END_OF_CHAIN)
5581 currentBlock = blockIndex;
5582 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5587 * Add new blocks to the chain
5589 while (oldNumBlocks < newNumBlocks)
5591 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5592 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5594 SmallBlockChainStream_SetNextBlockInChain(
5597 BLOCK_END_OF_CHAIN);
5599 currentBlock = blockIndex;
5606 /******************************************************************************
5607 * SmallBlockChainStream_SetSize
5609 * Sets the size of this stream.
5610 * The file will grow if we grow the chain.
5612 * TODO: Free the actual blocks in the file when we shrink the chain.
5613 * Currently, the blocks are still in the file. So the file size
5614 * doesn't shrink even if we shrink streams.
5616 BOOL SmallBlockChainStream_SetSize(
5617 SmallBlockChainStream* This,
5618 ULARGE_INTEGER newSize)
5620 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5622 if (newSize.u.LowPart == size.u.LowPart)
5625 if (newSize.u.LowPart < size.u.LowPart)
5627 SmallBlockChainStream_Shrink(This, newSize);
5631 SmallBlockChainStream_Enlarge(This, newSize);
5637 /******************************************************************************
5638 * SmallBlockChainStream_GetSize
5640 * Returns the size of this chain.
5642 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5644 StgProperty chainProperty;
5646 StorageImpl_ReadProperty(
5647 This->parentStorage,
5648 This->ownerPropertyIndex,
5651 return chainProperty.size;
5654 /******************************************************************************
5655 * StgCreateDocfile [OLE32.@]
5656 * Creates a new compound file storage object
5659 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5660 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5661 * reserved [ ?] unused?, usually 0
5662 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5665 * S_OK if the file was successfully created
5666 * some STG_E_ value if error
5668 * if pwcsName is NULL, create file with new unique name
5669 * the function can returns
5670 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5673 HRESULT WINAPI StgCreateDocfile(
5677 IStorage **ppstgOpen)
5679 StorageImpl* newStorage = 0;
5680 HANDLE hFile = INVALID_HANDLE_VALUE;
5681 HRESULT hr = STG_E_INVALIDFLAG;
5685 DWORD fileAttributes;
5686 WCHAR tempFileName[MAX_PATH];
5688 TRACE("(%s, %x, %d, %p)\n",
5689 debugstr_w(pwcsName), grfMode,
5690 reserved, ppstgOpen);
5693 * Validate the parameters
5696 return STG_E_INVALIDPOINTER;
5698 return STG_E_INVALIDPARAMETER;
5701 * Validate the STGM flags
5703 if ( FAILED( validateSTGM(grfMode) ))
5706 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5707 switch(STGM_ACCESS_MODE(grfMode))
5710 case STGM_READWRITE:
5716 /* if no share mode given then DENY_NONE is the default */
5717 if (STGM_SHARE_MODE(grfMode) == 0)
5718 grfMode |= STGM_SHARE_DENY_NONE;
5720 /* must have at least one access mode */
5721 if (STGM_ACCESS_MODE(grfMode) == 0)
5724 /* in direct mode, can only use SHARE_EXCLUSIVE */
5725 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5728 /* but in transacted mode, any share mode is valid */
5731 * Generate a unique name.
5735 WCHAR tempPath[MAX_PATH];
5736 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5738 memset(tempPath, 0, sizeof(tempPath));
5739 memset(tempFileName, 0, sizeof(tempFileName));
5741 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5744 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5745 pwcsName = tempFileName;
5748 hr = STG_E_INSUFFICIENTMEMORY;
5752 creationMode = TRUNCATE_EXISTING;
5756 creationMode = GetCreationModeFromSTGM(grfMode);
5760 * Interpret the STGM value grfMode
5762 shareMode = GetShareModeFromSTGM(grfMode);
5763 accessMode = GetAccessModeFromSTGM(grfMode);
5765 if (grfMode & STGM_DELETEONRELEASE)
5766 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5768 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5770 if (grfMode & STGM_TRANSACTED)
5771 FIXME("Transacted mode not implemented.\n");
5774 * Initialize the "out" parameter.
5778 hFile = CreateFileW(pwcsName,
5786 if (hFile == INVALID_HANDLE_VALUE)
5788 if(GetLastError() == ERROR_FILE_EXISTS)
5789 hr = STG_E_FILEALREADYEXISTS;
5796 * Allocate and initialize the new IStorage32object.
5798 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5800 if (newStorage == 0)
5802 hr = STG_E_INSUFFICIENTMEMORY;
5806 hr = StorageImpl_Construct(
5817 HeapFree(GetProcessHeap(), 0, newStorage);
5822 * Get an "out" pointer for the caller.
5824 hr = StorageBaseImpl_QueryInterface(
5825 (IStorage*)newStorage,
5826 (REFIID)&IID_IStorage,
5829 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
5834 /******************************************************************************
5835 * StgCreateStorageEx [OLE32.@]
5837 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5839 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5840 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5842 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5844 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5845 return STG_E_INVALIDPARAMETER;
5848 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5850 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5851 return STG_E_INVALIDPARAMETER;
5854 if (stgfmt == STGFMT_FILE)
5856 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5857 return STG_E_INVALIDPARAMETER;
5860 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5862 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5863 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5866 ERR("Invalid stgfmt argument\n");
5867 return STG_E_INVALIDPARAMETER;
5870 /******************************************************************************
5871 * StgCreatePropSetStg [OLE32.@]
5873 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5874 IPropertySetStorage **ppPropSetStg)
5878 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
5880 hr = STG_E_INVALIDPARAMETER;
5882 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5883 (void**)ppPropSetStg);
5887 /******************************************************************************
5888 * StgOpenStorageEx [OLE32.@]
5890 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5892 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5893 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5895 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5897 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5898 return STG_E_INVALIDPARAMETER;
5904 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5905 return STG_E_INVALIDPARAMETER;
5907 case STGFMT_STORAGE:
5910 case STGFMT_DOCFILE:
5911 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
5913 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5914 return STG_E_INVALIDPARAMETER;
5916 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5920 WARN("STGFMT_ANY assuming storage\n");
5924 return STG_E_INVALIDPARAMETER;
5927 return StgOpenStorage(pwcsName, NULL, grfMode, (SNB)NULL, 0, (IStorage **)ppObjectOpen);
5931 /******************************************************************************
5932 * StgOpenStorage [OLE32.@]
5934 HRESULT WINAPI StgOpenStorage(
5935 const OLECHAR *pwcsName,
5936 IStorage *pstgPriority,
5940 IStorage **ppstgOpen)
5942 StorageImpl* newStorage = 0;
5947 WCHAR fullname[MAX_PATH];
5949 TRACE("(%s, %p, %x, %p, %d, %p)\n",
5950 debugstr_w(pwcsName), pstgPriority, grfMode,
5951 snbExclude, reserved, ppstgOpen);
5954 * Perform sanity checks
5958 hr = STG_E_INVALIDNAME;
5964 hr = STG_E_INVALIDPOINTER;
5970 hr = STG_E_INVALIDPARAMETER;
5974 if (grfMode & STGM_PRIORITY)
5976 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
5977 return STG_E_INVALIDFLAG;
5978 if (grfMode & STGM_DELETEONRELEASE)
5979 return STG_E_INVALIDFUNCTION;
5980 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
5981 return STG_E_INVALIDFLAG;
5982 grfMode &= ~0xf0; /* remove the existing sharing mode */
5983 grfMode |= STGM_SHARE_DENY_NONE;
5985 /* STGM_PRIORITY stops other IStorage objects on the same file from
5986 * committing until the STGM_PRIORITY IStorage is closed. it also
5987 * stops non-transacted mode StgOpenStorage calls with write access from
5988 * succeeding. obviously, both of these cannot be achieved through just
5989 * file share flags */
5990 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5994 * Validate the sharing mode
5996 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
5997 switch(STGM_SHARE_MODE(grfMode))
5999 case STGM_SHARE_EXCLUSIVE:
6000 case STGM_SHARE_DENY_WRITE:
6003 hr = STG_E_INVALIDFLAG;
6008 * Validate the STGM flags
6010 if ( FAILED( validateSTGM(grfMode) ) ||
6011 (grfMode&STGM_CREATE))
6013 hr = STG_E_INVALIDFLAG;
6017 /* shared reading requires transacted mode */
6018 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
6019 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
6020 !(grfMode&STGM_TRANSACTED) )
6022 hr = STG_E_INVALIDFLAG;
6027 * Interpret the STGM value grfMode
6029 shareMode = GetShareModeFromSTGM(grfMode);
6030 accessMode = GetAccessModeFromSTGM(grfMode);
6033 * Initialize the "out" parameter.
6037 hFile = CreateFileW( pwcsName,
6042 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
6045 if (hFile==INVALID_HANDLE_VALUE)
6047 DWORD last_error = GetLastError();
6053 case ERROR_FILE_NOT_FOUND:
6054 hr = STG_E_FILENOTFOUND;
6057 case ERROR_PATH_NOT_FOUND:
6058 hr = STG_E_PATHNOTFOUND;
6061 case ERROR_ACCESS_DENIED:
6062 case ERROR_WRITE_PROTECT:
6063 hr = STG_E_ACCESSDENIED;
6066 case ERROR_SHARING_VIOLATION:
6067 hr = STG_E_SHAREVIOLATION;
6078 * Refuse to open the file if it's too small to be a structured storage file
6079 * FIXME: verify the file when reading instead of here
6081 if (GetFileSize(hFile, NULL) < 0x100)
6084 hr = STG_E_FILEALREADYEXISTS;
6089 * Allocate and initialize the new IStorage32object.
6091 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6093 if (newStorage == 0)
6095 hr = STG_E_INSUFFICIENTMEMORY;
6099 /* Initialize the storage */
6100 hr = StorageImpl_Construct(
6111 HeapFree(GetProcessHeap(), 0, newStorage);
6113 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6115 if(hr == STG_E_INVALIDHEADER)
6116 hr = STG_E_FILEALREADYEXISTS;
6120 /* prepare the file name string given in lieu of the root property name */
6121 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
6122 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
6123 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
6126 * Get an "out" pointer for the caller.
6128 hr = StorageBaseImpl_QueryInterface(
6129 (IStorage*)newStorage,
6130 (REFIID)&IID_IStorage,
6134 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6138 /******************************************************************************
6139 * StgCreateDocfileOnILockBytes [OLE32.@]
6141 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6145 IStorage** ppstgOpen)
6147 StorageImpl* newStorage = 0;
6151 * Validate the parameters
6153 if ((ppstgOpen == 0) || (plkbyt == 0))
6154 return STG_E_INVALIDPOINTER;
6157 * Allocate and initialize the new IStorage object.
6159 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6161 if (newStorage == 0)
6162 return STG_E_INSUFFICIENTMEMORY;
6164 hr = StorageImpl_Construct(
6175 HeapFree(GetProcessHeap(), 0, newStorage);
6180 * Get an "out" pointer for the caller.
6182 hr = StorageBaseImpl_QueryInterface(
6183 (IStorage*)newStorage,
6184 (REFIID)&IID_IStorage,
6190 /******************************************************************************
6191 * StgOpenStorageOnILockBytes [OLE32.@]
6193 HRESULT WINAPI StgOpenStorageOnILockBytes(
6195 IStorage *pstgPriority,
6199 IStorage **ppstgOpen)
6201 StorageImpl* newStorage = 0;
6205 * Perform a sanity check
6207 if ((plkbyt == 0) || (ppstgOpen == 0))
6208 return STG_E_INVALIDPOINTER;
6211 * Validate the STGM flags
6213 if ( FAILED( validateSTGM(grfMode) ))
6214 return STG_E_INVALIDFLAG;
6217 * Initialize the "out" parameter.
6222 * Allocate and initialize the new IStorage object.
6224 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6226 if (newStorage == 0)
6227 return STG_E_INSUFFICIENTMEMORY;
6229 hr = StorageImpl_Construct(
6240 HeapFree(GetProcessHeap(), 0, newStorage);
6245 * Get an "out" pointer for the caller.
6247 hr = StorageBaseImpl_QueryInterface(
6248 (IStorage*)newStorage,
6249 (REFIID)&IID_IStorage,
6255 /******************************************************************************
6256 * StgSetTimes [ole32.@]
6257 * StgSetTimes [OLE32.@]
6261 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6262 FILETIME const *patime, FILETIME const *pmtime)
6264 IStorage *stg = NULL;
6267 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6269 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6273 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6274 IStorage_Release(stg);
6280 /******************************************************************************
6281 * StgIsStorageILockBytes [OLE32.@]
6283 * Determines if the ILockBytes contains a storage object.
6285 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6288 ULARGE_INTEGER offset;
6290 offset.u.HighPart = 0;
6291 offset.u.LowPart = 0;
6293 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6295 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6301 /******************************************************************************
6302 * WriteClassStg [OLE32.@]
6304 * This method will store the specified CLSID in the specified storage object
6306 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6311 return E_INVALIDARG;
6313 hRes = IStorage_SetClass(pStg, rclsid);
6318 /***********************************************************************
6319 * ReadClassStg (OLE32.@)
6321 * This method reads the CLSID previously written to a storage object with
6322 * the WriteClassStg.
6325 * pstg [I] IStorage pointer
6326 * pclsid [O] Pointer to where the CLSID is written
6330 * Failure: HRESULT code.
6332 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6337 TRACE("(%p, %p)\n", pstg, pclsid);
6339 if(!pstg || !pclsid)
6340 return E_INVALIDARG;
6343 * read a STATSTG structure (contains the clsid) from the storage
6345 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
6348 *pclsid=pstatstg.clsid;
6353 /***********************************************************************
6354 * OleLoadFromStream (OLE32.@)
6356 * This function loads an object from stream
6358 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6362 LPPERSISTSTREAM xstm;
6364 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6366 res=ReadClassStm(pStm,&clsid);
6367 if (!SUCCEEDED(res))
6369 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6370 if (!SUCCEEDED(res))
6372 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6373 if (!SUCCEEDED(res)) {
6374 IUnknown_Release((IUnknown*)*ppvObj);
6377 res=IPersistStream_Load(xstm,pStm);
6378 IPersistStream_Release(xstm);
6379 /* FIXME: all refcounts ok at this point? I think they should be:
6382 * xstm : 0 (released)
6387 /***********************************************************************
6388 * OleSaveToStream (OLE32.@)
6390 * This function saves an object with the IPersistStream interface on it
6391 * to the specified stream.
6393 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6399 TRACE("(%p,%p)\n",pPStm,pStm);
6401 res=IPersistStream_GetClassID(pPStm,&clsid);
6403 if (SUCCEEDED(res)){
6405 res=WriteClassStm(pStm,&clsid);
6409 res=IPersistStream_Save(pPStm,pStm,TRUE);
6412 TRACE("Finished Save\n");
6416 /****************************************************************************
6417 * This method validate a STGM parameter that can contain the values below
6419 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6420 * The stgm values contained in 0xffff0000 are bitmasks.
6422 * STGM_DIRECT 0x00000000
6423 * STGM_TRANSACTED 0x00010000
6424 * STGM_SIMPLE 0x08000000
6426 * STGM_READ 0x00000000
6427 * STGM_WRITE 0x00000001
6428 * STGM_READWRITE 0x00000002
6430 * STGM_SHARE_DENY_NONE 0x00000040
6431 * STGM_SHARE_DENY_READ 0x00000030
6432 * STGM_SHARE_DENY_WRITE 0x00000020
6433 * STGM_SHARE_EXCLUSIVE 0x00000010
6435 * STGM_PRIORITY 0x00040000
6436 * STGM_DELETEONRELEASE 0x04000000
6438 * STGM_CREATE 0x00001000
6439 * STGM_CONVERT 0x00020000
6440 * STGM_FAILIFTHERE 0x00000000
6442 * STGM_NOSCRATCH 0x00100000
6443 * STGM_NOSNAPSHOT 0x00200000
6445 static HRESULT validateSTGM(DWORD stgm)
6447 DWORD access = STGM_ACCESS_MODE(stgm);
6448 DWORD share = STGM_SHARE_MODE(stgm);
6449 DWORD create = STGM_CREATE_MODE(stgm);
6451 if (stgm&~STGM_KNOWN_FLAGS)
6453 ERR("unknown flags %08x\n", stgm);
6461 case STGM_READWRITE:
6469 case STGM_SHARE_DENY_NONE:
6470 case STGM_SHARE_DENY_READ:
6471 case STGM_SHARE_DENY_WRITE:
6472 case STGM_SHARE_EXCLUSIVE:
6481 case STGM_FAILIFTHERE:
6488 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6490 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6494 * STGM_CREATE | STGM_CONVERT
6495 * if both are false, STGM_FAILIFTHERE is set to TRUE
6497 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6501 * STGM_NOSCRATCH requires STGM_TRANSACTED
6503 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6507 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6508 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6510 if ( (stgm & STGM_NOSNAPSHOT) &&
6511 (!(stgm & STGM_TRANSACTED) ||
6512 share == STGM_SHARE_EXCLUSIVE ||
6513 share == STGM_SHARE_DENY_WRITE) )
6519 /****************************************************************************
6520 * GetShareModeFromSTGM
6522 * This method will return a share mode flag from a STGM value.
6523 * The STGM value is assumed valid.
6525 static DWORD GetShareModeFromSTGM(DWORD stgm)
6527 switch (STGM_SHARE_MODE(stgm))
6529 case STGM_SHARE_DENY_NONE:
6530 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6531 case STGM_SHARE_DENY_READ:
6532 return FILE_SHARE_WRITE;
6533 case STGM_SHARE_DENY_WRITE:
6534 return FILE_SHARE_READ;
6535 case STGM_SHARE_EXCLUSIVE:
6538 ERR("Invalid share mode!\n");
6543 /****************************************************************************
6544 * GetAccessModeFromSTGM
6546 * This method will return an access mode flag from a STGM value.
6547 * The STGM value is assumed valid.
6549 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6551 switch (STGM_ACCESS_MODE(stgm))
6554 return GENERIC_READ;
6556 case STGM_READWRITE:
6557 return GENERIC_READ | GENERIC_WRITE;
6559 ERR("Invalid access mode!\n");
6564 /****************************************************************************
6565 * GetCreationModeFromSTGM
6567 * This method will return a creation mode flag from a STGM value.
6568 * The STGM value is assumed valid.
6570 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6572 switch(STGM_CREATE_MODE(stgm))
6575 return CREATE_ALWAYS;
6577 FIXME("STGM_CONVERT not implemented!\n");
6579 case STGM_FAILIFTHERE:
6582 ERR("Invalid create mode!\n");
6588 /*************************************************************************
6589 * OLECONVERT_LoadOLE10 [Internal]
6591 * Loads the OLE10 STREAM to memory
6594 * pOleStream [I] The OLESTREAM
6595 * pData [I] Data Structure for the OLESTREAM Data
6599 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6600 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6603 * This function is used by OleConvertOLESTREAMToIStorage only.
6605 * Memory allocated for pData must be freed by the caller
6607 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6610 HRESULT hRes = S_OK;
6614 pData->pData = NULL;
6615 pData->pstrOleObjFileName = (CHAR *) NULL;
6617 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6620 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6621 if(dwSize != sizeof(pData->dwOleID))
6623 hRes = CONVERT10_E_OLESTREAM_GET;
6625 else if(pData->dwOleID != OLESTREAM_ID)
6627 hRes = CONVERT10_E_OLESTREAM_FMT;
6638 /* Get the TypeID...more info needed for this field */
6639 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6640 if(dwSize != sizeof(pData->dwTypeID))
6642 hRes = CONVERT10_E_OLESTREAM_GET;
6647 if(pData->dwTypeID != 0)
6649 /* Get the length of the OleTypeName */
6650 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6651 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6653 hRes = CONVERT10_E_OLESTREAM_GET;
6658 if(pData->dwOleTypeNameLength > 0)
6660 /* Get the OleTypeName */
6661 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6662 if(dwSize != pData->dwOleTypeNameLength)
6664 hRes = CONVERT10_E_OLESTREAM_GET;
6670 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6671 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6673 hRes = CONVERT10_E_OLESTREAM_GET;
6677 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6678 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6679 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6680 if(pData->pstrOleObjFileName)
6682 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6683 if(dwSize != pData->dwOleObjFileNameLength)
6685 hRes = CONVERT10_E_OLESTREAM_GET;
6689 hRes = CONVERT10_E_OLESTREAM_GET;
6694 /* Get the Width of the Metafile */
6695 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6696 if(dwSize != sizeof(pData->dwMetaFileWidth))
6698 hRes = CONVERT10_E_OLESTREAM_GET;
6702 /* Get the Height of the Metafile */
6703 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6704 if(dwSize != sizeof(pData->dwMetaFileHeight))
6706 hRes = CONVERT10_E_OLESTREAM_GET;
6712 /* Get the Length of the Data */
6713 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6714 if(dwSize != sizeof(pData->dwDataLength))
6716 hRes = CONVERT10_E_OLESTREAM_GET;
6720 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6722 if(!bStrem1) /* if it is a second OLE stream data */
6724 pData->dwDataLength -= 8;
6725 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6726 if(dwSize != sizeof(pData->strUnknown))
6728 hRes = CONVERT10_E_OLESTREAM_GET;
6734 if(pData->dwDataLength > 0)
6736 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6738 /* Get Data (ex. IStorage, Metafile, or BMP) */
6741 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6742 if(dwSize != pData->dwDataLength)
6744 hRes = CONVERT10_E_OLESTREAM_GET;
6749 hRes = CONVERT10_E_OLESTREAM_GET;
6758 /*************************************************************************
6759 * OLECONVERT_SaveOLE10 [Internal]
6761 * Saves the OLE10 STREAM From memory
6764 * pData [I] Data Structure for the OLESTREAM Data
6765 * pOleStream [I] The OLESTREAM to save
6769 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6772 * This function is used by OleConvertIStorageToOLESTREAM only.
6775 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6778 HRESULT hRes = S_OK;
6782 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6783 if(dwSize != sizeof(pData->dwOleID))
6785 hRes = CONVERT10_E_OLESTREAM_PUT;
6790 /* Set the TypeID */
6791 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6792 if(dwSize != sizeof(pData->dwTypeID))
6794 hRes = CONVERT10_E_OLESTREAM_PUT;
6798 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6800 /* Set the Length of the OleTypeName */
6801 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6802 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6804 hRes = CONVERT10_E_OLESTREAM_PUT;
6809 if(pData->dwOleTypeNameLength > 0)
6811 /* Set the OleTypeName */
6812 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6813 if(dwSize != pData->dwOleTypeNameLength)
6815 hRes = CONVERT10_E_OLESTREAM_PUT;
6822 /* Set the width of the Metafile */
6823 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6824 if(dwSize != sizeof(pData->dwMetaFileWidth))
6826 hRes = CONVERT10_E_OLESTREAM_PUT;
6832 /* Set the height of the Metafile */
6833 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6834 if(dwSize != sizeof(pData->dwMetaFileHeight))
6836 hRes = CONVERT10_E_OLESTREAM_PUT;
6842 /* Set the length of the Data */
6843 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6844 if(dwSize != sizeof(pData->dwDataLength))
6846 hRes = CONVERT10_E_OLESTREAM_PUT;
6852 if(pData->dwDataLength > 0)
6854 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6855 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6856 if(dwSize != pData->dwDataLength)
6858 hRes = CONVERT10_E_OLESTREAM_PUT;
6866 /*************************************************************************
6867 * OLECONVERT_GetOLE20FromOLE10[Internal]
6869 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6870 * opens it, and copies the content to the dest IStorage for
6871 * OleConvertOLESTREAMToIStorage
6875 * pDestStorage [I] The IStorage to copy the data to
6876 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6877 * nBufferLength [I] The size of the buffer
6886 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6890 IStorage *pTempStorage;
6891 DWORD dwNumOfBytesWritten;
6892 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6893 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6895 /* Create a temp File */
6896 GetTempPathW(MAX_PATH, wstrTempDir);
6897 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6898 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6900 if(hFile != INVALID_HANDLE_VALUE)
6902 /* Write IStorage Data to File */
6903 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6906 /* Open and copy temp storage to the Dest Storage */
6907 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6910 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6911 StorageBaseImpl_Release(pTempStorage);
6913 DeleteFileW(wstrTempFile);
6918 /*************************************************************************
6919 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6921 * Saves the OLE10 STREAM From memory
6924 * pStorage [I] The Src IStorage to copy
6925 * pData [I] The Dest Memory to write to.
6928 * The size in bytes allocated for pData
6931 * Memory allocated for pData must be freed by the caller
6933 * Used by OleConvertIStorageToOLESTREAM only.
6936 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6940 DWORD nDataLength = 0;
6941 IStorage *pTempStorage;
6942 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6943 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6947 /* Create temp Storage */
6948 GetTempPathW(MAX_PATH, wstrTempDir);
6949 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6950 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6954 /* Copy Src Storage to the Temp Storage */
6955 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6956 StorageBaseImpl_Release(pTempStorage);
6958 /* Open Temp Storage as a file and copy to memory */
6959 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6960 if(hFile != INVALID_HANDLE_VALUE)
6962 nDataLength = GetFileSize(hFile, NULL);
6963 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6964 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6967 DeleteFileW(wstrTempFile);
6972 /*************************************************************************
6973 * OLECONVERT_CreateOleStream [Internal]
6975 * Creates the "\001OLE" stream in the IStorage if necessary.
6978 * pStorage [I] Dest storage to create the stream in
6984 * This function is used by OleConvertOLESTREAMToIStorage only.
6986 * This stream is still unknown, MS Word seems to have extra data
6987 * but since the data is stored in the OLESTREAM there should be
6988 * no need to recreate the stream. If the stream is manually
6989 * deleted it will create it with this default data.
6992 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6996 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6997 BYTE pOleStreamHeader [] =
6999 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
7000 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7001 0x00, 0x00, 0x00, 0x00
7004 /* Create stream if not present */
7005 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7006 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7010 /* Write default Data */
7011 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
7012 IStream_Release(pStream);
7016 /* write a string to a stream, preceded by its length */
7017 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
7024 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
7025 r = IStream_Write( stm, &len, sizeof(len), NULL);
7030 str = CoTaskMemAlloc( len );
7031 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
7032 r = IStream_Write( stm, str, len, NULL);
7033 CoTaskMemFree( str );
7037 /* read a string preceded by its length from a stream */
7038 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
7041 DWORD len, count = 0;
7045 r = IStream_Read( stm, &len, sizeof(len), &count );
7048 if( count != sizeof(len) )
7049 return E_OUTOFMEMORY;
7051 TRACE("%d bytes\n",len);
7053 str = CoTaskMemAlloc( len );
7055 return E_OUTOFMEMORY;
7057 r = IStream_Read( stm, str, len, &count );
7062 CoTaskMemFree( str );
7063 return E_OUTOFMEMORY;
7066 TRACE("Read string %s\n",debugstr_an(str,len));
7068 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7069 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7071 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7072 CoTaskMemFree( str );
7080 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7081 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7085 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7087 static const BYTE unknown1[12] =
7088 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7089 0xFF, 0xFF, 0xFF, 0xFF};
7090 static const BYTE unknown2[16] =
7091 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7092 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7094 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7095 debugstr_w(lpszUserType), debugstr_w(szClipName),
7096 debugstr_w(szProgIDName));
7098 /* Create a CompObj stream if it doesn't exist */
7099 r = IStorage_CreateStream(pstg, szwStreamName,
7100 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7104 /* Write CompObj Structure to stream */
7105 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7107 if( SUCCEEDED( r ) )
7108 r = WriteClassStm( pstm, clsid );
7110 if( SUCCEEDED( r ) )
7111 r = STREAM_WriteString( pstm, lpszUserType );
7112 if( SUCCEEDED( r ) )
7113 r = STREAM_WriteString( pstm, szClipName );
7114 if( SUCCEEDED( r ) )
7115 r = STREAM_WriteString( pstm, szProgIDName );
7116 if( SUCCEEDED( r ) )
7117 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7119 IStream_Release( pstm );
7124 /***********************************************************************
7125 * WriteFmtUserTypeStg (OLE32.@)
7127 HRESULT WINAPI WriteFmtUserTypeStg(
7128 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7131 WCHAR szwClipName[0x40];
7132 CLSID clsid = CLSID_NULL;
7133 LPWSTR wstrProgID = NULL;
7136 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7138 /* get the clipboard format name */
7139 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
7142 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7144 /* FIXME: There's room to save a CLSID and its ProgID, but
7145 the CLSID is not looked up in the registry and in all the
7146 tests I wrote it was CLSID_NULL. Where does it come from?
7149 /* get the real program ID. This may fail, but that's fine */
7150 ProgIDFromCLSID(&clsid, &wstrProgID);
7152 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7154 r = STORAGE_WriteCompObj( pstg, &clsid,
7155 lpszUserType, szwClipName, wstrProgID );
7157 CoTaskMemFree(wstrProgID);
7163 /******************************************************************************
7164 * ReadFmtUserTypeStg [OLE32.@]
7166 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7170 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7171 unsigned char unknown1[12];
7172 unsigned char unknown2[16];
7174 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7177 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7179 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7180 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7183 WARN("Failed to open stream r = %08x\n", r);
7187 /* read the various parts of the structure */
7188 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7189 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7191 r = ReadClassStm( stm, &clsid );
7195 r = STREAM_ReadString( stm, &szCLSIDName );
7199 r = STREAM_ReadString( stm, &szOleTypeName );
7203 r = STREAM_ReadString( stm, &szProgIDName );
7207 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7208 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7211 /* ok, success... now we just need to store what we found */
7213 *pcf = RegisterClipboardFormatW( szOleTypeName );
7214 CoTaskMemFree( szOleTypeName );
7216 if( lplpszUserType )
7217 *lplpszUserType = szCLSIDName;
7218 CoTaskMemFree( szProgIDName );
7221 IStream_Release( stm );
7227 /*************************************************************************
7228 * OLECONVERT_CreateCompObjStream [Internal]
7230 * Creates a "\001CompObj" is the destination IStorage if necessary.
7233 * pStorage [I] The dest IStorage to create the CompObj Stream
7235 * strOleTypeName [I] The ProgID
7239 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7242 * This function is used by OleConvertOLESTREAMToIStorage only.
7244 * The stream data is stored in the OLESTREAM and there should be
7245 * no need to recreate the stream. If the stream is manually
7246 * deleted it will attempt to create it by querying the registry.
7250 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7253 HRESULT hStorageRes, hRes = S_OK;
7254 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7255 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7256 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7258 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7259 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7261 /* Initialize the CompObj structure */
7262 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7263 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
7264 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
7267 /* Create a CompObj stream if it doesn't exist */
7268 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7269 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7270 if(hStorageRes == S_OK)
7272 /* copy the OleTypeName to the compobj struct */
7273 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7274 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7276 /* copy the OleTypeName to the compobj struct */
7277 /* Note: in the test made, these were Identical */
7278 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7279 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7282 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7283 bufferW, OLESTREAM_MAX_STR_LEN );
7284 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7290 /* Get the CLSID Default Name from the Registry */
7291 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7292 if(hErr == ERROR_SUCCESS)
7294 char strTemp[OLESTREAM_MAX_STR_LEN];
7295 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7296 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7297 if(hErr == ERROR_SUCCESS)
7299 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7305 /* Write CompObj Structure to stream */
7306 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7308 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7310 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7311 if(IStorageCompObj.dwCLSIDNameLength > 0)
7313 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7315 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7316 if(IStorageCompObj.dwOleTypeNameLength > 0)
7318 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7320 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7321 if(IStorageCompObj.dwProgIDNameLength > 0)
7323 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7325 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7326 IStream_Release(pStream);
7332 /*************************************************************************
7333 * OLECONVERT_CreateOlePresStream[Internal]
7335 * Creates the "\002OlePres000" Stream with the Metafile data
7338 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7339 * dwExtentX [I] Width of the Metafile
7340 * dwExtentY [I] Height of the Metafile
7341 * pData [I] Metafile data
7342 * dwDataLength [I] Size of the Metafile data
7346 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7349 * This function is used by OleConvertOLESTREAMToIStorage only.
7352 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7356 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7357 BYTE pOlePresStreamHeader [] =
7359 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7360 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7361 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7362 0x00, 0x00, 0x00, 0x00
7365 BYTE pOlePresStreamHeaderEmpty [] =
7367 0x00, 0x00, 0x00, 0x00,
7368 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7369 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7370 0x00, 0x00, 0x00, 0x00
7373 /* Create the OlePres000 Stream */
7374 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7375 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7380 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7382 memset(&OlePres, 0, sizeof(OlePres));
7383 /* Do we have any metafile data to save */
7384 if(dwDataLength > 0)
7386 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7387 nHeaderSize = sizeof(pOlePresStreamHeader);
7391 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7392 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7394 /* Set width and height of the metafile */
7395 OlePres.dwExtentX = dwExtentX;
7396 OlePres.dwExtentY = -dwExtentY;
7398 /* Set Data and Length */
7399 if(dwDataLength > sizeof(METAFILEPICT16))
7401 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7402 OlePres.pData = &(pData[8]);
7404 /* Save OlePres000 Data to Stream */
7405 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7406 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7407 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7408 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7409 if(OlePres.dwSize > 0)
7411 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7413 IStream_Release(pStream);
7417 /*************************************************************************
7418 * OLECONVERT_CreateOle10NativeStream [Internal]
7420 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7423 * pStorage [I] Dest storage to create the stream in
7424 * pData [I] Ole10 Native Data (ex. bmp)
7425 * dwDataLength [I] Size of the Ole10 Native Data
7431 * This function is used by OleConvertOLESTREAMToIStorage only.
7433 * Might need to verify the data and return appropriate error message
7436 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7440 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7442 /* Create the Ole10Native Stream */
7443 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7444 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7448 /* Write info to stream */
7449 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7450 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7451 IStream_Release(pStream);
7456 /*************************************************************************
7457 * OLECONVERT_GetOLE10ProgID [Internal]
7459 * Finds the ProgID (or OleTypeID) from the IStorage
7462 * pStorage [I] The Src IStorage to get the ProgID
7463 * strProgID [I] the ProgID string to get
7464 * dwSize [I] the size of the string
7468 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7471 * This function is used by OleConvertIStorageToOLESTREAM only.
7475 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7479 LARGE_INTEGER iSeekPos;
7480 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7481 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7483 /* Open the CompObj Stream */
7484 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7485 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7489 /*Get the OleType from the CompObj Stream */
7490 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7491 iSeekPos.u.HighPart = 0;
7493 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7494 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7495 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7496 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7497 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7498 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7499 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7501 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7504 IStream_Read(pStream, strProgID, *dwSize, NULL);
7506 IStream_Release(pStream);
7511 LPOLESTR wstrProgID;
7513 /* Get the OleType from the registry */
7514 REFCLSID clsid = &(stat.clsid);
7515 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7516 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7519 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7526 /*************************************************************************
7527 * OLECONVERT_GetOle10PresData [Internal]
7529 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7532 * pStorage [I] Src IStroage
7533 * pOleStream [I] Dest OleStream Mem Struct
7539 * This function is used by OleConvertIStorageToOLESTREAM only.
7541 * Memory allocated for pData must be freed by the caller
7545 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7550 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7552 /* Initialize Default data for OLESTREAM */
7553 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7554 pOleStreamData[0].dwTypeID = 2;
7555 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7556 pOleStreamData[1].dwTypeID = 0;
7557 pOleStreamData[0].dwMetaFileWidth = 0;
7558 pOleStreamData[0].dwMetaFileHeight = 0;
7559 pOleStreamData[0].pData = NULL;
7560 pOleStreamData[1].pData = NULL;
7562 /* Open Ole10Native Stream */
7563 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7564 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7568 /* Read Size and Data */
7569 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7570 if(pOleStreamData->dwDataLength > 0)
7572 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7573 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7575 IStream_Release(pStream);
7581 /*************************************************************************
7582 * OLECONVERT_GetOle20PresData[Internal]
7584 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7587 * pStorage [I] Src IStroage
7588 * pOleStreamData [I] Dest OleStream Mem Struct
7594 * This function is used by OleConvertIStorageToOLESTREAM only.
7596 * Memory allocated for pData must be freed by the caller
7598 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7602 OLECONVERT_ISTORAGE_OLEPRES olePress;
7603 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7605 /* Initialize Default data for OLESTREAM */
7606 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7607 pOleStreamData[0].dwTypeID = 2;
7608 pOleStreamData[0].dwMetaFileWidth = 0;
7609 pOleStreamData[0].dwMetaFileHeight = 0;
7610 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7611 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7612 pOleStreamData[1].dwTypeID = 0;
7613 pOleStreamData[1].dwOleTypeNameLength = 0;
7614 pOleStreamData[1].strOleTypeName[0] = 0;
7615 pOleStreamData[1].dwMetaFileWidth = 0;
7616 pOleStreamData[1].dwMetaFileHeight = 0;
7617 pOleStreamData[1].pData = NULL;
7618 pOleStreamData[1].dwDataLength = 0;
7621 /* Open OlePress000 stream */
7622 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7623 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7626 LARGE_INTEGER iSeekPos;
7627 METAFILEPICT16 MetaFilePict;
7628 static const char strMetafilePictName[] = "METAFILEPICT";
7630 /* Set the TypeID for a Metafile */
7631 pOleStreamData[1].dwTypeID = 5;
7633 /* Set the OleTypeName to Metafile */
7634 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7635 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7637 iSeekPos.u.HighPart = 0;
7638 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7640 /* Get Presentation Data */
7641 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7642 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7643 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7644 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7646 /*Set width and Height */
7647 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7648 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7649 if(olePress.dwSize > 0)
7652 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7654 /* Set MetaFilePict struct */
7655 MetaFilePict.mm = 8;
7656 MetaFilePict.xExt = olePress.dwExtentX;
7657 MetaFilePict.yExt = olePress.dwExtentY;
7658 MetaFilePict.hMF = 0;
7660 /* Get Metafile Data */
7661 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7662 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7663 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7665 IStream_Release(pStream);
7669 /*************************************************************************
7670 * OleConvertOLESTREAMToIStorage [OLE32.@]
7675 * DVTARGETDEVICE paramenter is not handled
7676 * Still unsure of some mem fields for OLE 10 Stream
7677 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7678 * and "\001OLE" streams
7681 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7682 LPOLESTREAM pOleStream,
7684 const DVTARGETDEVICE* ptd)
7688 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7690 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
7692 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7696 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7699 if(pstg == NULL || pOleStream == NULL)
7701 hRes = E_INVALIDARG;
7706 /* Load the OLESTREAM to Memory */
7707 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7712 /* Load the OLESTREAM to Memory (part 2)*/
7713 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7719 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7721 /* Do we have the IStorage Data in the OLESTREAM */
7722 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7724 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7725 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7729 /* It must be an original OLE 1.0 source */
7730 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7735 /* It must be an original OLE 1.0 source */
7736 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7739 /* Create CompObj Stream if necessary */
7740 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7743 /*Create the Ole Stream if necessary */
7744 OLECONVERT_CreateOleStream(pstg);
7749 /* Free allocated memory */
7750 for(i=0; i < 2; i++)
7752 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7753 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7754 pOleStreamData[i].pstrOleObjFileName = NULL;
7759 /*************************************************************************
7760 * OleConvertIStorageToOLESTREAM [OLE32.@]
7767 * Still unsure of some mem fields for OLE 10 Stream
7768 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7769 * and "\001OLE" streams.
7772 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7774 LPOLESTREAM pOleStream)
7777 HRESULT hRes = S_OK;
7779 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7780 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7782 TRACE("%p %p\n", pstg, pOleStream);
7784 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7786 if(pstg == NULL || pOleStream == NULL)
7788 hRes = E_INVALIDARG;
7792 /* Get the ProgID */
7793 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7794 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7798 /* Was it originally Ole10 */
7799 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7802 IStream_Release(pStream);
7803 /* Get Presentation Data for Ole10Native */
7804 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7808 /* Get Presentation Data (OLE20) */
7809 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7812 /* Save OLESTREAM */
7813 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7816 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7821 /* Free allocated memory */
7822 for(i=0; i < 2; i++)
7824 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7830 /***********************************************************************
7831 * GetConvertStg (OLE32.@)
7833 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7834 FIXME("unimplemented stub!\n");
7838 /******************************************************************************
7839 * StgIsStorageFile [OLE32.@]
7840 * Verify if the file contains a storage object
7846 * S_OK if file has magic bytes as a storage object
7847 * S_FALSE if file is not storage
7850 StgIsStorageFile(LPCOLESTR fn)
7856 TRACE("%s\n", debugstr_w(fn));
7857 hf = CreateFileW(fn, GENERIC_READ,
7858 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7859 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7861 if (hf == INVALID_HANDLE_VALUE)
7862 return STG_E_FILENOTFOUND;
7864 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7866 WARN(" unable to read file\n");
7873 if (bytes_read != 8) {
7874 WARN(" too short\n");
7878 if (!memcmp(magic,STORAGE_magic,8)) {
7883 WARN(" -> Invalid header.\n");
7887 /***********************************************************************
7888 * WriteClassStm (OLE32.@)
7890 * Writes a CLSID to a stream.
7893 * pStm [I] Stream to write to.
7894 * rclsid [I] CLSID to write.
7898 * Failure: HRESULT code.
7900 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
7902 TRACE("(%p,%p)\n",pStm,rclsid);
7904 if (!pStm || !rclsid)
7905 return E_INVALIDARG;
7907 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
7910 /***********************************************************************
7911 * ReadClassStm (OLE32.@)
7913 * Reads a CLSID from a stream.
7916 * pStm [I] Stream to read from.
7917 * rclsid [O] CLSID to read.
7921 * Failure: HRESULT code.
7923 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
7928 TRACE("(%p,%p)\n",pStm,pclsid);
7930 if (!pStm || !pclsid)
7931 return E_INVALIDARG;
7933 /* clear the output args */
7934 memcpy(pclsid, &CLSID_NULL, sizeof(*pclsid));
7936 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
7941 if (nbByte != sizeof(CLSID))
7942 return STG_E_READFAULT;