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.
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
47 #include "wine/unicode.h"
48 #include "wine/debug.h"
50 #include "storage32.h"
51 #include "ole2.h" /* For Write/ReadClassStm */
54 #include "wine/wingdi16.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(storage);
58 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
59 #define OLESTREAM_ID 0x501
60 #define OLESTREAM_MAX_STR_LEN 255
63 * These are signatures to detect the type of Document file.
65 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
66 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
68 static const char rootPropertyName[] = "Root Entry";
70 /****************************************************************************
71 * Storage32InternalImpl definitions.
73 * Definition of the implementation structure for the IStorage32 interface.
74 * This one implements the IStorage32 interface for storage that are
75 * inside another storage.
77 struct StorageInternalImpl
79 struct StorageBaseImpl base;
81 * There is no specific data for this class.
84 typedef struct StorageInternalImpl StorageInternalImpl;
86 /* Method definitions for the Storage32InternalImpl class. */
87 static StorageInternalImpl* StorageInternalImpl_Construct(StorageImpl* ancestorStorage,
88 DWORD openFlags, ULONG rootTropertyIndex);
89 static void StorageImpl_Destroy(StorageBaseImpl* iface);
90 static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
91 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const 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 Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
97 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
98 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
99 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
100 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
102 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
103 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
104 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
106 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
107 static ULONG SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream* This);
108 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
109 ULONG blockIndex, ULONG offset, DWORD value);
110 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This,
111 ULONG blockIndex, ULONG offset, DWORD* value);
113 /* OLESTREAM memory structure to use for Get and Put Routines */
114 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
119 DWORD dwOleTypeNameLength;
120 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
121 CHAR *pstrOleObjFileName;
122 DWORD dwOleObjFileNameLength;
123 DWORD dwMetaFileWidth;
124 DWORD dwMetaFileHeight;
125 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
128 }OLECONVERT_OLESTREAM_DATA;
130 /* CompObj Stream structure */
131 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
136 DWORD dwCLSIDNameLength;
137 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
138 DWORD dwOleTypeNameLength;
139 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
140 DWORD dwProgIDNameLength;
141 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
143 }OLECONVERT_ISTORAGE_COMPOBJ;
146 /* Ole Presentation Stream structure */
147 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
155 }OLECONVERT_ISTORAGE_OLEPRES;
159 /***********************************************************************
160 * Forward declaration of internal functions used by the method DestroyElement
162 static HRESULT deleteStorageProperty(
163 StorageImpl *parentStorage,
164 ULONG foundPropertyIndexToDelete,
165 StgProperty propertyToDelete);
167 static HRESULT deleteStreamProperty(
168 StorageImpl *parentStorage,
169 ULONG foundPropertyIndexToDelete,
170 StgProperty propertyToDelete);
172 static HRESULT findPlaceholder(
173 StorageImpl *storage,
174 ULONG propertyIndexToStore,
175 ULONG storagePropertyIndex,
178 static HRESULT adjustPropertyChain(
180 StgProperty propertyToDelete,
181 StgProperty parentProperty,
182 ULONG parentPropertyId,
185 /***********************************************************************
186 * Declaration of the functions used to manipulate StgProperty
189 static ULONG getFreeProperty(
190 StorageImpl *storage);
192 static void updatePropertyChain(
193 StorageImpl *storage,
194 ULONG newPropertyIndex,
195 StgProperty newProperty);
197 static LONG propertyNameCmp(
198 const OLECHAR *newProperty,
199 const OLECHAR *currentProperty);
201 static ULONG findElement(
202 StorageImpl *storage,
207 /***********************************************************************
208 * Declaration of miscellaneous functions...
210 static HRESULT validateSTGM(DWORD stgmValue);
212 static DWORD GetShareModeFromSTGM(DWORD stgm);
213 static DWORD GetAccessModeFromSTGM(DWORD stgm);
214 static DWORD GetCreationModeFromSTGM(DWORD stgm);
216 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
219 /****************************************************************************
220 * IEnumSTATSTGImpl definitions.
222 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
223 * This class allows iterating through the content of a storage and to find
224 * specific items inside it.
226 struct IEnumSTATSTGImpl
228 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
229 * since we want to cast this in an IEnumSTATSTG pointer */
231 LONG ref; /* Reference count */
232 StorageImpl* parentStorage; /* Reference to the parent storage */
233 ULONG firstPropertyNode; /* Index of the root of the storage to enumerate */
236 * The current implementation of the IEnumSTATSTGImpl class uses a stack
237 * to walk the property sets to get the content of a storage. This stack
238 * is implemented by the following 3 data members
244 #define ENUMSTATSGT_SIZE_INCREMENT 10
248 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageImpl* This, ULONG firstPropertyNode);
249 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
250 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl* This, ULONG nodeToPush);
251 static ULONG IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl* This, BOOL remove);
252 static INT IEnumSTATSTGImpl_FindParentProperty(IEnumSTATSTGImpl *This, ULONG childProperty,
253 StgProperty *currentProperty, ULONG *propertyId);
255 /************************************************************************
259 static ULONG BLOCK_GetBigBlockOffset(ULONG index)
261 if (index == 0xffffffff)
266 return index * BIG_BLOCK_SIZE;
269 /************************************************************************
270 ** Storage32BaseImpl implementation
272 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
273 ULARGE_INTEGER offset,
278 return BIGBLOCKFILE_ReadAt(This->bigBlockFile,offset,buffer,size,bytesRead);
281 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
282 ULARGE_INTEGER offset,
287 return BIGBLOCKFILE_WriteAt(This->bigBlockFile,offset,buffer,size,bytesWritten);
290 /************************************************************************
291 * Storage32BaseImpl_QueryInterface (IUnknown)
293 * This method implements the common QueryInterface for all IStorage32
294 * implementations contained in this file.
296 * See Windows documentation for more details on IUnknown methods.
298 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
303 StorageBaseImpl *This = (StorageBaseImpl *)iface;
305 if ( (This==0) || (ppvObject==0) )
310 if (IsEqualGUID(&IID_IUnknown, riid) ||
311 IsEqualGUID(&IID_IStorage, riid))
315 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
317 *ppvObject = &This->pssVtbl;
321 return E_NOINTERFACE;
323 IStorage_AddRef(iface);
328 /************************************************************************
329 * Storage32BaseImpl_AddRef (IUnknown)
331 * This method implements the common AddRef for all IStorage32
332 * implementations contained in this file.
334 * See Windows documentation for more details on IUnknown methods.
336 static ULONG WINAPI StorageBaseImpl_AddRef(
339 StorageBaseImpl *This = (StorageBaseImpl *)iface;
340 ULONG ref = InterlockedIncrement(&This->ref);
342 TRACE("(%p) AddRef to %d\n", This, ref);
347 /************************************************************************
348 * Storage32BaseImpl_Release (IUnknown)
350 * This method implements the common Release for all IStorage32
351 * implementations contained in this file.
353 * See Windows documentation for more details on IUnknown methods.
355 static ULONG WINAPI StorageBaseImpl_Release(
358 StorageBaseImpl *This = (StorageBaseImpl *)iface;
360 ULONG ref = InterlockedDecrement(&This->ref);
362 TRACE("(%p) ReleaseRef to %d\n", This, ref);
367 * Since we are using a system of base-classes, we want to call the
368 * destructor of the appropriate derived class. To do this, we are
369 * using virtual functions to implement the destructor.
371 This->v_destructor(This);
377 /************************************************************************
378 * Storage32BaseImpl_OpenStream (IStorage)
380 * This method will open the specified stream object from the current storage.
382 * See Windows documentation for more details on IStorage methods.
384 static HRESULT WINAPI StorageBaseImpl_OpenStream(
386 const OLECHAR* pwcsName, /* [string][in] */
387 void* reserved1, /* [unique][in] */
388 DWORD grfMode, /* [in] */
389 DWORD reserved2, /* [in] */
390 IStream** ppstm) /* [out] */
392 StorageBaseImpl *This = (StorageBaseImpl *)iface;
393 StgStreamImpl* newStream;
394 StgProperty currentProperty;
395 ULONG foundPropertyIndex;
396 HRESULT res = STG_E_UNKNOWN;
398 TRACE("(%p, %s, %p, %x, %d, %p)\n",
399 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
401 if ( (pwcsName==NULL) || (ppstm==0) )
409 if ( FAILED( validateSTGM(grfMode) ) ||
410 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
412 res = STG_E_INVALIDFLAG;
419 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
421 res = STG_E_INVALIDFUNCTION;
426 * Check that we're compatible with the parent's storage mode, but
427 * only if we are not in transacted mode
429 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
430 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
432 res = STG_E_ACCESSDENIED;
438 * Search for the element with the given name
440 foundPropertyIndex = findElement(
441 This->ancestorStorage,
442 This->rootPropertySetIndex,
447 * If it was found, construct the stream object and return a pointer to it.
449 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
450 (currentProperty.propertyType==PROPTYPE_STREAM) )
452 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
456 newStream->grfMode = grfMode;
457 *ppstm = (IStream*)newStream;
459 IStream_AddRef(*ppstm);
469 res = STG_E_FILENOTFOUND;
473 TRACE("<-- IStream %p\n", *ppstm);
474 TRACE("<-- %08x\n", res);
478 /************************************************************************
479 * Storage32BaseImpl_OpenStorage (IStorage)
481 * This method will open a new storage object from the current storage.
483 * See Windows documentation for more details on IStorage methods.
485 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
487 const OLECHAR* pwcsName, /* [string][unique][in] */
488 IStorage* pstgPriority, /* [unique][in] */
489 DWORD grfMode, /* [in] */
490 SNB snbExclude, /* [unique][in] */
491 DWORD reserved, /* [in] */
492 IStorage** ppstg) /* [out] */
494 StorageBaseImpl *This = (StorageBaseImpl *)iface;
495 StorageInternalImpl* newStorage;
496 StgProperty currentProperty;
497 ULONG foundPropertyIndex;
498 HRESULT res = STG_E_UNKNOWN;
500 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
501 iface, debugstr_w(pwcsName), pstgPriority,
502 grfMode, snbExclude, reserved, ppstg);
504 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
511 if (snbExclude != NULL)
513 res = STG_E_INVALIDPARAMETER;
517 if ( FAILED( validateSTGM(grfMode) ))
519 res = STG_E_INVALIDFLAG;
526 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
527 (grfMode & STGM_DELETEONRELEASE) ||
528 (grfMode & STGM_PRIORITY) )
530 res = STG_E_INVALIDFUNCTION;
535 * Check that we're compatible with the parent's storage mode,
536 * but only if we are not transacted
538 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
539 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
541 res = STG_E_ACCESSDENIED;
548 foundPropertyIndex = findElement(
549 This->ancestorStorage,
550 This->rootPropertySetIndex,
554 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
555 (currentProperty.propertyType==PROPTYPE_STORAGE) )
557 newStorage = StorageInternalImpl_Construct(
558 This->ancestorStorage,
564 *ppstg = (IStorage*)newStorage;
566 StorageBaseImpl_AddRef(*ppstg);
572 res = STG_E_INSUFFICIENTMEMORY;
576 res = STG_E_FILENOTFOUND;
579 TRACE("<-- %08x\n", res);
583 /************************************************************************
584 * Storage32BaseImpl_EnumElements (IStorage)
586 * This method will create an enumerator object that can be used to
587 * retrieve information about all the properties in the storage object.
589 * See Windows documentation for more details on IStorage methods.
591 static HRESULT WINAPI StorageBaseImpl_EnumElements(
593 DWORD reserved1, /* [in] */
594 void* reserved2, /* [size_is][unique][in] */
595 DWORD reserved3, /* [in] */
596 IEnumSTATSTG** ppenum) /* [out] */
598 StorageBaseImpl *This = (StorageBaseImpl *)iface;
599 IEnumSTATSTGImpl* newEnum;
601 TRACE("(%p, %d, %p, %d, %p)\n",
602 iface, reserved1, reserved2, reserved3, ppenum);
604 if ( (This==0) || (ppenum==0))
607 newEnum = IEnumSTATSTGImpl_Construct(
608 This->ancestorStorage,
609 This->rootPropertySetIndex);
613 *ppenum = (IEnumSTATSTG*)newEnum;
615 IEnumSTATSTG_AddRef(*ppenum);
620 return E_OUTOFMEMORY;
623 /************************************************************************
624 * Storage32BaseImpl_Stat (IStorage)
626 * This method will retrieve information about this storage object.
628 * See Windows documentation for more details on IStorage methods.
630 static HRESULT WINAPI StorageBaseImpl_Stat(
632 STATSTG* pstatstg, /* [out] */
633 DWORD grfStatFlag) /* [in] */
635 StorageBaseImpl *This = (StorageBaseImpl *)iface;
636 StgProperty curProperty;
638 HRESULT res = STG_E_UNKNOWN;
640 TRACE("(%p, %p, %x)\n",
641 iface, pstatstg, grfStatFlag);
643 if ( (This==0) || (pstatstg==0))
649 readSuccessful = StorageImpl_ReadProperty(
650 This->ancestorStorage,
651 This->rootPropertySetIndex,
656 StorageUtl_CopyPropertyToSTATSTG(
661 pstatstg->grfMode = This->openFlags;
662 pstatstg->grfStateBits = This->stateBits;
673 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);
675 TRACE("<-- %08x\n", res);
679 /************************************************************************
680 * Storage32BaseImpl_RenameElement (IStorage)
682 * This method will rename the specified element.
684 * See Windows documentation for more details on IStorage methods.
686 * Implementation notes: The method used to rename consists of creating a clone
687 * of the deleted StgProperty object setting it with the new name and to
688 * perform a DestroyElement of the old StgProperty.
690 static HRESULT WINAPI StorageBaseImpl_RenameElement(
692 const OLECHAR* pwcsOldName, /* [in] */
693 const OLECHAR* pwcsNewName) /* [in] */
695 StorageBaseImpl *This = (StorageBaseImpl *)iface;
696 StgProperty currentProperty;
697 ULONG foundPropertyIndex;
699 TRACE("(%p, %s, %s)\n",
700 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
702 foundPropertyIndex = findElement(This->ancestorStorage,
703 This->rootPropertySetIndex,
707 if (foundPropertyIndex != PROPERTY_NULL)
710 * There is already a property with the new name
712 return STG_E_FILEALREADYEXISTS;
716 * Search for the old element name
718 foundPropertyIndex = findElement(This->ancestorStorage,
719 This->rootPropertySetIndex,
723 if (foundPropertyIndex != PROPERTY_NULL)
725 StgProperty renamedProperty;
726 ULONG renamedPropertyIndex;
729 * Setup a new property for the renamed property
731 renamedProperty.sizeOfNameString =
732 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
734 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
735 return STG_E_INVALIDNAME;
737 strcpyW(renamedProperty.name, pwcsNewName);
739 renamedProperty.propertyType = currentProperty.propertyType;
740 renamedProperty.startingBlock = currentProperty.startingBlock;
741 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
742 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
744 renamedProperty.leftChild = PROPERTY_NULL;
745 renamedProperty.rightChild = PROPERTY_NULL;
748 * Bring the dirProperty link in case it is a storage and in which
749 * case the renamed storage elements don't require to be reorganized.
751 renamedProperty.dirProperty = currentProperty.dirProperty;
753 /* call CoFileTime to get the current time
754 renamedProperty.ctime
755 renamedProperty.mtime
756 renamedProperty.propertyUniqueID
760 * Obtain a free property in the property chain
762 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
765 * Save the new property into the new property spot
767 StorageImpl_WriteProperty(
768 This->ancestorStorage,
769 renamedPropertyIndex,
773 * Find a spot in the property chain for our newly created property.
777 renamedPropertyIndex,
781 * At this point the renamed property has been inserted in the tree,
782 * now, before Destroying the old property we must zero its dirProperty
783 * otherwise the DestroyProperty below will zap it all and we do not want
785 * Also, we fake that the old property is a storage so the DestroyProperty
786 * will not do a SetSize(0) on the stream data.
788 * This means that we need to tweak the StgProperty if it is a stream or a
791 StorageImpl_ReadProperty(This->ancestorStorage,
795 currentProperty.dirProperty = PROPERTY_NULL;
796 currentProperty.propertyType = PROPTYPE_STORAGE;
797 StorageImpl_WriteProperty(
798 This->ancestorStorage,
803 * Invoke Destroy to get rid of the ole property and automatically redo
804 * the linking of its previous and next members...
806 IStorage_DestroyElement(iface, pwcsOldName);
812 * There is no property with the old name
814 return STG_E_FILENOTFOUND;
820 /************************************************************************
821 * Storage32BaseImpl_CreateStream (IStorage)
823 * This method will create a stream object within this storage
825 * See Windows documentation for more details on IStorage methods.
827 static HRESULT WINAPI StorageBaseImpl_CreateStream(
829 const OLECHAR* pwcsName, /* [string][in] */
830 DWORD grfMode, /* [in] */
831 DWORD reserved1, /* [in] */
832 DWORD reserved2, /* [in] */
833 IStream** ppstm) /* [out] */
835 StorageBaseImpl *This = (StorageBaseImpl *)iface;
836 StgStreamImpl* newStream;
837 StgProperty currentProperty, newStreamProperty;
838 ULONG foundPropertyIndex, newPropertyIndex;
840 TRACE("(%p, %s, %x, %d, %d, %p)\n",
841 iface, debugstr_w(pwcsName), grfMode,
842 reserved1, reserved2, ppstm);
845 return STG_E_INVALIDPOINTER;
848 return STG_E_INVALIDNAME;
850 if (reserved1 || reserved2)
851 return STG_E_INVALIDPARAMETER;
853 if ( FAILED( validateSTGM(grfMode) ))
854 return STG_E_INVALIDFLAG;
856 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
857 return STG_E_INVALIDFLAG;
862 if ((grfMode & STGM_DELETEONRELEASE) ||
863 (grfMode & STGM_TRANSACTED))
864 return STG_E_INVALIDFUNCTION;
866 /* Can't create a stream on read-only storage */
867 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
868 return STG_E_ACCESSDENIED;
871 * Check that we're compatible with the parent's storage mode
872 * if not in transacted mode
874 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
875 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
876 return STG_E_ACCESSDENIED;
879 if(This->ancestorStorage->base.openFlags & STGM_SIMPLE)
880 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
884 foundPropertyIndex = findElement(This->ancestorStorage,
885 This->rootPropertySetIndex,
889 if (foundPropertyIndex != PROPERTY_NULL)
892 * An element with this name already exists
894 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
898 LIST_FOR_EACH_ENTRY(strm, &This->strmHead, StgStreamImpl, StrmListEntry)
900 if (strm->ownerProperty == foundPropertyIndex)
902 TRACE("Stream deleted %p\n", strm);
903 strm->parentStorage = NULL;
904 list_remove(&strm->StrmListEntry);
907 IStorage_DestroyElement(iface, pwcsName);
910 return STG_E_FILEALREADYEXISTS;
912 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
914 WARN("read-only storage\n");
915 return STG_E_ACCESSDENIED;
919 * memset the empty property
921 memset(&newStreamProperty, 0, sizeof(StgProperty));
923 newStreamProperty.sizeOfNameString =
924 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
926 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
927 return STG_E_INVALIDNAME;
929 strcpyW(newStreamProperty.name, pwcsName);
931 newStreamProperty.propertyType = PROPTYPE_STREAM;
932 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
933 newStreamProperty.size.u.LowPart = 0;
934 newStreamProperty.size.u.HighPart = 0;
936 newStreamProperty.leftChild = PROPERTY_NULL;
937 newStreamProperty.rightChild = PROPERTY_NULL;
938 newStreamProperty.dirProperty = PROPERTY_NULL;
940 /* call CoFileTime to get the current time
941 newStreamProperty.ctime
942 newStreamProperty.mtime
945 /* newStreamProperty.propertyUniqueID */
948 * Get a free property or create a new one
950 newPropertyIndex = getFreeProperty(This->ancestorStorage);
953 * Save the new property into the new property spot
955 StorageImpl_WriteProperty(
956 This->ancestorStorage,
961 * Find a spot in the property chain for our newly created property.
969 * Open the stream to return it.
971 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
975 *ppstm = (IStream*)newStream;
977 IStream_AddRef(*ppstm);
981 return STG_E_INSUFFICIENTMEMORY;
987 /************************************************************************
988 * Storage32BaseImpl_SetClass (IStorage)
990 * This method will write the specified CLSID in the property of this
993 * See Windows documentation for more details on IStorage methods.
995 static HRESULT WINAPI StorageBaseImpl_SetClass(
997 REFCLSID clsid) /* [in] */
999 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1000 HRESULT hRes = E_FAIL;
1001 StgProperty curProperty;
1004 TRACE("(%p, %p)\n", iface, clsid);
1006 success = StorageImpl_ReadProperty(This->ancestorStorage,
1007 This->rootPropertySetIndex,
1011 curProperty.propertyUniqueID = *clsid;
1013 success = StorageImpl_WriteProperty(This->ancestorStorage,
1014 This->rootPropertySetIndex,
1023 /************************************************************************
1024 ** Storage32Impl implementation
1027 /************************************************************************
1028 * Storage32Impl_CreateStorage (IStorage)
1030 * This method will create the storage object within the provided storage.
1032 * See Windows documentation for more details on IStorage methods.
1034 static HRESULT WINAPI StorageImpl_CreateStorage(
1036 const OLECHAR *pwcsName, /* [string][in] */
1037 DWORD grfMode, /* [in] */
1038 DWORD reserved1, /* [in] */
1039 DWORD reserved2, /* [in] */
1040 IStorage **ppstg) /* [out] */
1042 StorageImpl* const This=(StorageImpl*)iface;
1044 StgProperty currentProperty;
1045 StgProperty newProperty;
1046 ULONG foundPropertyIndex;
1047 ULONG newPropertyIndex;
1050 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1051 iface, debugstr_w(pwcsName), grfMode,
1052 reserved1, reserved2, ppstg);
1055 return STG_E_INVALIDPOINTER;
1058 return STG_E_INVALIDNAME;
1062 if ( FAILED( validateSTGM(grfMode) ) ||
1063 (grfMode & STGM_DELETEONRELEASE) )
1065 WARN("bad grfMode: 0x%x\n", grfMode);
1066 return STG_E_INVALIDFLAG;
1070 * Check that we're compatible with the parent's storage mode
1072 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->base.openFlags ) )
1074 WARN("access denied\n");
1075 return STG_E_ACCESSDENIED;
1078 foundPropertyIndex = findElement(This->base.ancestorStorage,
1079 This->base.rootPropertySetIndex,
1083 if (foundPropertyIndex != PROPERTY_NULL)
1086 * An element with this name already exists
1088 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1089 STGM_ACCESS_MODE(This->base.openFlags) != STGM_READ)
1091 hr = IStorage_DestroyElement(iface, pwcsName);
1097 WARN("file already exists\n");
1098 return STG_E_FILEALREADYEXISTS;
1101 else if (STGM_ACCESS_MODE(This->base.openFlags) == STGM_READ)
1103 WARN("read-only storage\n");
1104 return STG_E_ACCESSDENIED;
1108 * memset the empty property
1110 memset(&newProperty, 0, sizeof(StgProperty));
1112 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1114 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1116 FIXME("name too long\n");
1117 return STG_E_INVALIDNAME;
1120 strcpyW(newProperty.name, pwcsName);
1122 newProperty.propertyType = PROPTYPE_STORAGE;
1123 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1124 newProperty.size.u.LowPart = 0;
1125 newProperty.size.u.HighPart = 0;
1127 newProperty.leftChild = PROPERTY_NULL;
1128 newProperty.rightChild = PROPERTY_NULL;
1129 newProperty.dirProperty = PROPERTY_NULL;
1131 /* call CoFileTime to get the current time
1136 /* newStorageProperty.propertyUniqueID */
1139 * Obtain a free property in the property chain
1141 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1144 * Save the new property into the new property spot
1146 StorageImpl_WriteProperty(
1147 This->base.ancestorStorage,
1152 * Find a spot in the property chain for our newly created property.
1154 updatePropertyChain(
1160 * Open it to get a pointer to return.
1162 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1164 if( (hr != S_OK) || (*ppstg == NULL))
1174 /***************************************************************************
1178 * Get a free property or create a new one.
1180 static ULONG getFreeProperty(
1181 StorageImpl *storage)
1183 ULONG currentPropertyIndex = 0;
1184 ULONG newPropertyIndex = PROPERTY_NULL;
1185 BOOL readSuccessful = TRUE;
1186 StgProperty currentProperty;
1191 * Start by reading the root property
1193 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1194 currentPropertyIndex,
1198 if (currentProperty.sizeOfNameString == 0)
1201 * The property existis and is available, we found it.
1203 newPropertyIndex = currentPropertyIndex;
1209 * We exhausted the property list, we will create more space below
1211 newPropertyIndex = currentPropertyIndex;
1213 currentPropertyIndex++;
1215 } while (newPropertyIndex == PROPERTY_NULL);
1218 * grow the property chain
1220 if (! readSuccessful)
1222 StgProperty emptyProperty;
1223 ULARGE_INTEGER newSize;
1224 ULONG propertyIndex;
1225 ULONG lastProperty = 0;
1226 ULONG blockCount = 0;
1229 * obtain the new count of property blocks
1231 blockCount = BlockChainStream_GetCount(
1232 storage->base.ancestorStorage->rootBlockChain)+1;
1235 * initialize the size used by the property stream
1237 newSize.u.HighPart = 0;
1238 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1241 * add a property block to the property chain
1243 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1246 * memset the empty property in order to initialize the unused newly
1249 memset(&emptyProperty, 0, sizeof(StgProperty));
1254 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1257 propertyIndex = newPropertyIndex;
1258 propertyIndex < lastProperty;
1261 StorageImpl_WriteProperty(
1262 storage->base.ancestorStorage,
1268 return newPropertyIndex;
1271 /****************************************************************************
1275 * Case insensitive comparison of StgProperty.name by first considering
1278 * Returns <0 when newProperty < currentProperty
1279 * >0 when newProperty > currentProperty
1280 * 0 when newProperty == currentProperty
1282 static LONG propertyNameCmp(
1283 const OLECHAR *newProperty,
1284 const OLECHAR *currentProperty)
1286 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1291 * We compare the string themselves only when they are of the same length
1293 diff = lstrcmpiW( newProperty, currentProperty);
1299 /****************************************************************************
1303 * Properly link this new element in the property chain.
1305 static void updatePropertyChain(
1306 StorageImpl *storage,
1307 ULONG newPropertyIndex,
1308 StgProperty newProperty)
1310 StgProperty currentProperty;
1313 * Read the root property
1315 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1316 storage->base.rootPropertySetIndex,
1319 if (currentProperty.dirProperty != PROPERTY_NULL)
1322 * The root storage contains some element, therefore, start the research
1323 * for the appropriate location.
1326 ULONG current, next, previous, currentPropertyId;
1329 * Keep the StgProperty sequence number of the storage first property
1331 currentPropertyId = currentProperty.dirProperty;
1336 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1337 currentProperty.dirProperty,
1340 previous = currentProperty.leftChild;
1341 next = currentProperty.rightChild;
1342 current = currentPropertyId;
1346 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1350 if (previous != PROPERTY_NULL)
1352 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1359 currentProperty.leftChild = newPropertyIndex;
1360 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1368 if (next != PROPERTY_NULL)
1370 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1377 currentProperty.rightChild = newPropertyIndex;
1378 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1387 * Trying to insert an item with the same name in the
1388 * subtree structure.
1393 previous = currentProperty.leftChild;
1394 next = currentProperty.rightChild;
1400 * The root storage is empty, link the new property to its dir property
1402 currentProperty.dirProperty = newPropertyIndex;
1403 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1404 storage->base.rootPropertySetIndex,
1409 /****************************************************************************
1413 * Find and read the element of a storage with the given name.
1415 static ULONG findElement(StorageImpl *storage, ULONG storageEntry,
1416 const OLECHAR *name, StgProperty *data)
1420 /* Read the storage entry to find the root of the tree. */
1421 StorageImpl_ReadProperty(storage, storageEntry, data);
1423 currentEntry = data->dirProperty;
1425 while (currentEntry != PROPERTY_NULL)
1429 StorageImpl_ReadProperty(storage, currentEntry, data);
1431 cmp = propertyNameCmp(name, data->name);
1438 currentEntry = data->leftChild;
1441 currentEntry = data->rightChild;
1444 return currentEntry;
1448 /*************************************************************************
1451 static HRESULT WINAPI StorageImpl_CopyTo(
1453 DWORD ciidExclude, /* [in] */
1454 const IID* rgiidExclude, /* [size_is][unique][in] */
1455 SNB snbExclude, /* [unique][in] */
1456 IStorage* pstgDest) /* [unique][in] */
1458 IEnumSTATSTG *elements = 0;
1459 STATSTG curElement, strStat;
1461 IStorage *pstgTmp, *pstgChild;
1462 IStream *pstrTmp, *pstrChild;
1463 BOOL skip = FALSE, skip_storage = FALSE, skip_stream = FALSE;
1466 TRACE("(%p, %d, %p, %p, %p)\n",
1467 iface, ciidExclude, rgiidExclude,
1468 snbExclude, pstgDest);
1470 if ( pstgDest == 0 )
1471 return STG_E_INVALIDPOINTER;
1474 * Enumerate the elements
1476 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1484 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1485 IStorage_SetClass( pstgDest, &curElement.clsid );
1487 for(i = 0; i < ciidExclude; ++i)
1489 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1490 skip_storage = TRUE;
1491 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1494 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1500 * Obtain the next element
1502 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1504 if ( hr == S_FALSE )
1506 hr = S_OK; /* done, every element has been copied */
1512 WCHAR **snb = snbExclude;
1514 while ( *snb != NULL && !skip )
1516 if ( lstrcmpW(curElement.pwcsName, *snb) == 0 )
1525 if (curElement.type == STGTY_STORAGE)
1531 * open child source storage
1533 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1534 STGM_READ|STGM_SHARE_EXCLUSIVE,
1535 NULL, 0, &pstgChild );
1541 * Check if destination storage is not a child of the source
1542 * storage, which will cause an infinite loop
1544 if (pstgChild == pstgDest)
1546 IEnumSTATSTG_Release(elements);
1548 return STG_E_ACCESSDENIED;
1552 * create a new storage in destination storage
1554 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1555 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1559 * if it already exist, don't create a new one use this one
1561 if (hr == STG_E_FILEALREADYEXISTS)
1563 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1564 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1565 NULL, 0, &pstgTmp );
1573 * do the copy recursively
1575 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1578 IStorage_Release( pstgTmp );
1579 IStorage_Release( pstgChild );
1581 else if (curElement.type == STGTY_STREAM)
1587 * create a new stream in destination storage. If the stream already
1588 * exist, it will be deleted and a new one will be created.
1590 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1591 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1598 * open child stream storage
1600 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1601 STGM_READ|STGM_SHARE_EXCLUSIVE,
1608 * Get the size of the source stream
1610 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1613 * Set the size of the destination stream.
1615 IStream_SetSize(pstrTmp, strStat.cbSize);
1620 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1623 IStream_Release( pstrTmp );
1624 IStream_Release( pstrChild );
1628 WARN("unknown element type: %d\n", curElement.type);
1631 } while (hr == S_OK);
1636 IEnumSTATSTG_Release(elements);
1641 /*************************************************************************
1642 * MoveElementTo (IStorage)
1644 static HRESULT WINAPI StorageImpl_MoveElementTo(
1646 const OLECHAR *pwcsName, /* [string][in] */
1647 IStorage *pstgDest, /* [unique][in] */
1648 const OLECHAR *pwcsNewName,/* [string][in] */
1649 DWORD grfFlags) /* [in] */
1651 FIXME("(%p %s %p %s %u): stub\n", iface,
1652 debugstr_w(pwcsName), pstgDest,
1653 debugstr_w(pwcsNewName), grfFlags);
1657 /*************************************************************************
1660 * Ensures that any changes made to a storage object open in transacted mode
1661 * are reflected in the parent storage
1664 * Wine doesn't implement transacted mode, which seems to be a basic
1665 * optimization, so we can ignore this stub for now.
1667 static HRESULT WINAPI StorageImpl_Commit(
1669 DWORD grfCommitFlags)/* [in] */
1671 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1675 /*************************************************************************
1678 * Discard all changes that have been made since the last commit operation
1680 static HRESULT WINAPI StorageImpl_Revert(
1683 FIXME("(%p): stub\n", iface);
1687 /*************************************************************************
1688 * DestroyElement (IStorage)
1690 * Strategy: This implementation is built this way for simplicity not for speed.
1691 * I always delete the topmost element of the enumeration and adjust
1692 * the deleted element pointer all the time. This takes longer to
1693 * do but allow to reinvoke DestroyElement whenever we encounter a
1694 * storage object. The optimisation resides in the usage of another
1695 * enumeration strategy that would give all the leaves of a storage
1696 * first. (postfix order)
1698 static HRESULT WINAPI StorageImpl_DestroyElement(
1700 const OLECHAR *pwcsName)/* [string][in] */
1702 StorageImpl* const This=(StorageImpl*)iface;
1706 StgProperty propertyToDelete;
1707 StgProperty parentProperty;
1708 ULONG foundPropertyIndexToDelete;
1709 ULONG typeOfRelation;
1710 ULONG parentPropertyId = 0;
1713 iface, debugstr_w(pwcsName));
1716 return STG_E_INVALIDPOINTER;
1718 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
1719 return STG_E_ACCESSDENIED;
1721 foundPropertyIndexToDelete = findElement(
1722 This->base.ancestorStorage,
1723 This->base.rootPropertySetIndex,
1727 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1729 return STG_E_FILENOTFOUND;
1733 * Find the parent property of the property to delete (the one that
1734 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1735 * the parent is This. Otherwise, the parent is one of its sibling...
1739 * First, read This's StgProperty..
1741 res = StorageImpl_ReadProperty(
1742 This->base.ancestorStorage,
1743 This->base.rootPropertySetIndex,
1749 * Second, check to see if by any chance the actual storage (This) is not
1750 * the parent of the property to delete... We never know...
1752 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1755 * Set data as it would have been done in the else part...
1757 typeOfRelation = PROPERTY_RELATION_DIR;
1758 parentPropertyId = This->base.rootPropertySetIndex;
1763 * Create a property enumeration to search the parent properties, and
1764 * delete it once done.
1766 IEnumSTATSTGImpl* propertyEnumeration2;
1768 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1769 This->base.ancestorStorage,
1770 This->base.rootPropertySetIndex);
1772 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1773 propertyEnumeration2,
1774 foundPropertyIndexToDelete,
1778 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1781 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1783 hr = deleteStorageProperty(
1785 foundPropertyIndexToDelete,
1788 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1790 hr = deleteStreamProperty(
1792 foundPropertyIndexToDelete,
1800 * Adjust the property chain
1802 hr = adjustPropertyChain(
1813 /************************************************************************
1814 * StorageImpl_Stat (IStorage)
1816 * This method will retrieve information about this storage object.
1818 * See Windows documentation for more details on IStorage methods.
1820 static HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1821 STATSTG* pstatstg, /* [out] */
1822 DWORD grfStatFlag) /* [in] */
1824 StorageImpl* const This = (StorageImpl*)iface;
1825 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1827 if ( SUCCEEDED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1829 CoTaskMemFree(pstatstg->pwcsName);
1830 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1831 strcpyW(pstatstg->pwcsName, This->pwcsName);
1837 /******************************************************************************
1838 * Internal stream list handlers
1841 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1843 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1844 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1847 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1849 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1850 list_remove(&(strm->StrmListEntry));
1853 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1855 struct list *cur, *cur2;
1856 StgStreamImpl *strm=NULL;
1858 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1859 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1860 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1861 strm->parentStorage = NULL;
1867 /*********************************************************************
1871 * Perform the deletion of a complete storage node
1874 static HRESULT deleteStorageProperty(
1875 StorageImpl *parentStorage,
1876 ULONG indexOfPropertyToDelete,
1877 StgProperty propertyToDelete)
1879 IEnumSTATSTG *elements = 0;
1880 IStorage *childStorage = 0;
1881 STATSTG currentElement;
1883 HRESULT destroyHr = S_OK;
1886 * Open the storage and enumerate it
1888 hr = StorageBaseImpl_OpenStorage(
1889 (IStorage*)parentStorage,
1890 propertyToDelete.name,
1892 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1903 * Enumerate the elements
1905 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1910 * Obtain the next element
1912 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
1915 destroyHr = StorageImpl_DestroyElement(childStorage, currentElement.pwcsName);
1917 CoTaskMemFree(currentElement.pwcsName);
1921 * We need to Reset the enumeration every time because we delete elements
1922 * and the enumeration could be invalid
1924 IEnumSTATSTG_Reset(elements);
1926 } while ((hr == S_OK) && (destroyHr == S_OK));
1929 * Invalidate the property by zeroing its name member.
1931 propertyToDelete.sizeOfNameString = 0;
1933 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
1934 indexOfPropertyToDelete,
1937 IStorage_Release(childStorage);
1938 IEnumSTATSTG_Release(elements);
1943 /*********************************************************************
1947 * Perform the deletion of a stream node
1950 static HRESULT deleteStreamProperty(
1951 StorageImpl *parentStorage,
1952 ULONG indexOfPropertyToDelete,
1953 StgProperty propertyToDelete)
1957 ULARGE_INTEGER size;
1959 size.u.HighPart = 0;
1962 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
1963 propertyToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
1973 hr = IStream_SetSize(pis, size);
1981 * Release the stream object.
1983 IStream_Release(pis);
1986 * Invalidate the property by zeroing its name member.
1988 propertyToDelete.sizeOfNameString = 0;
1991 * Here we should re-read the property so we get the updated pointer
1992 * but since we are here to zap it, I don't do it...
1994 StorageImpl_WriteProperty(
1995 parentStorage->base.ancestorStorage,
1996 indexOfPropertyToDelete,
2002 /*********************************************************************
2006 * Finds a placeholder for the StgProperty within the Storage
2009 static HRESULT findPlaceholder(
2010 StorageImpl *storage,
2011 ULONG propertyIndexToStore,
2012 ULONG storePropertyIndex,
2015 StgProperty storeProperty;
2019 * Read the storage property
2021 res = StorageImpl_ReadProperty(
2022 storage->base.ancestorStorage,
2031 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2033 if (storeProperty.leftChild != PROPERTY_NULL)
2035 return findPlaceholder(
2037 propertyIndexToStore,
2038 storeProperty.leftChild,
2043 storeProperty.leftChild = propertyIndexToStore;
2046 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2048 if (storeProperty.rightChild != PROPERTY_NULL)
2050 return findPlaceholder(
2052 propertyIndexToStore,
2053 storeProperty.rightChild,
2058 storeProperty.rightChild = propertyIndexToStore;
2061 else if (typeOfRelation == PROPERTY_RELATION_DIR)
2063 if (storeProperty.dirProperty != PROPERTY_NULL)
2065 return findPlaceholder(
2067 propertyIndexToStore,
2068 storeProperty.dirProperty,
2073 storeProperty.dirProperty = propertyIndexToStore;
2077 res = StorageImpl_WriteProperty(
2078 storage->base.ancestorStorage,
2090 /*************************************************************************
2094 * This method takes the previous and the next property link of a property
2095 * to be deleted and find them a place in the Storage.
2097 static HRESULT adjustPropertyChain(
2099 StgProperty propertyToDelete,
2100 StgProperty parentProperty,
2101 ULONG parentPropertyId,
2104 ULONG newLinkProperty = PROPERTY_NULL;
2105 BOOL needToFindAPlaceholder = FALSE;
2106 ULONG storeNode = PROPERTY_NULL;
2107 ULONG toStoreNode = PROPERTY_NULL;
2108 INT relationType = 0;
2112 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2114 if (propertyToDelete.leftChild != PROPERTY_NULL)
2117 * Set the parent previous to the property to delete previous
2119 newLinkProperty = propertyToDelete.leftChild;
2121 if (propertyToDelete.rightChild != PROPERTY_NULL)
2124 * We also need to find a storage for the other link, setup variables
2125 * to do this at the end...
2127 needToFindAPlaceholder = TRUE;
2128 storeNode = propertyToDelete.leftChild;
2129 toStoreNode = propertyToDelete.rightChild;
2130 relationType = PROPERTY_RELATION_NEXT;
2133 else if (propertyToDelete.rightChild != PROPERTY_NULL)
2136 * Set the parent previous to the property to delete next
2138 newLinkProperty = propertyToDelete.rightChild;
2142 * Link it for real...
2144 parentProperty.leftChild = newLinkProperty;
2147 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2149 if (propertyToDelete.leftChild != PROPERTY_NULL)
2152 * Set the parent next to the property to delete next previous
2154 newLinkProperty = propertyToDelete.leftChild;
2156 if (propertyToDelete.rightChild != PROPERTY_NULL)
2159 * We also need to find a storage for the other link, setup variables
2160 * to do this at the end...
2162 needToFindAPlaceholder = TRUE;
2163 storeNode = propertyToDelete.leftChild;
2164 toStoreNode = propertyToDelete.rightChild;
2165 relationType = PROPERTY_RELATION_NEXT;
2168 else if (propertyToDelete.rightChild != PROPERTY_NULL)
2171 * Set the parent next to the property to delete next
2173 newLinkProperty = propertyToDelete.rightChild;
2177 * Link it for real...
2179 parentProperty.rightChild = newLinkProperty;
2181 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2183 if (propertyToDelete.leftChild != PROPERTY_NULL)
2186 * Set the parent dir to the property to delete previous
2188 newLinkProperty = propertyToDelete.leftChild;
2190 if (propertyToDelete.rightChild != PROPERTY_NULL)
2193 * We also need to find a storage for the other link, setup variables
2194 * to do this at the end...
2196 needToFindAPlaceholder = TRUE;
2197 storeNode = propertyToDelete.leftChild;
2198 toStoreNode = propertyToDelete.rightChild;
2199 relationType = PROPERTY_RELATION_NEXT;
2202 else if (propertyToDelete.rightChild != PROPERTY_NULL)
2205 * Set the parent dir to the property to delete next
2207 newLinkProperty = propertyToDelete.rightChild;
2211 * Link it for real...
2213 parentProperty.dirProperty = newLinkProperty;
2217 * Write back the parent property
2219 res = StorageImpl_WriteProperty(
2220 This->base.ancestorStorage,
2229 * If a placeholder is required for the other link, then, find one and
2230 * get out of here...
2232 if (needToFindAPlaceholder)
2234 hr = findPlaceholder(
2245 /******************************************************************************
2246 * SetElementTimes (IStorage)
2248 static HRESULT WINAPI StorageImpl_SetElementTimes(
2250 const OLECHAR *pwcsName,/* [string][in] */
2251 const FILETIME *pctime, /* [in] */
2252 const FILETIME *patime, /* [in] */
2253 const FILETIME *pmtime) /* [in] */
2255 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2259 /******************************************************************************
2260 * SetStateBits (IStorage)
2262 static HRESULT WINAPI StorageImpl_SetStateBits(
2264 DWORD grfStateBits,/* [in] */
2265 DWORD grfMask) /* [in] */
2267 StorageImpl* const This = (StorageImpl*)iface;
2268 This->base.stateBits = (This->base.stateBits & ~grfMask) | (grfStateBits & grfMask);
2273 * Virtual function table for the IStorage32Impl class.
2275 static const IStorageVtbl Storage32Impl_Vtbl =
2277 StorageBaseImpl_QueryInterface,
2278 StorageBaseImpl_AddRef,
2279 StorageBaseImpl_Release,
2280 StorageBaseImpl_CreateStream,
2281 StorageBaseImpl_OpenStream,
2282 StorageImpl_CreateStorage,
2283 StorageBaseImpl_OpenStorage,
2285 StorageImpl_MoveElementTo,
2288 StorageBaseImpl_EnumElements,
2289 StorageImpl_DestroyElement,
2290 StorageBaseImpl_RenameElement,
2291 StorageImpl_SetElementTimes,
2292 StorageBaseImpl_SetClass,
2293 StorageImpl_SetStateBits,
2297 static HRESULT StorageImpl_Construct(
2307 StgProperty currentProperty;
2308 BOOL readSuccessful;
2309 ULONG currentPropertyIndex;
2311 if ( FAILED( validateSTGM(openFlags) ))
2312 return STG_E_INVALIDFLAG;
2314 memset(This, 0, sizeof(StorageImpl));
2316 list_init(&This->base.strmHead);
2318 This->base.lpVtbl = &Storage32Impl_Vtbl;
2319 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2320 This->base.v_destructor = StorageImpl_Destroy;
2321 This->base.openFlags = (openFlags & ~STGM_CREATE);
2322 This->create = create;
2325 * This is the top-level storage so initialize the ancestor pointer
2328 This->base.ancestorStorage = This;
2330 This->hFile = hFile;
2333 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2334 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2335 if (!This->pwcsName)
2336 return STG_E_INSUFFICIENTMEMORY;
2337 strcpyW(This->pwcsName, pwcsName);
2341 * Initialize the big block cache.
2343 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2344 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2345 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2351 if (This->bigBlockFile == 0)
2356 ULARGE_INTEGER size;
2357 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2360 * Initialize all header variables:
2361 * - The big block depot consists of one block and it is at block 0
2362 * - The properties start at block 1
2363 * - There is no small block depot
2365 memset( This->bigBlockDepotStart,
2367 sizeof(This->bigBlockDepotStart));
2369 This->bigBlockDepotCount = 1;
2370 This->bigBlockDepotStart[0] = 0;
2371 This->rootStartBlock = 1;
2372 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2373 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2374 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2375 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2376 This->extBigBlockDepotCount = 0;
2378 StorageImpl_SaveFileHeader(This);
2381 * Add one block for the big block depot and one block for the properties
2383 size.u.HighPart = 0;
2384 size.u.LowPart = This->bigBlockSize * 3;
2385 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2388 * Initialize the big block depot
2390 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2391 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2392 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2393 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2398 * Load the header for the file.
2400 hr = StorageImpl_LoadFileHeader(This);
2404 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2411 * There is no block depot cached yet.
2413 This->indexBlockDepotCached = 0xFFFFFFFF;
2416 * Start searching for free blocks with block 0.
2418 This->prevFreeBlock = 0;
2421 * Create the block chain abstractions.
2423 if(!(This->rootBlockChain =
2424 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2425 return STG_E_READFAULT;
2427 if(!(This->smallBlockDepotChain =
2428 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2430 return STG_E_READFAULT;
2433 * Write the root property (memory only)
2437 StgProperty rootProp;
2439 * Initialize the property chain
2441 memset(&rootProp, 0, sizeof(rootProp));
2442 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2443 sizeof(rootProp.name)/sizeof(WCHAR) );
2444 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2445 rootProp.propertyType = PROPTYPE_ROOT;
2446 rootProp.leftChild = PROPERTY_NULL;
2447 rootProp.rightChild = PROPERTY_NULL;
2448 rootProp.dirProperty = PROPERTY_NULL;
2449 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2450 rootProp.size.u.HighPart = 0;
2451 rootProp.size.u.LowPart = 0;
2453 StorageImpl_WriteProperty(This, 0, &rootProp);
2457 * Find the ID of the root in the property sets.
2459 currentPropertyIndex = 0;
2463 readSuccessful = StorageImpl_ReadProperty(
2465 currentPropertyIndex,
2470 if ( (currentProperty.sizeOfNameString != 0 ) &&
2471 (currentProperty.propertyType == PROPTYPE_ROOT) )
2473 This->base.rootPropertySetIndex = currentPropertyIndex;
2477 currentPropertyIndex++;
2479 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2481 if (!readSuccessful)
2484 return STG_E_READFAULT;
2488 * Create the block chain abstraction for the small block root chain.
2490 if(!(This->smallBlockRootChain =
2491 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2492 return STG_E_READFAULT;
2497 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2499 StorageImpl *This = (StorageImpl*) iface;
2500 TRACE("(%p)\n", This);
2502 StorageBaseImpl_DeleteAll(&This->base);
2504 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2506 BlockChainStream_Destroy(This->smallBlockRootChain);
2507 BlockChainStream_Destroy(This->rootBlockChain);
2508 BlockChainStream_Destroy(This->smallBlockDepotChain);
2510 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2511 HeapFree(GetProcessHeap(), 0, This);
2514 /******************************************************************************
2515 * Storage32Impl_GetNextFreeBigBlock
2517 * Returns the index of the next free big block.
2518 * If the big block depot is filled, this method will enlarge it.
2521 static ULONG StorageImpl_GetNextFreeBigBlock(
2524 ULONG depotBlockIndexPos;
2525 BYTE depotBuffer[BIG_BLOCK_SIZE];
2527 ULONG depotBlockOffset;
2528 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2529 ULONG nextBlockIndex = BLOCK_SPECIAL;
2531 ULONG freeBlock = BLOCK_UNUSED;
2533 depotIndex = This->prevFreeBlock / blocksPerDepot;
2534 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2537 * Scan the entire big block depot until we find a block marked free
2539 while (nextBlockIndex != BLOCK_UNUSED)
2541 if (depotIndex < COUNT_BBDEPOTINHEADER)
2543 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2546 * Grow the primary depot.
2548 if (depotBlockIndexPos == BLOCK_UNUSED)
2550 depotBlockIndexPos = depotIndex*blocksPerDepot;
2553 * Add a block depot.
2555 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2556 This->bigBlockDepotCount++;
2557 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2560 * Flag it as a block depot.
2562 StorageImpl_SetNextBlockInChain(This,
2566 /* Save new header information.
2568 StorageImpl_SaveFileHeader(This);
2573 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2575 if (depotBlockIndexPos == BLOCK_UNUSED)
2578 * Grow the extended depot.
2580 ULONG extIndex = BLOCK_UNUSED;
2581 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2582 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2584 if (extBlockOffset == 0)
2586 /* We need an extended block.
2588 extIndex = Storage32Impl_AddExtBlockDepot(This);
2589 This->extBigBlockDepotCount++;
2590 depotBlockIndexPos = extIndex + 1;
2593 depotBlockIndexPos = depotIndex * blocksPerDepot;
2596 * Add a block depot and mark it in the extended block.
2598 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2599 This->bigBlockDepotCount++;
2600 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2602 /* Flag the block depot.
2604 StorageImpl_SetNextBlockInChain(This,
2608 /* If necessary, flag the extended depot block.
2610 if (extIndex != BLOCK_UNUSED)
2611 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2613 /* Save header information.
2615 StorageImpl_SaveFileHeader(This);
2619 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2623 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2624 ( nextBlockIndex != BLOCK_UNUSED))
2626 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2628 if (nextBlockIndex == BLOCK_UNUSED)
2630 freeBlock = (depotIndex * blocksPerDepot) +
2631 (depotBlockOffset/sizeof(ULONG));
2634 depotBlockOffset += sizeof(ULONG);
2639 depotBlockOffset = 0;
2643 * make sure that the block physically exists before using it
2645 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2647 This->prevFreeBlock = freeBlock;
2652 /******************************************************************************
2653 * Storage32Impl_AddBlockDepot
2655 * This will create a depot block, essentially it is a block initialized
2658 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2660 BYTE blockBuffer[BIG_BLOCK_SIZE];
2663 * Initialize blocks as free
2665 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2666 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2669 /******************************************************************************
2670 * Storage32Impl_GetExtDepotBlock
2672 * Returns the index of the block that corresponds to the specified depot
2673 * index. This method is only for depot indexes equal or greater than
2674 * COUNT_BBDEPOTINHEADER.
2676 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2678 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2679 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2680 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2681 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2682 ULONG blockIndex = BLOCK_UNUSED;
2683 ULONG extBlockIndex = This->extBigBlockDepotStart;
2685 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2687 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2688 return BLOCK_UNUSED;
2690 while (extBlockCount > 0)
2692 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2696 if (extBlockIndex != BLOCK_UNUSED)
2697 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
2698 extBlockOffset * sizeof(ULONG), &blockIndex);
2703 /******************************************************************************
2704 * Storage32Impl_SetExtDepotBlock
2706 * Associates the specified block index to the specified depot index.
2707 * This method is only for depot indexes equal or greater than
2708 * COUNT_BBDEPOTINHEADER.
2710 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2712 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2713 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2714 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2715 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2716 ULONG extBlockIndex = This->extBigBlockDepotStart;
2718 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2720 while (extBlockCount > 0)
2722 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2726 if (extBlockIndex != BLOCK_UNUSED)
2728 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
2729 extBlockOffset * sizeof(ULONG),
2734 /******************************************************************************
2735 * Storage32Impl_AddExtBlockDepot
2737 * Creates an extended depot block.
2739 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2741 ULONG numExtBlocks = This->extBigBlockDepotCount;
2742 ULONG nextExtBlock = This->extBigBlockDepotStart;
2743 BYTE depotBuffer[BIG_BLOCK_SIZE];
2744 ULONG index = BLOCK_UNUSED;
2745 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2746 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2747 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2749 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2750 blocksPerDepotBlock;
2752 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2755 * The first extended block.
2757 This->extBigBlockDepotStart = index;
2763 * Follow the chain to the last one.
2765 for (i = 0; i < (numExtBlocks - 1); i++)
2767 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2771 * Add the new extended block to the chain.
2773 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
2778 * Initialize this block.
2780 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2781 StorageImpl_WriteBigBlock(This, index, depotBuffer);
2786 /******************************************************************************
2787 * Storage32Impl_FreeBigBlock
2789 * This method will flag the specified block as free in the big block depot.
2791 static void StorageImpl_FreeBigBlock(
2795 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2797 if (blockIndex < This->prevFreeBlock)
2798 This->prevFreeBlock = blockIndex;
2801 /************************************************************************
2802 * Storage32Impl_GetNextBlockInChain
2804 * This method will retrieve the block index of the next big block in
2807 * Params: This - Pointer to the Storage object.
2808 * blockIndex - Index of the block to retrieve the chain
2810 * nextBlockIndex - receives the return value.
2812 * Returns: This method returns the index of the next block in the chain.
2813 * It will return the constants:
2814 * BLOCK_SPECIAL - If the block given was not part of a
2816 * BLOCK_END_OF_CHAIN - If the block given was the last in
2818 * BLOCK_UNUSED - If the block given was not past of a chain
2820 * BLOCK_EXTBBDEPOT - This block is part of the extended
2823 * See Windows documentation for more details on IStorage methods.
2825 static HRESULT StorageImpl_GetNextBlockInChain(
2828 ULONG* nextBlockIndex)
2830 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2831 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2832 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2833 BYTE depotBuffer[BIG_BLOCK_SIZE];
2835 ULONG depotBlockIndexPos;
2838 *nextBlockIndex = BLOCK_SPECIAL;
2840 if(depotBlockCount >= This->bigBlockDepotCount)
2842 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
2843 This->bigBlockDepotCount);
2844 return STG_E_READFAULT;
2848 * Cache the currently accessed depot block.
2850 if (depotBlockCount != This->indexBlockDepotCached)
2852 This->indexBlockDepotCached = depotBlockCount;
2854 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2856 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2861 * We have to look in the extended depot.
2863 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2866 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2869 return STG_E_READFAULT;
2871 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2873 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2874 This->blockDepotCached[index] = *nextBlockIndex;
2878 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2883 /******************************************************************************
2884 * Storage32Impl_GetNextExtendedBlock
2886 * Given an extended block this method will return the next extended block.
2889 * The last ULONG of an extended block is the block index of the next
2890 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2894 * - The index of the next extended block
2895 * - BLOCK_UNUSED: there is no next extended block.
2896 * - Any other return values denotes failure.
2898 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2900 ULONG nextBlockIndex = BLOCK_SPECIAL;
2901 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2903 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
2906 return nextBlockIndex;
2909 /******************************************************************************
2910 * Storage32Impl_SetNextBlockInChain
2912 * This method will write the index of the specified block's next block
2913 * in the big block depot.
2915 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2918 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2919 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2920 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2923 static void StorageImpl_SetNextBlockInChain(
2928 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2929 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2930 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2931 ULONG depotBlockIndexPos;
2933 assert(depotBlockCount < This->bigBlockDepotCount);
2934 assert(blockIndex != nextBlock);
2936 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2938 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2943 * We have to look in the extended depot.
2945 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2948 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
2951 * Update the cached block depot, if necessary.
2953 if (depotBlockCount == This->indexBlockDepotCached)
2955 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2959 /******************************************************************************
2960 * Storage32Impl_LoadFileHeader
2962 * This method will read in the file header, i.e. big block index -1.
2964 static HRESULT StorageImpl_LoadFileHeader(
2967 HRESULT hr = STG_E_FILENOTFOUND;
2968 BYTE headerBigBlock[BIG_BLOCK_SIZE];
2974 * Get a pointer to the big block of data containing the header.
2976 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
2979 * Extract the information from the header.
2984 * Check for the "magic number" signature and return an error if it is not
2987 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2989 return STG_E_OLDFORMAT;
2992 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2994 return STG_E_INVALIDHEADER;
2997 StorageUtl_ReadWord(
2999 OFFSET_BIGBLOCKSIZEBITS,
3000 &This->bigBlockSizeBits);
3002 StorageUtl_ReadWord(
3004 OFFSET_SMALLBLOCKSIZEBITS,
3005 &This->smallBlockSizeBits);
3007 StorageUtl_ReadDWord(
3009 OFFSET_BBDEPOTCOUNT,
3010 &This->bigBlockDepotCount);
3012 StorageUtl_ReadDWord(
3014 OFFSET_ROOTSTARTBLOCK,
3015 &This->rootStartBlock);
3017 StorageUtl_ReadDWord(
3019 OFFSET_SBDEPOTSTART,
3020 &This->smallBlockDepotStart);
3022 StorageUtl_ReadDWord(
3024 OFFSET_EXTBBDEPOTSTART,
3025 &This->extBigBlockDepotStart);
3027 StorageUtl_ReadDWord(
3029 OFFSET_EXTBBDEPOTCOUNT,
3030 &This->extBigBlockDepotCount);
3032 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3034 StorageUtl_ReadDWord(
3036 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3037 &(This->bigBlockDepotStart[index]));
3041 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3043 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3044 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3047 * Right now, the code is making some assumptions about the size of the
3048 * blocks, just make sure they are what we're expecting.
3050 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3051 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3053 WARN("Broken OLE storage file\n");
3054 hr = STG_E_INVALIDHEADER;
3063 /******************************************************************************
3064 * Storage32Impl_SaveFileHeader
3066 * This method will save to the file the header, i.e. big block -1.
3068 static void StorageImpl_SaveFileHeader(
3071 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3076 * Get a pointer to the big block of data containing the header.
3078 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3081 * If the block read failed, the file is probably new.
3086 * Initialize for all unknown fields.
3088 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3091 * Initialize the magic number.
3093 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3096 * And a bunch of things we don't know what they mean
3098 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3099 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3100 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3101 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3105 * Write the information to the header.
3107 StorageUtl_WriteWord(
3109 OFFSET_BIGBLOCKSIZEBITS,
3110 This->bigBlockSizeBits);
3112 StorageUtl_WriteWord(
3114 OFFSET_SMALLBLOCKSIZEBITS,
3115 This->smallBlockSizeBits);
3117 StorageUtl_WriteDWord(
3119 OFFSET_BBDEPOTCOUNT,
3120 This->bigBlockDepotCount);
3122 StorageUtl_WriteDWord(
3124 OFFSET_ROOTSTARTBLOCK,
3125 This->rootStartBlock);
3127 StorageUtl_WriteDWord(
3129 OFFSET_SBDEPOTSTART,
3130 This->smallBlockDepotStart);
3132 StorageUtl_WriteDWord(
3134 OFFSET_SBDEPOTCOUNT,
3135 This->smallBlockDepotChain ?
3136 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3138 StorageUtl_WriteDWord(
3140 OFFSET_EXTBBDEPOTSTART,
3141 This->extBigBlockDepotStart);
3143 StorageUtl_WriteDWord(
3145 OFFSET_EXTBBDEPOTCOUNT,
3146 This->extBigBlockDepotCount);
3148 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3150 StorageUtl_WriteDWord(
3152 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3153 (This->bigBlockDepotStart[index]));
3157 * Write the big block back to the file.
3159 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3162 /******************************************************************************
3163 * Storage32Impl_ReadProperty
3165 * This method will read the specified property from the property chain.
3167 BOOL StorageImpl_ReadProperty(
3170 StgProperty* buffer)
3172 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3173 ULARGE_INTEGER offsetInPropSet;
3177 offsetInPropSet.u.HighPart = 0;
3178 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3180 readRes = BlockChainStream_ReadAt(
3181 This->rootBlockChain,
3187 if (SUCCEEDED(readRes))
3189 /* replace the name of root entry (often "Root Entry") by the file name */
3190 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3191 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3193 memset(buffer->name, 0, sizeof(buffer->name));
3197 PROPERTY_NAME_BUFFER_LEN );
3198 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3200 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3202 StorageUtl_ReadWord(
3204 OFFSET_PS_NAMELENGTH,
3205 &buffer->sizeOfNameString);
3207 StorageUtl_ReadDWord(
3209 OFFSET_PS_LEFTCHILD,
3210 &buffer->leftChild);
3212 StorageUtl_ReadDWord(
3214 OFFSET_PS_RIGHTCHILD,
3215 &buffer->rightChild);
3217 StorageUtl_ReadDWord(
3220 &buffer->dirProperty);
3222 StorageUtl_ReadGUID(
3225 &buffer->propertyUniqueID);
3227 StorageUtl_ReadDWord(
3230 &buffer->ctime.dwLowDateTime);
3232 StorageUtl_ReadDWord(
3234 OFFSET_PS_CTIMEHIGH,
3235 &buffer->ctime.dwHighDateTime);
3237 StorageUtl_ReadDWord(
3240 &buffer->mtime.dwLowDateTime);
3242 StorageUtl_ReadDWord(
3244 OFFSET_PS_MTIMEHIGH,
3245 &buffer->mtime.dwHighDateTime);
3247 StorageUtl_ReadDWord(
3249 OFFSET_PS_STARTBLOCK,
3250 &buffer->startingBlock);
3252 StorageUtl_ReadDWord(
3255 &buffer->size.u.LowPart);
3257 buffer->size.u.HighPart = 0;
3260 return SUCCEEDED(readRes) ? TRUE : FALSE;
3263 /*********************************************************************
3264 * Write the specified property into the property chain
3266 BOOL StorageImpl_WriteProperty(
3269 const StgProperty* buffer)
3271 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3272 ULARGE_INTEGER offsetInPropSet;
3276 offsetInPropSet.u.HighPart = 0;
3277 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3279 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3282 currentProperty + OFFSET_PS_NAME,
3284 PROPERTY_NAME_BUFFER_LEN );
3286 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3288 StorageUtl_WriteWord(
3290 OFFSET_PS_NAMELENGTH,
3291 buffer->sizeOfNameString);
3293 StorageUtl_WriteDWord(
3295 OFFSET_PS_LEFTCHILD,
3298 StorageUtl_WriteDWord(
3300 OFFSET_PS_RIGHTCHILD,
3301 buffer->rightChild);
3303 StorageUtl_WriteDWord(
3306 buffer->dirProperty);
3308 StorageUtl_WriteGUID(
3311 &buffer->propertyUniqueID);
3313 StorageUtl_WriteDWord(
3316 buffer->ctime.dwLowDateTime);
3318 StorageUtl_WriteDWord(
3320 OFFSET_PS_CTIMEHIGH,
3321 buffer->ctime.dwHighDateTime);
3323 StorageUtl_WriteDWord(
3326 buffer->mtime.dwLowDateTime);
3328 StorageUtl_WriteDWord(
3330 OFFSET_PS_MTIMEHIGH,
3331 buffer->ctime.dwHighDateTime);
3333 StorageUtl_WriteDWord(
3335 OFFSET_PS_STARTBLOCK,
3336 buffer->startingBlock);
3338 StorageUtl_WriteDWord(
3341 buffer->size.u.LowPart);
3343 writeRes = BlockChainStream_WriteAt(This->rootBlockChain,
3348 return SUCCEEDED(writeRes) ? TRUE : FALSE;
3351 static BOOL StorageImpl_ReadBigBlock(
3356 ULARGE_INTEGER ulOffset;
3359 ulOffset.u.HighPart = 0;
3360 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3362 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3363 return (read == This->bigBlockSize);
3366 static BOOL StorageImpl_ReadDWordFromBigBlock(
3372 ULARGE_INTEGER ulOffset;
3376 ulOffset.u.HighPart = 0;
3377 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3378 ulOffset.u.LowPart += offset;
3380 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3381 *value = lendian32toh(tmp);
3382 return (read == sizeof(DWORD));
3385 static BOOL StorageImpl_WriteBigBlock(
3390 ULARGE_INTEGER ulOffset;
3393 ulOffset.u.HighPart = 0;
3394 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3396 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3397 return (wrote == This->bigBlockSize);
3400 static BOOL StorageImpl_WriteDWordToBigBlock(
3406 ULARGE_INTEGER ulOffset;
3409 ulOffset.u.HighPart = 0;
3410 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3411 ulOffset.u.LowPart += offset;
3413 value = htole32(value);
3414 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3415 return (wrote == sizeof(DWORD));
3418 /******************************************************************************
3419 * Storage32Impl_SmallBlocksToBigBlocks
3421 * This method will convert a small block chain to a big block chain.
3422 * The small block chain will be destroyed.
3424 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3426 SmallBlockChainStream** ppsbChain)
3428 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3429 ULARGE_INTEGER size, offset;
3430 ULONG cbRead, cbWritten;
3431 ULARGE_INTEGER cbTotalRead;
3432 ULONG propertyIndex;
3433 HRESULT resWrite = S_OK;
3435 StgProperty chainProperty;
3437 BlockChainStream *bbTempChain = NULL;
3438 BlockChainStream *bigBlockChain = NULL;
3441 * Create a temporary big block chain that doesn't have
3442 * an associated property. This temporary chain will be
3443 * used to copy data from small blocks to big blocks.
3445 bbTempChain = BlockChainStream_Construct(This,
3448 if(!bbTempChain) return NULL;
3450 * Grow the big block chain.
3452 size = SmallBlockChainStream_GetSize(*ppsbChain);
3453 BlockChainStream_SetSize(bbTempChain, size);
3456 * Copy the contents of the small block chain to the big block chain
3457 * by small block size increments.
3459 offset.u.LowPart = 0;
3460 offset.u.HighPart = 0;
3461 cbTotalRead.QuadPart = 0;
3463 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3466 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3468 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3471 if (FAILED(resRead))
3476 cbTotalRead.QuadPart += cbRead;
3478 resWrite = BlockChainStream_WriteAt(bbTempChain,
3484 if (FAILED(resWrite))
3487 offset.u.LowPart += cbRead;
3489 } while (cbTotalRead.QuadPart < size.QuadPart);
3490 HeapFree(GetProcessHeap(),0,buffer);
3492 size.u.HighPart = 0;
3495 if (FAILED(resRead) || FAILED(resWrite))
3497 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3498 BlockChainStream_SetSize(bbTempChain, size);
3499 BlockChainStream_Destroy(bbTempChain);
3504 * Destroy the small block chain.
3506 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3507 SmallBlockChainStream_SetSize(*ppsbChain, size);
3508 SmallBlockChainStream_Destroy(*ppsbChain);
3512 * Change the property information. This chain is now a big block chain
3513 * and it doesn't reside in the small blocks chain anymore.
3515 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3517 chainProperty.startingBlock = bbHeadOfChain;
3519 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3522 * Destroy the temporary propertyless big block chain.
3523 * Create a new big block chain associated with this property.
3525 BlockChainStream_Destroy(bbTempChain);
3526 bigBlockChain = BlockChainStream_Construct(This,
3530 return bigBlockChain;
3533 /******************************************************************************
3534 * Storage32Impl_BigBlocksToSmallBlocks
3536 * This method will convert a big block chain to a small block chain.
3537 * The big block chain will be destroyed on success.
3539 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3541 BlockChainStream** ppbbChain)
3543 ULARGE_INTEGER size, offset, cbTotalRead;
3544 ULONG cbRead, cbWritten, propertyIndex, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3545 HRESULT resWrite = S_OK, resRead;
3546 StgProperty chainProperty;
3548 SmallBlockChainStream* sbTempChain;
3550 TRACE("%p %p\n", This, ppbbChain);
3552 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3558 size = BlockChainStream_GetSize(*ppbbChain);
3559 SmallBlockChainStream_SetSize(sbTempChain, size);
3561 offset.u.HighPart = 0;
3562 offset.u.LowPart = 0;
3563 cbTotalRead.QuadPart = 0;
3564 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3567 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3568 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
3576 cbTotalRead.QuadPart += cbRead;
3578 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3579 cbRead, buffer, &cbWritten);
3581 if(FAILED(resWrite))
3584 offset.u.LowPart += cbRead;
3586 }while(cbTotalRead.QuadPart < size.QuadPart);
3587 HeapFree(GetProcessHeap(), 0, buffer);
3589 size.u.HighPart = 0;
3592 if(FAILED(resRead) || FAILED(resWrite))
3594 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3595 SmallBlockChainStream_SetSize(sbTempChain, size);
3596 SmallBlockChainStream_Destroy(sbTempChain);
3600 /* destroy the original big block chain */
3601 propertyIndex = (*ppbbChain)->ownerPropertyIndex;
3602 BlockChainStream_SetSize(*ppbbChain, size);
3603 BlockChainStream_Destroy(*ppbbChain);
3606 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3607 chainProperty.startingBlock = sbHeadOfChain;
3608 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3610 SmallBlockChainStream_Destroy(sbTempChain);
3611 return SmallBlockChainStream_Construct(This, NULL, propertyIndex);
3614 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3616 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3618 HeapFree(GetProcessHeap(), 0, This);
3621 /******************************************************************************
3623 ** Storage32InternalImpl_Commit
3626 static HRESULT WINAPI StorageInternalImpl_Commit(
3628 DWORD grfCommitFlags) /* [in] */
3630 FIXME("(%p,%x): stub\n", iface, grfCommitFlags);
3634 /******************************************************************************
3636 ** Storage32InternalImpl_Revert
3639 static HRESULT WINAPI StorageInternalImpl_Revert(
3642 FIXME("(%p): stub\n", iface);
3646 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3648 IStorage_Release((IStorage*)This->parentStorage);
3649 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3650 HeapFree(GetProcessHeap(), 0, This);
3653 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3654 IEnumSTATSTG* iface,
3658 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3661 return E_INVALIDARG;
3665 if (IsEqualGUID(&IID_IUnknown, riid) ||
3666 IsEqualGUID(&IID_IEnumSTATSTG, riid))
3669 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3673 return E_NOINTERFACE;
3676 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3677 IEnumSTATSTG* iface)
3679 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3680 return InterlockedIncrement(&This->ref);
3683 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3684 IEnumSTATSTG* iface)
3686 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3690 newRef = InterlockedDecrement(&This->ref);
3694 IEnumSTATSTGImpl_Destroy(This);
3700 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3701 IEnumSTATSTG* iface,
3704 ULONG* pceltFetched)
3706 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3708 StgProperty currentProperty;
3709 STATSTG* currentReturnStruct = rgelt;
3710 ULONG objectFetched = 0;
3711 ULONG currentSearchNode;
3713 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3714 return E_INVALIDARG;
3717 * To avoid the special case, get another pointer to a ULONG value if
3718 * the caller didn't supply one.
3720 if (pceltFetched==0)
3721 pceltFetched = &objectFetched;
3724 * Start the iteration, we will iterate until we hit the end of the
3725 * linked list or until we hit the number of items to iterate through
3730 * Start with the node at the top of the stack.
3732 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3734 while ( ( *pceltFetched < celt) &&
3735 ( currentSearchNode!=PROPERTY_NULL) )
3738 * Remove the top node from the stack
3740 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3743 * Read the property from the storage.
3745 StorageImpl_ReadProperty(This->parentStorage,
3750 * Copy the information to the return buffer.
3752 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3757 * Step to the next item in the iteration
3760 currentReturnStruct++;
3763 * Push the next search node in the search stack.
3765 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.rightChild);
3768 * continue the iteration.
3770 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3773 if (*pceltFetched == celt)
3780 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3781 IEnumSTATSTG* iface,
3784 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3786 StgProperty currentProperty;
3787 ULONG objectFetched = 0;
3788 ULONG currentSearchNode;
3791 * Start with the node at the top of the stack.
3793 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3795 while ( (objectFetched < celt) &&
3796 (currentSearchNode!=PROPERTY_NULL) )
3799 * Remove the top node from the stack
3801 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3804 * Read the property from the storage.
3806 StorageImpl_ReadProperty(This->parentStorage,
3811 * Step to the next item in the iteration
3816 * Push the next search node in the search stack.
3818 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.rightChild);
3821 * continue the iteration.
3823 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3826 if (objectFetched == celt)
3832 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3833 IEnumSTATSTG* iface)
3835 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3837 StgProperty rootProperty;
3838 BOOL readSuccessful;
3841 * Re-initialize the search stack to an empty stack
3843 This->stackSize = 0;
3846 * Read the root property from the storage.
3848 readSuccessful = StorageImpl_ReadProperty(
3849 This->parentStorage,
3850 This->firstPropertyNode,
3855 assert(rootProperty.sizeOfNameString!=0);
3858 * Push the search node in the search stack.
3860 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3866 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3867 IEnumSTATSTG* iface,
3868 IEnumSTATSTG** ppenum)
3870 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3872 IEnumSTATSTGImpl* newClone;
3875 * Perform a sanity check on the parameters.
3878 return E_INVALIDARG;
3880 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3881 This->firstPropertyNode);
3885 * The new clone enumeration must point to the same current node as
3888 newClone->stackSize = This->stackSize ;
3889 newClone->stackMaxSize = This->stackMaxSize ;
3890 newClone->stackToVisit =
3891 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3894 newClone->stackToVisit,
3896 sizeof(ULONG) * newClone->stackSize);
3898 *ppenum = (IEnumSTATSTG*)newClone;
3901 * Don't forget to nail down a reference to the clone before
3904 IEnumSTATSTGImpl_AddRef(*ppenum);
3909 static INT IEnumSTATSTGImpl_FindParentProperty(
3910 IEnumSTATSTGImpl *This,
3911 ULONG childProperty,
3912 StgProperty *currentProperty,
3915 ULONG currentSearchNode;
3919 * To avoid the special case, get another pointer to a ULONG value if
3920 * the caller didn't supply one.
3923 thisNodeId = &foundNode;
3926 * Start with the node at the top of the stack.
3928 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3931 while (currentSearchNode!=PROPERTY_NULL)
3934 * Store the current node in the returned parameters
3936 *thisNodeId = currentSearchNode;
3939 * Remove the top node from the stack
3941 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3944 * Read the property from the storage.
3946 StorageImpl_ReadProperty(
3947 This->parentStorage,
3951 if (currentProperty->leftChild == childProperty)
3952 return PROPERTY_RELATION_PREVIOUS;
3954 else if (currentProperty->rightChild == childProperty)
3955 return PROPERTY_RELATION_NEXT;
3957 else if (currentProperty->dirProperty == childProperty)
3958 return PROPERTY_RELATION_DIR;
3961 * Push the next search node in the search stack.
3963 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->rightChild);
3966 * continue the iteration.
3968 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3971 return PROPERTY_NULL;
3974 static void IEnumSTATSTGImpl_PushSearchNode(
3975 IEnumSTATSTGImpl* This,
3978 StgProperty rootProperty;
3979 BOOL readSuccessful;
3982 * First, make sure we're not trying to push an unexisting node.
3984 if (nodeToPush==PROPERTY_NULL)
3988 * First push the node to the stack
3990 if (This->stackSize == This->stackMaxSize)
3992 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
3994 This->stackToVisit = HeapReAlloc(
3998 sizeof(ULONG) * This->stackMaxSize);
4001 This->stackToVisit[This->stackSize] = nodeToPush;
4005 * Read the root property from the storage.
4007 readSuccessful = StorageImpl_ReadProperty(
4008 This->parentStorage,
4014 assert(rootProperty.sizeOfNameString!=0);
4017 * Push the previous search node in the search stack.
4019 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.leftChild);
4023 static ULONG IEnumSTATSTGImpl_PopSearchNode(
4024 IEnumSTATSTGImpl* This,
4029 if (This->stackSize == 0)
4030 return PROPERTY_NULL;
4032 topNode = This->stackToVisit[This->stackSize-1];
4041 * Virtual function table for the IEnumSTATSTGImpl class.
4043 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4045 IEnumSTATSTGImpl_QueryInterface,
4046 IEnumSTATSTGImpl_AddRef,
4047 IEnumSTATSTGImpl_Release,
4048 IEnumSTATSTGImpl_Next,
4049 IEnumSTATSTGImpl_Skip,
4050 IEnumSTATSTGImpl_Reset,
4051 IEnumSTATSTGImpl_Clone
4054 /******************************************************************************
4055 ** IEnumSTATSTGImpl implementation
4058 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4059 StorageImpl* parentStorage,
4060 ULONG firstPropertyNode)
4062 IEnumSTATSTGImpl* newEnumeration;
4064 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4066 if (newEnumeration!=0)
4069 * Set-up the virtual function table and reference count.
4071 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4072 newEnumeration->ref = 0;
4075 * We want to nail-down the reference to the storage in case the
4076 * enumeration out-lives the storage in the client application.
4078 newEnumeration->parentStorage = parentStorage;
4079 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4081 newEnumeration->firstPropertyNode = firstPropertyNode;
4084 * Initialize the search stack
4086 newEnumeration->stackSize = 0;
4087 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4088 newEnumeration->stackToVisit =
4089 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4092 * Make sure the current node of the iterator is the first one.
4094 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4097 return newEnumeration;
4101 * Virtual function table for the Storage32InternalImpl class.
4103 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4105 StorageBaseImpl_QueryInterface,
4106 StorageBaseImpl_AddRef,
4107 StorageBaseImpl_Release,
4108 StorageBaseImpl_CreateStream,
4109 StorageBaseImpl_OpenStream,
4110 StorageImpl_CreateStorage,
4111 StorageBaseImpl_OpenStorage,
4113 StorageImpl_MoveElementTo,
4114 StorageInternalImpl_Commit,
4115 StorageInternalImpl_Revert,
4116 StorageBaseImpl_EnumElements,
4117 StorageImpl_DestroyElement,
4118 StorageBaseImpl_RenameElement,
4119 StorageImpl_SetElementTimes,
4120 StorageBaseImpl_SetClass,
4121 StorageImpl_SetStateBits,
4122 StorageBaseImpl_Stat
4125 /******************************************************************************
4126 ** Storage32InternalImpl implementation
4129 static StorageInternalImpl* StorageInternalImpl_Construct(
4130 StorageImpl* ancestorStorage,
4132 ULONG rootPropertyIndex)
4134 StorageInternalImpl* newStorage;
4136 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
4141 * Initialize the stream list
4143 list_init(&newStorage->base.strmHead);
4146 * Initialize the virtual function table.
4148 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4149 newStorage->base.v_destructor = StorageInternalImpl_Destroy;
4150 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4153 * Keep the ancestor storage pointer but do not nail a reference to it.
4155 newStorage->base.ancestorStorage = ancestorStorage;
4158 * Keep the index of the root property set for this storage,
4160 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4168 /******************************************************************************
4169 ** StorageUtl implementation
4172 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4176 memcpy(&tmp, buffer+offset, sizeof(WORD));
4177 *value = lendian16toh(tmp);
4180 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4182 value = htole16(value);
4183 memcpy(buffer+offset, &value, sizeof(WORD));
4186 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4190 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4191 *value = lendian32toh(tmp);
4194 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4196 value = htole32(value);
4197 memcpy(buffer+offset, &value, sizeof(DWORD));
4200 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4201 ULARGE_INTEGER* value)
4203 #ifdef WORDS_BIGENDIAN
4206 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4207 value->u.LowPart = htole32(tmp.u.HighPart);
4208 value->u.HighPart = htole32(tmp.u.LowPart);
4210 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4214 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4215 const ULARGE_INTEGER *value)
4217 #ifdef WORDS_BIGENDIAN
4220 tmp.u.LowPart = htole32(value->u.HighPart);
4221 tmp.u.HighPart = htole32(value->u.LowPart);
4222 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4224 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4228 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4230 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4231 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4232 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4234 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4237 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4239 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4240 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4241 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4243 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4246 void StorageUtl_CopyPropertyToSTATSTG(
4247 STATSTG* destination,
4248 const StgProperty* source,
4252 * The copy of the string occurs only when the flag is not set
4254 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4255 (source->name == NULL) ||
4256 (source->name[0] == 0) )
4258 destination->pwcsName = 0;
4262 destination->pwcsName =
4263 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4265 strcpyW(destination->pwcsName, source->name);
4268 switch (source->propertyType)
4270 case PROPTYPE_STORAGE:
4272 destination->type = STGTY_STORAGE;
4274 case PROPTYPE_STREAM:
4275 destination->type = STGTY_STREAM;
4278 destination->type = STGTY_STREAM;
4282 destination->cbSize = source->size;
4284 currentReturnStruct->mtime = {0}; TODO
4285 currentReturnStruct->ctime = {0};
4286 currentReturnStruct->atime = {0};
4288 destination->grfMode = 0;
4289 destination->grfLocksSupported = 0;
4290 destination->clsid = source->propertyUniqueID;
4291 destination->grfStateBits = 0;
4292 destination->reserved = 0;
4295 /******************************************************************************
4296 ** BlockChainStream implementation
4299 BlockChainStream* BlockChainStream_Construct(
4300 StorageImpl* parentStorage,
4301 ULONG* headOfStreamPlaceHolder,
4302 ULONG propertyIndex)
4304 BlockChainStream* newStream;
4307 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4309 newStream->parentStorage = parentStorage;
4310 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4311 newStream->ownerPropertyIndex = propertyIndex;
4312 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4313 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4314 newStream->numBlocks = 0;
4316 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4318 while (blockIndex != BLOCK_END_OF_CHAIN)
4320 newStream->numBlocks++;
4321 newStream->tailIndex = blockIndex;
4323 if(FAILED(StorageImpl_GetNextBlockInChain(
4328 HeapFree(GetProcessHeap(), 0, newStream);
4336 void BlockChainStream_Destroy(BlockChainStream* This)
4338 HeapFree(GetProcessHeap(), 0, This);
4341 /******************************************************************************
4342 * BlockChainStream_GetHeadOfChain
4344 * Returns the head of this stream chain.
4345 * Some special chains don't have properties, their heads are kept in
4346 * This->headOfStreamPlaceHolder.
4349 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4351 StgProperty chainProperty;
4352 BOOL readSuccessful;
4354 if (This->headOfStreamPlaceHolder != 0)
4355 return *(This->headOfStreamPlaceHolder);
4357 if (This->ownerPropertyIndex != PROPERTY_NULL)
4359 readSuccessful = StorageImpl_ReadProperty(
4360 This->parentStorage,
4361 This->ownerPropertyIndex,
4366 return chainProperty.startingBlock;
4370 return BLOCK_END_OF_CHAIN;
4373 /******************************************************************************
4374 * BlockChainStream_GetCount
4376 * Returns the number of blocks that comprises this chain.
4377 * This is not the size of the stream as the last block may not be full!
4380 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
4385 blockIndex = BlockChainStream_GetHeadOfChain(This);
4387 while (blockIndex != BLOCK_END_OF_CHAIN)
4391 if(FAILED(StorageImpl_GetNextBlockInChain(
4392 This->parentStorage,
4401 /******************************************************************************
4402 * BlockChainStream_ReadAt
4404 * Reads a specified number of bytes from this chain at the specified offset.
4405 * bytesRead may be NULL.
4406 * Failure will be returned if the specified number of bytes has not been read.
4408 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
4409 ULARGE_INTEGER offset,
4414 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4415 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4416 ULONG bytesToReadInBuffer;
4420 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
4423 * Find the first block in the stream that contains part of the buffer.
4425 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4426 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4427 (blockNoInSequence < This->lastBlockNoInSequence) )
4429 blockIndex = BlockChainStream_GetHeadOfChain(This);
4430 This->lastBlockNoInSequence = blockNoInSequence;
4434 ULONG temp = blockNoInSequence;
4436 blockIndex = This->lastBlockNoInSequenceIndex;
4437 blockNoInSequence -= This->lastBlockNoInSequence;
4438 This->lastBlockNoInSequence = temp;
4441 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4443 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4444 return STG_E_DOCFILECORRUPT;
4445 blockNoInSequence--;
4448 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4449 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
4451 This->lastBlockNoInSequenceIndex = blockIndex;
4454 * Start reading the buffer.
4457 bufferWalker = buffer;
4459 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4461 ULARGE_INTEGER ulOffset;
4464 * Calculate how many bytes we can copy from this big block.
4466 bytesToReadInBuffer =
4467 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4469 TRACE("block %i\n",blockIndex);
4470 ulOffset.u.HighPart = 0;
4471 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4474 StorageImpl_ReadAt(This->parentStorage,
4477 bytesToReadInBuffer,
4480 * Step to the next big block.
4482 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4483 return STG_E_DOCFILECORRUPT;
4485 bufferWalker += bytesReadAt;
4486 size -= bytesReadAt;
4487 *bytesRead += bytesReadAt;
4488 offsetInBlock = 0; /* There is no offset on the next block */
4490 if (bytesToReadInBuffer != bytesReadAt)
4494 return (size == 0) ? S_OK : STG_E_READFAULT;
4497 /******************************************************************************
4498 * BlockChainStream_WriteAt
4500 * Writes the specified number of bytes to this chain at the specified offset.
4501 * Will fail if not all specified number of bytes have been written.
4503 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
4504 ULARGE_INTEGER offset,
4507 ULONG* bytesWritten)
4509 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4510 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4513 const BYTE* bufferWalker;
4516 * Find the first block in the stream that contains part of the buffer.
4518 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4519 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4520 (blockNoInSequence < This->lastBlockNoInSequence) )
4522 blockIndex = BlockChainStream_GetHeadOfChain(This);
4523 This->lastBlockNoInSequence = blockNoInSequence;
4527 ULONG temp = blockNoInSequence;
4529 blockIndex = This->lastBlockNoInSequenceIndex;
4530 blockNoInSequence -= This->lastBlockNoInSequence;
4531 This->lastBlockNoInSequence = temp;
4534 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4536 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4538 return STG_E_DOCFILECORRUPT;
4539 blockNoInSequence--;
4542 This->lastBlockNoInSequenceIndex = blockIndex;
4544 /* BlockChainStream_SetSize should have already been called to ensure we have
4545 * enough blocks in the chain to write into */
4546 if (blockIndex == BLOCK_END_OF_CHAIN)
4548 ERR("not enough blocks in chain to write data\n");
4549 return STG_E_DOCFILECORRUPT;
4553 bufferWalker = buffer;
4555 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4557 ULARGE_INTEGER ulOffset;
4558 DWORD bytesWrittenAt;
4560 * Calculate how many bytes we can copy from this big block.
4563 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4565 TRACE("block %i\n",blockIndex);
4566 ulOffset.u.HighPart = 0;
4567 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4570 StorageImpl_WriteAt(This->parentStorage,
4577 * Step to the next big block.
4579 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4581 return STG_E_DOCFILECORRUPT;
4583 bufferWalker += bytesWrittenAt;
4584 size -= bytesWrittenAt;
4585 *bytesWritten += bytesWrittenAt;
4586 offsetInBlock = 0; /* There is no offset on the next block */
4588 if (bytesWrittenAt != bytesToWrite)
4592 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
4595 /******************************************************************************
4596 * BlockChainStream_Shrink
4598 * Shrinks this chain in the big block depot.
4600 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
4601 ULARGE_INTEGER newSize)
4603 ULONG blockIndex, extraBlock;
4608 * Reset the last accessed block cache.
4610 This->lastBlockNoInSequence = 0xFFFFFFFF;
4611 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4614 * Figure out how many blocks are needed to contain the new size
4616 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4618 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4621 blockIndex = BlockChainStream_GetHeadOfChain(This);
4624 * Go to the new end of chain
4626 while (count < numBlocks)
4628 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4634 /* Get the next block before marking the new end */
4635 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4639 /* Mark the new end of chain */
4640 StorageImpl_SetNextBlockInChain(
4641 This->parentStorage,
4643 BLOCK_END_OF_CHAIN);
4645 This->tailIndex = blockIndex;
4646 This->numBlocks = numBlocks;
4649 * Mark the extra blocks as free
4651 while (extraBlock != BLOCK_END_OF_CHAIN)
4653 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4656 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4657 extraBlock = blockIndex;
4663 /******************************************************************************
4664 * BlockChainStream_Enlarge
4666 * Grows this chain in the big block depot.
4668 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4669 ULARGE_INTEGER newSize)
4671 ULONG blockIndex, currentBlock;
4673 ULONG oldNumBlocks = 0;
4675 blockIndex = BlockChainStream_GetHeadOfChain(This);
4678 * Empty chain. Create the head.
4680 if (blockIndex == BLOCK_END_OF_CHAIN)
4682 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4683 StorageImpl_SetNextBlockInChain(This->parentStorage,
4685 BLOCK_END_OF_CHAIN);
4687 if (This->headOfStreamPlaceHolder != 0)
4689 *(This->headOfStreamPlaceHolder) = blockIndex;
4693 StgProperty chainProp;
4694 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4696 StorageImpl_ReadProperty(
4697 This->parentStorage,
4698 This->ownerPropertyIndex,
4701 chainProp.startingBlock = blockIndex;
4703 StorageImpl_WriteProperty(
4704 This->parentStorage,
4705 This->ownerPropertyIndex,
4709 This->tailIndex = blockIndex;
4710 This->numBlocks = 1;
4714 * Figure out how many blocks are needed to contain this stream
4716 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4718 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4722 * Go to the current end of chain
4724 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4726 currentBlock = blockIndex;
4728 while (blockIndex != BLOCK_END_OF_CHAIN)
4731 currentBlock = blockIndex;
4733 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4738 This->tailIndex = currentBlock;
4741 currentBlock = This->tailIndex;
4742 oldNumBlocks = This->numBlocks;
4745 * Add new blocks to the chain
4747 if (oldNumBlocks < newNumBlocks)
4749 while (oldNumBlocks < newNumBlocks)
4751 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4753 StorageImpl_SetNextBlockInChain(
4754 This->parentStorage,
4758 StorageImpl_SetNextBlockInChain(
4759 This->parentStorage,
4761 BLOCK_END_OF_CHAIN);
4763 currentBlock = blockIndex;
4767 This->tailIndex = blockIndex;
4768 This->numBlocks = newNumBlocks;
4774 /******************************************************************************
4775 * BlockChainStream_SetSize
4777 * Sets the size of this stream. The big block depot will be updated.
4778 * The file will grow if we grow the chain.
4780 * TODO: Free the actual blocks in the file when we shrink the chain.
4781 * Currently, the blocks are still in the file. So the file size
4782 * doesn't shrink even if we shrink streams.
4784 BOOL BlockChainStream_SetSize(
4785 BlockChainStream* This,
4786 ULARGE_INTEGER newSize)
4788 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4790 if (newSize.u.LowPart == size.u.LowPart)
4793 if (newSize.u.LowPart < size.u.LowPart)
4795 BlockChainStream_Shrink(This, newSize);
4799 BlockChainStream_Enlarge(This, newSize);
4805 /******************************************************************************
4806 * BlockChainStream_GetSize
4808 * Returns the size of this chain.
4809 * Will return the block count if this chain doesn't have a property.
4811 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4813 StgProperty chainProperty;
4815 if(This->headOfStreamPlaceHolder == NULL)
4818 * This chain is a data stream read the property and return
4819 * the appropriate size
4821 StorageImpl_ReadProperty(
4822 This->parentStorage,
4823 This->ownerPropertyIndex,
4826 return chainProperty.size;
4831 * this chain is a chain that does not have a property, figure out the
4832 * size by making the product number of used blocks times the
4835 ULARGE_INTEGER result;
4836 result.u.HighPart = 0;
4839 BlockChainStream_GetCount(This) *
4840 This->parentStorage->bigBlockSize;
4846 /******************************************************************************
4847 ** SmallBlockChainStream implementation
4850 SmallBlockChainStream* SmallBlockChainStream_Construct(
4851 StorageImpl* parentStorage,
4852 ULONG* headOfStreamPlaceHolder,
4853 ULONG propertyIndex)
4855 SmallBlockChainStream* newStream;
4857 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4859 newStream->parentStorage = parentStorage;
4860 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4861 newStream->ownerPropertyIndex = propertyIndex;
4866 void SmallBlockChainStream_Destroy(
4867 SmallBlockChainStream* This)
4869 HeapFree(GetProcessHeap(), 0, This);
4872 /******************************************************************************
4873 * SmallBlockChainStream_GetHeadOfChain
4875 * Returns the head of this chain of small blocks.
4877 static ULONG SmallBlockChainStream_GetHeadOfChain(
4878 SmallBlockChainStream* This)
4880 StgProperty chainProperty;
4881 BOOL readSuccessful;
4883 if (This->headOfStreamPlaceHolder != NULL)
4884 return *(This->headOfStreamPlaceHolder);
4886 if (This->ownerPropertyIndex)
4888 readSuccessful = StorageImpl_ReadProperty(
4889 This->parentStorage,
4890 This->ownerPropertyIndex,
4895 return chainProperty.startingBlock;
4900 return BLOCK_END_OF_CHAIN;
4903 /******************************************************************************
4904 * SmallBlockChainStream_GetNextBlockInChain
4906 * Returns the index of the next small block in this chain.
4909 * - BLOCK_END_OF_CHAIN: end of this chain
4910 * - BLOCK_UNUSED: small block 'blockIndex' is free
4912 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
4913 SmallBlockChainStream* This,
4915 ULONG* nextBlockInChain)
4917 ULARGE_INTEGER offsetOfBlockInDepot;
4922 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4924 offsetOfBlockInDepot.u.HighPart = 0;
4925 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4928 * Read those bytes in the buffer from the small block file.
4930 res = BlockChainStream_ReadAt(
4931 This->parentStorage->smallBlockDepotChain,
4932 offsetOfBlockInDepot,
4939 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
4946 /******************************************************************************
4947 * SmallBlockChainStream_SetNextBlockInChain
4949 * Writes the index of the next block of the specified block in the small
4951 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4952 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4954 static void SmallBlockChainStream_SetNextBlockInChain(
4955 SmallBlockChainStream* This,
4959 ULARGE_INTEGER offsetOfBlockInDepot;
4963 offsetOfBlockInDepot.u.HighPart = 0;
4964 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4966 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
4969 * Read those bytes in the buffer from the small block file.
4971 BlockChainStream_WriteAt(
4972 This->parentStorage->smallBlockDepotChain,
4973 offsetOfBlockInDepot,
4979 /******************************************************************************
4980 * SmallBlockChainStream_FreeBlock
4982 * Flag small block 'blockIndex' as free in the small block depot.
4984 static void SmallBlockChainStream_FreeBlock(
4985 SmallBlockChainStream* This,
4988 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4991 /******************************************************************************
4992 * SmallBlockChainStream_GetNextFreeBlock
4994 * Returns the index of a free small block. The small block depot will be
4995 * enlarged if necessary. The small block chain will also be enlarged if
4998 static ULONG SmallBlockChainStream_GetNextFreeBlock(
4999 SmallBlockChainStream* This)
5001 ULARGE_INTEGER offsetOfBlockInDepot;
5004 ULONG blockIndex = 0;
5005 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5007 ULONG smallBlocksPerBigBlock;
5009 offsetOfBlockInDepot.u.HighPart = 0;
5012 * Scan the small block depot for a free block
5014 while (nextBlockIndex != BLOCK_UNUSED)
5016 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5018 res = BlockChainStream_ReadAt(
5019 This->parentStorage->smallBlockDepotChain,
5020 offsetOfBlockInDepot,
5026 * If we run out of space for the small block depot, enlarge it
5030 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5032 if (nextBlockIndex != BLOCK_UNUSED)
5038 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5040 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5041 ULONG nextBlock, newsbdIndex;
5042 BYTE smallBlockDepot[BIG_BLOCK_SIZE];
5044 nextBlock = sbdIndex;
5045 while (nextBlock != BLOCK_END_OF_CHAIN)
5047 sbdIndex = nextBlock;
5048 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5051 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5052 if (sbdIndex != BLOCK_END_OF_CHAIN)
5053 StorageImpl_SetNextBlockInChain(
5054 This->parentStorage,
5058 StorageImpl_SetNextBlockInChain(
5059 This->parentStorage,
5061 BLOCK_END_OF_CHAIN);
5064 * Initialize all the small blocks to free
5066 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5067 StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
5072 * We have just created the small block depot.
5074 StgProperty rootProp;
5078 * Save it in the header
5080 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5081 StorageImpl_SaveFileHeader(This->parentStorage);
5084 * And allocate the first big block that will contain small blocks
5087 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5089 StorageImpl_SetNextBlockInChain(
5090 This->parentStorage,
5092 BLOCK_END_OF_CHAIN);
5094 StorageImpl_ReadProperty(
5095 This->parentStorage,
5096 This->parentStorage->base.rootPropertySetIndex,
5099 rootProp.startingBlock = sbStartIndex;
5100 rootProp.size.u.HighPart = 0;
5101 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
5103 StorageImpl_WriteProperty(
5104 This->parentStorage,
5105 This->parentStorage->base.rootPropertySetIndex,
5109 StorageImpl_SaveFileHeader(This->parentStorage);
5113 smallBlocksPerBigBlock =
5114 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5117 * Verify if we have to allocate big blocks to contain small blocks
5119 if (blockIndex % smallBlocksPerBigBlock == 0)
5121 StgProperty rootProp;
5122 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5124 StorageImpl_ReadProperty(
5125 This->parentStorage,
5126 This->parentStorage->base.rootPropertySetIndex,
5129 if (rootProp.size.u.LowPart <
5130 (blocksRequired * This->parentStorage->bigBlockSize))
5132 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5134 BlockChainStream_SetSize(
5135 This->parentStorage->smallBlockRootChain,
5138 StorageImpl_WriteProperty(
5139 This->parentStorage,
5140 This->parentStorage->base.rootPropertySetIndex,
5148 /******************************************************************************
5149 * SmallBlockChainStream_ReadAt
5151 * Reads a specified number of bytes from this chain at the specified offset.
5152 * bytesRead may be NULL.
5153 * Failure will be returned if the specified number of bytes has not been read.
5155 HRESULT SmallBlockChainStream_ReadAt(
5156 SmallBlockChainStream* This,
5157 ULARGE_INTEGER offset,
5163 ULARGE_INTEGER offsetInBigBlockFile;
5164 ULONG blockNoInSequence =
5165 offset.u.LowPart / This->parentStorage->smallBlockSize;
5167 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5168 ULONG bytesToReadInBuffer;
5170 ULONG bytesReadFromBigBlockFile;
5174 * This should never happen on a small block file.
5176 assert(offset.u.HighPart==0);
5179 * Find the first block in the stream that contains part of the buffer.
5181 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5183 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5185 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5188 blockNoInSequence--;
5192 * Start reading the buffer.
5195 bufferWalker = buffer;
5197 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5200 * Calculate how many bytes we can copy from this small block.
5202 bytesToReadInBuffer =
5203 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5206 * Calculate the offset of the small block in the small block file.
5208 offsetInBigBlockFile.u.HighPart = 0;
5209 offsetInBigBlockFile.u.LowPart =
5210 blockIndex * This->parentStorage->smallBlockSize;
5212 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5215 * Read those bytes in the buffer from the small block file.
5216 * The small block has already been identified so it shouldn't fail
5217 * unless the file is corrupt.
5219 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5220 offsetInBigBlockFile,
5221 bytesToReadInBuffer,
5223 &bytesReadFromBigBlockFile);
5229 * Step to the next big block.
5231 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5233 return STG_E_DOCFILECORRUPT;
5235 bufferWalker += bytesReadFromBigBlockFile;
5236 size -= bytesReadFromBigBlockFile;
5237 *bytesRead += bytesReadFromBigBlockFile;
5238 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5241 return (size == 0) ? S_OK : STG_E_READFAULT;
5244 /******************************************************************************
5245 * SmallBlockChainStream_WriteAt
5247 * Writes the specified number of bytes to this chain at the specified offset.
5248 * Will fail if not all specified number of bytes have been written.
5250 HRESULT SmallBlockChainStream_WriteAt(
5251 SmallBlockChainStream* This,
5252 ULARGE_INTEGER offset,
5255 ULONG* bytesWritten)
5257 ULARGE_INTEGER offsetInBigBlockFile;
5258 ULONG blockNoInSequence =
5259 offset.u.LowPart / This->parentStorage->smallBlockSize;
5261 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5262 ULONG bytesToWriteInBuffer;
5264 ULONG bytesWrittenToBigBlockFile;
5265 const BYTE* bufferWalker;
5269 * This should never happen on a small block file.
5271 assert(offset.u.HighPart==0);
5274 * Find the first block in the stream that contains part of the buffer.
5276 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5278 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5280 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5281 return STG_E_DOCFILECORRUPT;
5282 blockNoInSequence--;
5286 * Start writing the buffer.
5289 bufferWalker = buffer;
5290 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5293 * Calculate how many bytes we can copy to this small block.
5295 bytesToWriteInBuffer =
5296 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5299 * Calculate the offset of the small block in the small block file.
5301 offsetInBigBlockFile.u.HighPart = 0;
5302 offsetInBigBlockFile.u.LowPart =
5303 blockIndex * This->parentStorage->smallBlockSize;
5305 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5308 * Write those bytes in the buffer to the small block file.
5310 res = BlockChainStream_WriteAt(
5311 This->parentStorage->smallBlockRootChain,
5312 offsetInBigBlockFile,
5313 bytesToWriteInBuffer,
5315 &bytesWrittenToBigBlockFile);
5320 * Step to the next big block.
5322 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5325 bufferWalker += bytesWrittenToBigBlockFile;
5326 size -= bytesWrittenToBigBlockFile;
5327 *bytesWritten += bytesWrittenToBigBlockFile;
5328 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
5331 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5334 /******************************************************************************
5335 * SmallBlockChainStream_Shrink
5337 * Shrinks this chain in the small block depot.
5339 static BOOL SmallBlockChainStream_Shrink(
5340 SmallBlockChainStream* This,
5341 ULARGE_INTEGER newSize)
5343 ULONG blockIndex, extraBlock;
5347 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5349 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5352 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5355 * Go to the new end of chain
5357 while (count < numBlocks)
5359 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5366 * If the count is 0, we have a special case, the head of the chain was
5371 StgProperty chainProp;
5373 StorageImpl_ReadProperty(This->parentStorage,
5374 This->ownerPropertyIndex,
5377 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5379 StorageImpl_WriteProperty(This->parentStorage,
5380 This->ownerPropertyIndex,
5384 * We start freeing the chain at the head block.
5386 extraBlock = blockIndex;
5390 /* Get the next block before marking the new end */
5391 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5395 /* Mark the new end of chain */
5396 SmallBlockChainStream_SetNextBlockInChain(
5399 BLOCK_END_OF_CHAIN);
5403 * Mark the extra blocks as free
5405 while (extraBlock != BLOCK_END_OF_CHAIN)
5407 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5410 SmallBlockChainStream_FreeBlock(This, extraBlock);
5411 extraBlock = blockIndex;
5417 /******************************************************************************
5418 * SmallBlockChainStream_Enlarge
5420 * Grows this chain in the small block depot.
5422 static BOOL SmallBlockChainStream_Enlarge(
5423 SmallBlockChainStream* This,
5424 ULARGE_INTEGER newSize)
5426 ULONG blockIndex, currentBlock;
5428 ULONG oldNumBlocks = 0;
5430 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5433 * Empty chain. Create the head.
5435 if (blockIndex == BLOCK_END_OF_CHAIN)
5437 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5438 SmallBlockChainStream_SetNextBlockInChain(
5441 BLOCK_END_OF_CHAIN);
5443 if (This->headOfStreamPlaceHolder != NULL)
5445 *(This->headOfStreamPlaceHolder) = blockIndex;
5449 StgProperty chainProp;
5451 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5454 chainProp.startingBlock = blockIndex;
5456 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5461 currentBlock = blockIndex;
5464 * Figure out how many blocks are needed to contain this stream
5466 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5468 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5472 * Go to the current end of chain
5474 while (blockIndex != BLOCK_END_OF_CHAIN)
5477 currentBlock = blockIndex;
5478 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5483 * Add new blocks to the chain
5485 while (oldNumBlocks < newNumBlocks)
5487 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5488 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5490 SmallBlockChainStream_SetNextBlockInChain(
5493 BLOCK_END_OF_CHAIN);
5495 currentBlock = blockIndex;
5502 /******************************************************************************
5503 * SmallBlockChainStream_SetSize
5505 * Sets the size of this stream.
5506 * The file will grow if we grow the chain.
5508 * TODO: Free the actual blocks in the file when we shrink the chain.
5509 * Currently, the blocks are still in the file. So the file size
5510 * doesn't shrink even if we shrink streams.
5512 BOOL SmallBlockChainStream_SetSize(
5513 SmallBlockChainStream* This,
5514 ULARGE_INTEGER newSize)
5516 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5518 if (newSize.u.LowPart == size.u.LowPart)
5521 if (newSize.u.LowPart < size.u.LowPart)
5523 SmallBlockChainStream_Shrink(This, newSize);
5527 SmallBlockChainStream_Enlarge(This, newSize);
5533 /******************************************************************************
5534 * SmallBlockChainStream_GetCount
5536 * Returns the number of small blocks that comprises this chain.
5537 * This is not the size of the stream as the last block may not be full!
5540 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5545 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5547 while(blockIndex != BLOCK_END_OF_CHAIN)
5551 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
5552 blockIndex, &blockIndex)))
5559 /******************************************************************************
5560 * SmallBlockChainStream_GetSize
5562 * Returns the size of this chain.
5564 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5566 StgProperty chainProperty;
5568 if(This->headOfStreamPlaceHolder != NULL)
5570 ULARGE_INTEGER result;
5571 result.u.HighPart = 0;
5573 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
5574 This->parentStorage->smallBlockSize;
5579 StorageImpl_ReadProperty(
5580 This->parentStorage,
5581 This->ownerPropertyIndex,
5584 return chainProperty.size;
5587 /******************************************************************************
5588 * StgCreateDocfile [OLE32.@]
5589 * Creates a new compound file storage object
5592 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5593 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5594 * reserved [ ?] unused?, usually 0
5595 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5598 * S_OK if the file was successfully created
5599 * some STG_E_ value if error
5601 * if pwcsName is NULL, create file with new unique name
5602 * the function can returns
5603 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5606 HRESULT WINAPI StgCreateDocfile(
5610 IStorage **ppstgOpen)
5612 StorageImpl* newStorage = 0;
5613 HANDLE hFile = INVALID_HANDLE_VALUE;
5614 HRESULT hr = STG_E_INVALIDFLAG;
5618 DWORD fileAttributes;
5619 WCHAR tempFileName[MAX_PATH];
5621 TRACE("(%s, %x, %d, %p)\n",
5622 debugstr_w(pwcsName), grfMode,
5623 reserved, ppstgOpen);
5626 return STG_E_INVALIDPOINTER;
5628 return STG_E_INVALIDPARAMETER;
5630 /* if no share mode given then DENY_NONE is the default */
5631 if (STGM_SHARE_MODE(grfMode) == 0)
5632 grfMode |= STGM_SHARE_DENY_NONE;
5634 if ( FAILED( validateSTGM(grfMode) ))
5637 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5638 switch(STGM_ACCESS_MODE(grfMode))
5641 case STGM_READWRITE:
5647 /* in direct mode, can only use SHARE_EXCLUSIVE */
5648 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5651 /* but in transacted mode, any share mode is valid */
5654 * Generate a unique name.
5658 WCHAR tempPath[MAX_PATH];
5659 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5661 memset(tempPath, 0, sizeof(tempPath));
5662 memset(tempFileName, 0, sizeof(tempFileName));
5664 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5667 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5668 pwcsName = tempFileName;
5671 hr = STG_E_INSUFFICIENTMEMORY;
5675 creationMode = TRUNCATE_EXISTING;
5679 creationMode = GetCreationModeFromSTGM(grfMode);
5683 * Interpret the STGM value grfMode
5685 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
5686 accessMode = GetAccessModeFromSTGM(grfMode);
5688 if (grfMode & STGM_DELETEONRELEASE)
5689 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5691 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5693 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
5694 FIXME("Storage share mode not implemented.\n");
5696 if (grfMode & STGM_TRANSACTED)
5697 FIXME("Transacted mode not implemented.\n");
5701 hFile = CreateFileW(pwcsName,
5709 if (hFile == INVALID_HANDLE_VALUE)
5711 if(GetLastError() == ERROR_FILE_EXISTS)
5712 hr = STG_E_FILEALREADYEXISTS;
5719 * Allocate and initialize the new IStorage32object.
5721 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5723 if (newStorage == 0)
5725 hr = STG_E_INSUFFICIENTMEMORY;
5729 hr = StorageImpl_Construct(
5740 HeapFree(GetProcessHeap(), 0, newStorage);
5745 * Get an "out" pointer for the caller.
5747 hr = StorageBaseImpl_QueryInterface(
5748 (IStorage*)newStorage,
5752 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
5757 /******************************************************************************
5758 * StgCreateStorageEx [OLE32.@]
5760 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5762 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5763 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5765 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5767 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5768 return STG_E_INVALIDPARAMETER;
5771 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5773 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5774 return STG_E_INVALIDPARAMETER;
5777 if (stgfmt == STGFMT_FILE)
5779 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5780 return STG_E_INVALIDPARAMETER;
5783 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5785 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5786 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5789 ERR("Invalid stgfmt argument\n");
5790 return STG_E_INVALIDPARAMETER;
5793 /******************************************************************************
5794 * StgCreatePropSetStg [OLE32.@]
5796 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5797 IPropertySetStorage **ppPropSetStg)
5801 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
5803 hr = STG_E_INVALIDPARAMETER;
5805 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5806 (void**)ppPropSetStg);
5810 /******************************************************************************
5811 * StgOpenStorageEx [OLE32.@]
5813 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5815 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5816 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5818 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5820 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5821 return STG_E_INVALIDPARAMETER;
5827 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5828 return STG_E_INVALIDPARAMETER;
5830 case STGFMT_STORAGE:
5833 case STGFMT_DOCFILE:
5834 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
5836 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5837 return STG_E_INVALIDPARAMETER;
5839 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5843 WARN("STGFMT_ANY assuming storage\n");
5847 return STG_E_INVALIDPARAMETER;
5850 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
5854 /******************************************************************************
5855 * StgOpenStorage [OLE32.@]
5857 HRESULT WINAPI StgOpenStorage(
5858 const OLECHAR *pwcsName,
5859 IStorage *pstgPriority,
5863 IStorage **ppstgOpen)
5865 StorageImpl* newStorage = 0;
5870 WCHAR fullname[MAX_PATH];
5872 TRACE("(%s, %p, %x, %p, %d, %p)\n",
5873 debugstr_w(pwcsName), pstgPriority, grfMode,
5874 snbExclude, reserved, ppstgOpen);
5878 hr = STG_E_INVALIDNAME;
5884 hr = STG_E_INVALIDPOINTER;
5890 hr = STG_E_INVALIDPARAMETER;
5894 if (grfMode & STGM_PRIORITY)
5896 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
5897 return STG_E_INVALIDFLAG;
5898 if (grfMode & STGM_DELETEONRELEASE)
5899 return STG_E_INVALIDFUNCTION;
5900 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
5901 return STG_E_INVALIDFLAG;
5902 grfMode &= ~0xf0; /* remove the existing sharing mode */
5903 grfMode |= STGM_SHARE_DENY_NONE;
5905 /* STGM_PRIORITY stops other IStorage objects on the same file from
5906 * committing until the STGM_PRIORITY IStorage is closed. it also
5907 * stops non-transacted mode StgOpenStorage calls with write access from
5908 * succeeding. obviously, both of these cannot be achieved through just
5909 * file share flags */
5910 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5914 * Validate the sharing mode
5916 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
5917 switch(STGM_SHARE_MODE(grfMode))
5919 case STGM_SHARE_EXCLUSIVE:
5920 case STGM_SHARE_DENY_WRITE:
5923 hr = STG_E_INVALIDFLAG;
5927 if ( FAILED( validateSTGM(grfMode) ) ||
5928 (grfMode&STGM_CREATE))
5930 hr = STG_E_INVALIDFLAG;
5934 /* shared reading requires transacted mode */
5935 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5936 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
5937 !(grfMode&STGM_TRANSACTED) )
5939 hr = STG_E_INVALIDFLAG;
5944 * Interpret the STGM value grfMode
5946 shareMode = GetShareModeFromSTGM(grfMode);
5947 accessMode = GetAccessModeFromSTGM(grfMode);
5951 hFile = CreateFileW( pwcsName,
5956 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5959 if (hFile==INVALID_HANDLE_VALUE)
5961 DWORD last_error = GetLastError();
5967 case ERROR_FILE_NOT_FOUND:
5968 hr = STG_E_FILENOTFOUND;
5971 case ERROR_PATH_NOT_FOUND:
5972 hr = STG_E_PATHNOTFOUND;
5975 case ERROR_ACCESS_DENIED:
5976 case ERROR_WRITE_PROTECT:
5977 hr = STG_E_ACCESSDENIED;
5980 case ERROR_SHARING_VIOLATION:
5981 hr = STG_E_SHAREVIOLATION;
5992 * Refuse to open the file if it's too small to be a structured storage file
5993 * FIXME: verify the file when reading instead of here
5995 if (GetFileSize(hFile, NULL) < 0x100)
5998 hr = STG_E_FILEALREADYEXISTS;
6003 * Allocate and initialize the new IStorage32object.
6005 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6007 if (newStorage == 0)
6009 hr = STG_E_INSUFFICIENTMEMORY;
6013 /* Initialize the storage */
6014 hr = StorageImpl_Construct(
6025 HeapFree(GetProcessHeap(), 0, newStorage);
6027 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6029 if(hr == STG_E_INVALIDHEADER)
6030 hr = STG_E_FILEALREADYEXISTS;
6034 /* prepare the file name string given in lieu of the root property name */
6035 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
6036 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
6037 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
6040 * Get an "out" pointer for the caller.
6042 hr = StorageBaseImpl_QueryInterface(
6043 (IStorage*)newStorage,
6048 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6052 /******************************************************************************
6053 * StgCreateDocfileOnILockBytes [OLE32.@]
6055 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6059 IStorage** ppstgOpen)
6061 StorageImpl* newStorage = 0;
6064 if ((ppstgOpen == 0) || (plkbyt == 0))
6065 return STG_E_INVALIDPOINTER;
6068 * Allocate and initialize the new IStorage object.
6070 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6072 if (newStorage == 0)
6073 return STG_E_INSUFFICIENTMEMORY;
6075 hr = StorageImpl_Construct(
6086 HeapFree(GetProcessHeap(), 0, newStorage);
6091 * Get an "out" pointer for the caller.
6093 hr = StorageBaseImpl_QueryInterface(
6094 (IStorage*)newStorage,
6101 /******************************************************************************
6102 * StgOpenStorageOnILockBytes [OLE32.@]
6104 HRESULT WINAPI StgOpenStorageOnILockBytes(
6106 IStorage *pstgPriority,
6110 IStorage **ppstgOpen)
6112 StorageImpl* newStorage = 0;
6115 if ((plkbyt == 0) || (ppstgOpen == 0))
6116 return STG_E_INVALIDPOINTER;
6118 if ( FAILED( validateSTGM(grfMode) ))
6119 return STG_E_INVALIDFLAG;
6124 * Allocate and initialize the new IStorage object.
6126 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6128 if (newStorage == 0)
6129 return STG_E_INSUFFICIENTMEMORY;
6131 hr = StorageImpl_Construct(
6142 HeapFree(GetProcessHeap(), 0, newStorage);
6147 * Get an "out" pointer for the caller.
6149 hr = StorageBaseImpl_QueryInterface(
6150 (IStorage*)newStorage,
6157 /******************************************************************************
6158 * StgSetTimes [ole32.@]
6159 * StgSetTimes [OLE32.@]
6163 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6164 FILETIME const *patime, FILETIME const *pmtime)
6166 IStorage *stg = NULL;
6169 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6171 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6175 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6176 IStorage_Release(stg);
6182 /******************************************************************************
6183 * StgIsStorageILockBytes [OLE32.@]
6185 * Determines if the ILockBytes contains a storage object.
6187 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6190 ULARGE_INTEGER offset;
6192 offset.u.HighPart = 0;
6193 offset.u.LowPart = 0;
6195 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6197 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6203 /******************************************************************************
6204 * WriteClassStg [OLE32.@]
6206 * This method will store the specified CLSID in the specified storage object
6208 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6213 return E_INVALIDARG;
6216 return STG_E_INVALIDPOINTER;
6218 hRes = IStorage_SetClass(pStg, rclsid);
6223 /***********************************************************************
6224 * ReadClassStg (OLE32.@)
6226 * This method reads the CLSID previously written to a storage object with
6227 * the WriteClassStg.
6230 * pstg [I] IStorage pointer
6231 * pclsid [O] Pointer to where the CLSID is written
6235 * Failure: HRESULT code.
6237 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6242 TRACE("(%p, %p)\n", pstg, pclsid);
6244 if(!pstg || !pclsid)
6245 return E_INVALIDARG;
6248 * read a STATSTG structure (contains the clsid) from the storage
6250 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
6253 *pclsid=pstatstg.clsid;
6258 /***********************************************************************
6259 * OleLoadFromStream (OLE32.@)
6261 * This function loads an object from stream
6263 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6267 LPPERSISTSTREAM xstm;
6269 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6271 res=ReadClassStm(pStm,&clsid);
6274 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6277 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6279 IUnknown_Release((IUnknown*)*ppvObj);
6282 res=IPersistStream_Load(xstm,pStm);
6283 IPersistStream_Release(xstm);
6284 /* FIXME: all refcounts ok at this point? I think they should be:
6287 * xstm : 0 (released)
6292 /***********************************************************************
6293 * OleSaveToStream (OLE32.@)
6295 * This function saves an object with the IPersistStream interface on it
6296 * to the specified stream.
6298 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6304 TRACE("(%p,%p)\n",pPStm,pStm);
6306 res=IPersistStream_GetClassID(pPStm,&clsid);
6308 if (SUCCEEDED(res)){
6310 res=WriteClassStm(pStm,&clsid);
6314 res=IPersistStream_Save(pPStm,pStm,TRUE);
6317 TRACE("Finished Save\n");
6321 /****************************************************************************
6322 * This method validate a STGM parameter that can contain the values below
6324 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6325 * The stgm values contained in 0xffff0000 are bitmasks.
6327 * STGM_DIRECT 0x00000000
6328 * STGM_TRANSACTED 0x00010000
6329 * STGM_SIMPLE 0x08000000
6331 * STGM_READ 0x00000000
6332 * STGM_WRITE 0x00000001
6333 * STGM_READWRITE 0x00000002
6335 * STGM_SHARE_DENY_NONE 0x00000040
6336 * STGM_SHARE_DENY_READ 0x00000030
6337 * STGM_SHARE_DENY_WRITE 0x00000020
6338 * STGM_SHARE_EXCLUSIVE 0x00000010
6340 * STGM_PRIORITY 0x00040000
6341 * STGM_DELETEONRELEASE 0x04000000
6343 * STGM_CREATE 0x00001000
6344 * STGM_CONVERT 0x00020000
6345 * STGM_FAILIFTHERE 0x00000000
6347 * STGM_NOSCRATCH 0x00100000
6348 * STGM_NOSNAPSHOT 0x00200000
6350 static HRESULT validateSTGM(DWORD stgm)
6352 DWORD access = STGM_ACCESS_MODE(stgm);
6353 DWORD share = STGM_SHARE_MODE(stgm);
6354 DWORD create = STGM_CREATE_MODE(stgm);
6356 if (stgm&~STGM_KNOWN_FLAGS)
6358 ERR("unknown flags %08x\n", stgm);
6366 case STGM_READWRITE:
6374 case STGM_SHARE_DENY_NONE:
6375 case STGM_SHARE_DENY_READ:
6376 case STGM_SHARE_DENY_WRITE:
6377 case STGM_SHARE_EXCLUSIVE:
6386 case STGM_FAILIFTHERE:
6393 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6395 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6399 * STGM_CREATE | STGM_CONVERT
6400 * if both are false, STGM_FAILIFTHERE is set to TRUE
6402 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6406 * STGM_NOSCRATCH requires STGM_TRANSACTED
6408 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6412 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6413 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6415 if ( (stgm & STGM_NOSNAPSHOT) &&
6416 (!(stgm & STGM_TRANSACTED) ||
6417 share == STGM_SHARE_EXCLUSIVE ||
6418 share == STGM_SHARE_DENY_WRITE) )
6424 /****************************************************************************
6425 * GetShareModeFromSTGM
6427 * This method will return a share mode flag from a STGM value.
6428 * The STGM value is assumed valid.
6430 static DWORD GetShareModeFromSTGM(DWORD stgm)
6432 switch (STGM_SHARE_MODE(stgm))
6434 case STGM_SHARE_DENY_NONE:
6435 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6436 case STGM_SHARE_DENY_READ:
6437 return FILE_SHARE_WRITE;
6438 case STGM_SHARE_DENY_WRITE:
6439 return FILE_SHARE_READ;
6440 case STGM_SHARE_EXCLUSIVE:
6443 ERR("Invalid share mode!\n");
6448 /****************************************************************************
6449 * GetAccessModeFromSTGM
6451 * This method will return an access mode flag from a STGM value.
6452 * The STGM value is assumed valid.
6454 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6456 switch (STGM_ACCESS_MODE(stgm))
6459 return GENERIC_READ;
6461 case STGM_READWRITE:
6462 return GENERIC_READ | GENERIC_WRITE;
6464 ERR("Invalid access mode!\n");
6469 /****************************************************************************
6470 * GetCreationModeFromSTGM
6472 * This method will return a creation mode flag from a STGM value.
6473 * The STGM value is assumed valid.
6475 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6477 switch(STGM_CREATE_MODE(stgm))
6480 return CREATE_ALWAYS;
6482 FIXME("STGM_CONVERT not implemented!\n");
6484 case STGM_FAILIFTHERE:
6487 ERR("Invalid create mode!\n");
6493 /*************************************************************************
6494 * OLECONVERT_LoadOLE10 [Internal]
6496 * Loads the OLE10 STREAM to memory
6499 * pOleStream [I] The OLESTREAM
6500 * pData [I] Data Structure for the OLESTREAM Data
6504 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6505 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
6508 * This function is used by OleConvertOLESTREAMToIStorage only.
6510 * Memory allocated for pData must be freed by the caller
6512 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6515 HRESULT hRes = S_OK;
6519 pData->pData = NULL;
6520 pData->pstrOleObjFileName = NULL;
6522 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6525 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6526 if(dwSize != sizeof(pData->dwOleID))
6528 hRes = CONVERT10_E_OLESTREAM_GET;
6530 else if(pData->dwOleID != OLESTREAM_ID)
6532 hRes = CONVERT10_E_OLESTREAM_FMT;
6543 /* Get the TypeID... more info needed for this field */
6544 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6545 if(dwSize != sizeof(pData->dwTypeID))
6547 hRes = CONVERT10_E_OLESTREAM_GET;
6552 if(pData->dwTypeID != 0)
6554 /* Get the length of the OleTypeName */
6555 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6556 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6558 hRes = CONVERT10_E_OLESTREAM_GET;
6563 if(pData->dwOleTypeNameLength > 0)
6565 /* Get the OleTypeName */
6566 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
6567 if(dwSize != pData->dwOleTypeNameLength)
6569 hRes = CONVERT10_E_OLESTREAM_GET;
6575 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6576 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6578 hRes = CONVERT10_E_OLESTREAM_GET;
6582 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6583 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6584 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6585 if(pData->pstrOleObjFileName)
6587 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
6588 if(dwSize != pData->dwOleObjFileNameLength)
6590 hRes = CONVERT10_E_OLESTREAM_GET;
6594 hRes = CONVERT10_E_OLESTREAM_GET;
6599 /* Get the Width of the Metafile */
6600 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6601 if(dwSize != sizeof(pData->dwMetaFileWidth))
6603 hRes = CONVERT10_E_OLESTREAM_GET;
6607 /* Get the Height of the Metafile */
6608 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6609 if(dwSize != sizeof(pData->dwMetaFileHeight))
6611 hRes = CONVERT10_E_OLESTREAM_GET;
6617 /* Get the Length of the Data */
6618 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6619 if(dwSize != sizeof(pData->dwDataLength))
6621 hRes = CONVERT10_E_OLESTREAM_GET;
6625 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
6627 if(!bStrem1) /* if it is a second OLE stream data */
6629 pData->dwDataLength -= 8;
6630 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
6631 if(dwSize != sizeof(pData->strUnknown))
6633 hRes = CONVERT10_E_OLESTREAM_GET;
6639 if(pData->dwDataLength > 0)
6641 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6643 /* Get Data (ex. IStorage, Metafile, or BMP) */
6646 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6647 if(dwSize != pData->dwDataLength)
6649 hRes = CONVERT10_E_OLESTREAM_GET;
6654 hRes = CONVERT10_E_OLESTREAM_GET;
6663 /*************************************************************************
6664 * OLECONVERT_SaveOLE10 [Internal]
6666 * Saves the OLE10 STREAM From memory
6669 * pData [I] Data Structure for the OLESTREAM Data
6670 * pOleStream [I] The OLESTREAM to save
6674 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6677 * This function is used by OleConvertIStorageToOLESTREAM only.
6680 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6683 HRESULT hRes = S_OK;
6687 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6688 if(dwSize != sizeof(pData->dwOleID))
6690 hRes = CONVERT10_E_OLESTREAM_PUT;
6695 /* Set the TypeID */
6696 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6697 if(dwSize != sizeof(pData->dwTypeID))
6699 hRes = CONVERT10_E_OLESTREAM_PUT;
6703 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6705 /* Set the Length of the OleTypeName */
6706 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6707 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6709 hRes = CONVERT10_E_OLESTREAM_PUT;
6714 if(pData->dwOleTypeNameLength > 0)
6716 /* Set the OleTypeName */
6717 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
6718 if(dwSize != pData->dwOleTypeNameLength)
6720 hRes = CONVERT10_E_OLESTREAM_PUT;
6727 /* Set the width of the Metafile */
6728 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6729 if(dwSize != sizeof(pData->dwMetaFileWidth))
6731 hRes = CONVERT10_E_OLESTREAM_PUT;
6737 /* Set the height of the Metafile */
6738 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6739 if(dwSize != sizeof(pData->dwMetaFileHeight))
6741 hRes = CONVERT10_E_OLESTREAM_PUT;
6747 /* Set the length of the Data */
6748 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6749 if(dwSize != sizeof(pData->dwDataLength))
6751 hRes = CONVERT10_E_OLESTREAM_PUT;
6757 if(pData->dwDataLength > 0)
6759 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6760 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6761 if(dwSize != pData->dwDataLength)
6763 hRes = CONVERT10_E_OLESTREAM_PUT;
6771 /*************************************************************************
6772 * OLECONVERT_GetOLE20FromOLE10[Internal]
6774 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6775 * opens it, and copies the content to the dest IStorage for
6776 * OleConvertOLESTREAMToIStorage
6780 * pDestStorage [I] The IStorage to copy the data to
6781 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6782 * nBufferLength [I] The size of the buffer
6791 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
6795 IStorage *pTempStorage;
6796 DWORD dwNumOfBytesWritten;
6797 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6798 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6800 /* Create a temp File */
6801 GetTempPathW(MAX_PATH, wstrTempDir);
6802 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6803 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6805 if(hFile != INVALID_HANDLE_VALUE)
6807 /* Write IStorage Data to File */
6808 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6811 /* Open and copy temp storage to the Dest Storage */
6812 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6815 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6816 StorageBaseImpl_Release(pTempStorage);
6818 DeleteFileW(wstrTempFile);
6823 /*************************************************************************
6824 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6826 * Saves the OLE10 STREAM From memory
6829 * pStorage [I] The Src IStorage to copy
6830 * pData [I] The Dest Memory to write to.
6833 * The size in bytes allocated for pData
6836 * Memory allocated for pData must be freed by the caller
6838 * Used by OleConvertIStorageToOLESTREAM only.
6841 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6845 DWORD nDataLength = 0;
6846 IStorage *pTempStorage;
6847 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6848 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6852 /* Create temp Storage */
6853 GetTempPathW(MAX_PATH, wstrTempDir);
6854 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6855 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6859 /* Copy Src Storage to the Temp Storage */
6860 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6861 StorageBaseImpl_Release(pTempStorage);
6863 /* Open Temp Storage as a file and copy to memory */
6864 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6865 if(hFile != INVALID_HANDLE_VALUE)
6867 nDataLength = GetFileSize(hFile, NULL);
6868 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6869 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6872 DeleteFileW(wstrTempFile);
6877 /*************************************************************************
6878 * OLECONVERT_CreateOleStream [Internal]
6880 * Creates the "\001OLE" stream in the IStorage if necessary.
6883 * pStorage [I] Dest storage to create the stream in
6889 * This function is used by OleConvertOLESTREAMToIStorage only.
6891 * This stream is still unknown, MS Word seems to have extra data
6892 * but since the data is stored in the OLESTREAM there should be
6893 * no need to recreate the stream. If the stream is manually
6894 * deleted it will create it with this default data.
6897 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6901 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6902 BYTE pOleStreamHeader [] =
6904 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6905 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6906 0x00, 0x00, 0x00, 0x00
6909 /* Create stream if not present */
6910 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6911 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6915 /* Write default Data */
6916 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6917 IStream_Release(pStream);
6921 /* write a string to a stream, preceded by its length */
6922 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6929 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6930 r = IStream_Write( stm, &len, sizeof(len), NULL);
6935 str = CoTaskMemAlloc( len );
6936 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6937 r = IStream_Write( stm, str, len, NULL);
6938 CoTaskMemFree( str );
6942 /* read a string preceded by its length from a stream */
6943 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6946 DWORD len, count = 0;
6950 r = IStream_Read( stm, &len, sizeof(len), &count );
6953 if( count != sizeof(len) )
6954 return E_OUTOFMEMORY;
6956 TRACE("%d bytes\n",len);
6958 str = CoTaskMemAlloc( len );
6960 return E_OUTOFMEMORY;
6962 r = IStream_Read( stm, str, len, &count );
6967 CoTaskMemFree( str );
6968 return E_OUTOFMEMORY;
6971 TRACE("Read string %s\n",debugstr_an(str,len));
6973 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
6974 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
6976 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
6977 CoTaskMemFree( str );
6985 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
6986 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
6990 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6992 static const BYTE unknown1[12] =
6993 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6994 0xFF, 0xFF, 0xFF, 0xFF};
6995 static const BYTE unknown2[16] =
6996 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6997 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6999 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7000 debugstr_w(lpszUserType), debugstr_w(szClipName),
7001 debugstr_w(szProgIDName));
7003 /* Create a CompObj stream */
7004 r = IStorage_CreateStream(pstg, szwStreamName,
7005 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7009 /* Write CompObj Structure to stream */
7010 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7012 if( SUCCEEDED( r ) )
7013 r = WriteClassStm( pstm, clsid );
7015 if( SUCCEEDED( r ) )
7016 r = STREAM_WriteString( pstm, lpszUserType );
7017 if( SUCCEEDED( r ) )
7018 r = STREAM_WriteString( pstm, szClipName );
7019 if( SUCCEEDED( r ) )
7020 r = STREAM_WriteString( pstm, szProgIDName );
7021 if( SUCCEEDED( r ) )
7022 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7024 IStream_Release( pstm );
7029 /***********************************************************************
7030 * WriteFmtUserTypeStg (OLE32.@)
7032 HRESULT WINAPI WriteFmtUserTypeStg(
7033 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7036 WCHAR szwClipName[0x40];
7037 CLSID clsid = CLSID_NULL;
7038 LPWSTR wstrProgID = NULL;
7041 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7043 /* get the clipboard format name */
7044 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
7047 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7049 /* FIXME: There's room to save a CLSID and its ProgID, but
7050 the CLSID is not looked up in the registry and in all the
7051 tests I wrote it was CLSID_NULL. Where does it come from?
7054 /* get the real program ID. This may fail, but that's fine */
7055 ProgIDFromCLSID(&clsid, &wstrProgID);
7057 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7059 r = STORAGE_WriteCompObj( pstg, &clsid,
7060 lpszUserType, szwClipName, wstrProgID );
7062 CoTaskMemFree(wstrProgID);
7068 /******************************************************************************
7069 * ReadFmtUserTypeStg [OLE32.@]
7071 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7075 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7076 unsigned char unknown1[12];
7077 unsigned char unknown2[16];
7079 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7082 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7084 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7085 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7088 WARN("Failed to open stream r = %08x\n", r);
7092 /* read the various parts of the structure */
7093 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7094 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7096 r = ReadClassStm( stm, &clsid );
7100 r = STREAM_ReadString( stm, &szCLSIDName );
7104 r = STREAM_ReadString( stm, &szOleTypeName );
7108 r = STREAM_ReadString( stm, &szProgIDName );
7112 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7113 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7116 /* ok, success... now we just need to store what we found */
7118 *pcf = RegisterClipboardFormatW( szOleTypeName );
7119 CoTaskMemFree( szOleTypeName );
7121 if( lplpszUserType )
7122 *lplpszUserType = szCLSIDName;
7123 CoTaskMemFree( szProgIDName );
7126 IStream_Release( stm );
7132 /*************************************************************************
7133 * OLECONVERT_CreateCompObjStream [Internal]
7135 * Creates a "\001CompObj" is the destination IStorage if necessary.
7138 * pStorage [I] The dest IStorage to create the CompObj Stream
7140 * strOleTypeName [I] The ProgID
7144 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7147 * This function is used by OleConvertOLESTREAMToIStorage only.
7149 * The stream data is stored in the OLESTREAM and there should be
7150 * no need to recreate the stream. If the stream is manually
7151 * deleted it will attempt to create it by querying the registry.
7155 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7158 HRESULT hStorageRes, hRes = S_OK;
7159 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7160 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7161 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7163 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7164 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7166 /* Initialize the CompObj structure */
7167 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7168 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
7169 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
7172 /* Create a CompObj stream if it doesn't exist */
7173 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7174 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7175 if(hStorageRes == S_OK)
7177 /* copy the OleTypeName to the compobj struct */
7178 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7179 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7181 /* copy the OleTypeName to the compobj struct */
7182 /* Note: in the test made, these were Identical */
7183 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7184 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7187 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7188 bufferW, OLESTREAM_MAX_STR_LEN );
7189 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7195 /* Get the CLSID Default Name from the Registry */
7196 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7197 if(hErr == ERROR_SUCCESS)
7199 char strTemp[OLESTREAM_MAX_STR_LEN];
7200 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7201 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7202 if(hErr == ERROR_SUCCESS)
7204 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7210 /* Write CompObj Structure to stream */
7211 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7213 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7215 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7216 if(IStorageCompObj.dwCLSIDNameLength > 0)
7218 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7220 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7221 if(IStorageCompObj.dwOleTypeNameLength > 0)
7223 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7225 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7226 if(IStorageCompObj.dwProgIDNameLength > 0)
7228 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7230 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7231 IStream_Release(pStream);
7237 /*************************************************************************
7238 * OLECONVERT_CreateOlePresStream[Internal]
7240 * Creates the "\002OlePres000" Stream with the Metafile data
7243 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7244 * dwExtentX [I] Width of the Metafile
7245 * dwExtentY [I] Height of the Metafile
7246 * pData [I] Metafile data
7247 * dwDataLength [I] Size of the Metafile data
7251 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7254 * This function is used by OleConvertOLESTREAMToIStorage only.
7257 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7261 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7262 BYTE pOlePresStreamHeader [] =
7264 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7265 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7266 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7267 0x00, 0x00, 0x00, 0x00
7270 BYTE pOlePresStreamHeaderEmpty [] =
7272 0x00, 0x00, 0x00, 0x00,
7273 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7274 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7275 0x00, 0x00, 0x00, 0x00
7278 /* Create the OlePres000 Stream */
7279 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7280 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7285 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7287 memset(&OlePres, 0, sizeof(OlePres));
7288 /* Do we have any metafile data to save */
7289 if(dwDataLength > 0)
7291 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7292 nHeaderSize = sizeof(pOlePresStreamHeader);
7296 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7297 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7299 /* Set width and height of the metafile */
7300 OlePres.dwExtentX = dwExtentX;
7301 OlePres.dwExtentY = -dwExtentY;
7303 /* Set Data and Length */
7304 if(dwDataLength > sizeof(METAFILEPICT16))
7306 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7307 OlePres.pData = &(pData[8]);
7309 /* Save OlePres000 Data to Stream */
7310 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7311 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7312 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7313 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7314 if(OlePres.dwSize > 0)
7316 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7318 IStream_Release(pStream);
7322 /*************************************************************************
7323 * OLECONVERT_CreateOle10NativeStream [Internal]
7325 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7328 * pStorage [I] Dest storage to create the stream in
7329 * pData [I] Ole10 Native Data (ex. bmp)
7330 * dwDataLength [I] Size of the Ole10 Native Data
7336 * This function is used by OleConvertOLESTREAMToIStorage only.
7338 * Might need to verify the data and return appropriate error message
7341 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
7345 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7347 /* Create the Ole10Native Stream */
7348 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7349 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7353 /* Write info to stream */
7354 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7355 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7356 IStream_Release(pStream);
7361 /*************************************************************************
7362 * OLECONVERT_GetOLE10ProgID [Internal]
7364 * Finds the ProgID (or OleTypeID) from the IStorage
7367 * pStorage [I] The Src IStorage to get the ProgID
7368 * strProgID [I] the ProgID string to get
7369 * dwSize [I] the size of the string
7373 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7376 * This function is used by OleConvertIStorageToOLESTREAM only.
7380 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7384 LARGE_INTEGER iSeekPos;
7385 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7386 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7388 /* Open the CompObj Stream */
7389 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7390 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7394 /*Get the OleType from the CompObj Stream */
7395 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7396 iSeekPos.u.HighPart = 0;
7398 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7399 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7400 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7401 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7402 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7403 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7404 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7406 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7409 IStream_Read(pStream, strProgID, *dwSize, NULL);
7411 IStream_Release(pStream);
7416 LPOLESTR wstrProgID;
7418 /* Get the OleType from the registry */
7419 REFCLSID clsid = &(stat.clsid);
7420 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7421 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7424 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7431 /*************************************************************************
7432 * OLECONVERT_GetOle10PresData [Internal]
7434 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7437 * pStorage [I] Src IStroage
7438 * pOleStream [I] Dest OleStream Mem Struct
7444 * This function is used by OleConvertIStorageToOLESTREAM only.
7446 * Memory allocated for pData must be freed by the caller
7450 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7455 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7457 /* Initialize Default data for OLESTREAM */
7458 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7459 pOleStreamData[0].dwTypeID = 2;
7460 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7461 pOleStreamData[1].dwTypeID = 0;
7462 pOleStreamData[0].dwMetaFileWidth = 0;
7463 pOleStreamData[0].dwMetaFileHeight = 0;
7464 pOleStreamData[0].pData = NULL;
7465 pOleStreamData[1].pData = NULL;
7467 /* Open Ole10Native Stream */
7468 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7469 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7473 /* Read Size and Data */
7474 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7475 if(pOleStreamData->dwDataLength > 0)
7477 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7478 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7480 IStream_Release(pStream);
7486 /*************************************************************************
7487 * OLECONVERT_GetOle20PresData[Internal]
7489 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7492 * pStorage [I] Src IStroage
7493 * pOleStreamData [I] Dest OleStream Mem Struct
7499 * This function is used by OleConvertIStorageToOLESTREAM only.
7501 * Memory allocated for pData must be freed by the caller
7503 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7507 OLECONVERT_ISTORAGE_OLEPRES olePress;
7508 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7510 /* Initialize Default data for OLESTREAM */
7511 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7512 pOleStreamData[0].dwTypeID = 2;
7513 pOleStreamData[0].dwMetaFileWidth = 0;
7514 pOleStreamData[0].dwMetaFileHeight = 0;
7515 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7516 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7517 pOleStreamData[1].dwTypeID = 0;
7518 pOleStreamData[1].dwOleTypeNameLength = 0;
7519 pOleStreamData[1].strOleTypeName[0] = 0;
7520 pOleStreamData[1].dwMetaFileWidth = 0;
7521 pOleStreamData[1].dwMetaFileHeight = 0;
7522 pOleStreamData[1].pData = NULL;
7523 pOleStreamData[1].dwDataLength = 0;
7526 /* Open OlePress000 stream */
7527 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7528 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7531 LARGE_INTEGER iSeekPos;
7532 METAFILEPICT16 MetaFilePict;
7533 static const char strMetafilePictName[] = "METAFILEPICT";
7535 /* Set the TypeID for a Metafile */
7536 pOleStreamData[1].dwTypeID = 5;
7538 /* Set the OleTypeName to Metafile */
7539 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7540 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7542 iSeekPos.u.HighPart = 0;
7543 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7545 /* Get Presentation Data */
7546 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7547 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7548 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7549 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7551 /*Set width and Height */
7552 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7553 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7554 if(olePress.dwSize > 0)
7557 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7559 /* Set MetaFilePict struct */
7560 MetaFilePict.mm = 8;
7561 MetaFilePict.xExt = olePress.dwExtentX;
7562 MetaFilePict.yExt = olePress.dwExtentY;
7563 MetaFilePict.hMF = 0;
7565 /* Get Metafile Data */
7566 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7567 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7568 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7570 IStream_Release(pStream);
7574 /*************************************************************************
7575 * OleConvertOLESTREAMToIStorage [OLE32.@]
7580 * DVTARGETDEVICE parameter is not handled
7581 * Still unsure of some mem fields for OLE 10 Stream
7582 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7583 * and "\001OLE" streams
7586 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7587 LPOLESTREAM pOleStream,
7589 const DVTARGETDEVICE* ptd)
7593 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7595 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
7597 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7601 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7604 if(pstg == NULL || pOleStream == NULL)
7606 hRes = E_INVALIDARG;
7611 /* Load the OLESTREAM to Memory */
7612 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7617 /* Load the OLESTREAM to Memory (part 2)*/
7618 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7624 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7626 /* Do we have the IStorage Data in the OLESTREAM */
7627 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7629 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7630 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7634 /* It must be an original OLE 1.0 source */
7635 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7640 /* It must be an original OLE 1.0 source */
7641 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7644 /* Create CompObj Stream if necessary */
7645 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7648 /*Create the Ole Stream if necessary */
7649 OLECONVERT_CreateOleStream(pstg);
7654 /* Free allocated memory */
7655 for(i=0; i < 2; i++)
7657 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7658 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7659 pOleStreamData[i].pstrOleObjFileName = NULL;
7664 /*************************************************************************
7665 * OleConvertIStorageToOLESTREAM [OLE32.@]
7672 * Still unsure of some mem fields for OLE 10 Stream
7673 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7674 * and "\001OLE" streams.
7677 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7679 LPOLESTREAM pOleStream)
7682 HRESULT hRes = S_OK;
7684 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7685 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7687 TRACE("%p %p\n", pstg, pOleStream);
7689 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7691 if(pstg == NULL || pOleStream == NULL)
7693 hRes = E_INVALIDARG;
7697 /* Get the ProgID */
7698 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7699 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7703 /* Was it originally Ole10 */
7704 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7707 IStream_Release(pStream);
7708 /* Get Presentation Data for Ole10Native */
7709 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7713 /* Get Presentation Data (OLE20) */
7714 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7717 /* Save OLESTREAM */
7718 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7721 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7726 /* Free allocated memory */
7727 for(i=0; i < 2; i++)
7729 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7735 /***********************************************************************
7736 * GetConvertStg (OLE32.@)
7738 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7739 FIXME("unimplemented stub!\n");
7743 /******************************************************************************
7744 * StgIsStorageFile [OLE32.@]
7745 * Verify if the file contains a storage object
7751 * S_OK if file has magic bytes as a storage object
7752 * S_FALSE if file is not storage
7755 StgIsStorageFile(LPCOLESTR fn)
7761 TRACE("%s\n", debugstr_w(fn));
7762 hf = CreateFileW(fn, GENERIC_READ,
7763 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7764 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7766 if (hf == INVALID_HANDLE_VALUE)
7767 return STG_E_FILENOTFOUND;
7769 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7771 WARN(" unable to read file\n");
7778 if (bytes_read != 8) {
7779 WARN(" too short\n");
7783 if (!memcmp(magic,STORAGE_magic,8)) {
7788 WARN(" -> Invalid header.\n");
7792 /***********************************************************************
7793 * WriteClassStm (OLE32.@)
7795 * Writes a CLSID to a stream.
7798 * pStm [I] Stream to write to.
7799 * rclsid [I] CLSID to write.
7803 * Failure: HRESULT code.
7805 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
7807 TRACE("(%p,%p)\n",pStm,rclsid);
7809 if (!pStm || !rclsid)
7810 return E_INVALIDARG;
7812 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
7815 /***********************************************************************
7816 * ReadClassStm (OLE32.@)
7818 * Reads a CLSID from a stream.
7821 * pStm [I] Stream to read from.
7822 * rclsid [O] CLSID to read.
7826 * Failure: HRESULT code.
7828 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
7833 TRACE("(%p,%p)\n",pStm,pclsid);
7835 if (!pStm || !pclsid)
7836 return E_INVALIDARG;
7838 /* clear the output args */
7839 *pclsid = CLSID_NULL;
7841 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
7846 if (nbByte != sizeof(CLSID))
7847 return STG_E_READFAULT;