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);
202 /***********************************************************************
203 * Declaration of miscellaneous functions...
205 static HRESULT validateSTGM(DWORD stgmValue);
207 static DWORD GetShareModeFromSTGM(DWORD stgm);
208 static DWORD GetAccessModeFromSTGM(DWORD stgm);
209 static DWORD GetCreationModeFromSTGM(DWORD stgm);
211 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
214 /****************************************************************************
215 * IEnumSTATSTGImpl definitions.
217 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
218 * This class allows iterating through the content of a storage and to find
219 * specific items inside it.
221 struct IEnumSTATSTGImpl
223 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
224 * since we want to cast this in an IEnumSTATSTG pointer */
226 LONG ref; /* Reference count */
227 StorageImpl* parentStorage; /* Reference to the parent storage */
228 ULONG firstPropertyNode; /* Index of the root of the storage to enumerate */
231 * The current implementation of the IEnumSTATSTGImpl class uses a stack
232 * to walk the property sets to get the content of a storage. This stack
233 * is implemented by the following 3 data members
239 #define ENUMSTATSGT_SIZE_INCREMENT 10
243 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageImpl* This, ULONG firstPropertyNode);
244 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
245 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl* This, ULONG nodeToPush);
246 static ULONG IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl* This, BOOL remove);
247 static ULONG IEnumSTATSTGImpl_FindProperty(IEnumSTATSTGImpl* This, const OLECHAR* lpszPropName,
248 StgProperty* buffer);
249 static INT IEnumSTATSTGImpl_FindParentProperty(IEnumSTATSTGImpl *This, ULONG childProperty,
250 StgProperty *currentProperty, ULONG *propertyId);
252 /************************************************************************
256 static ULONG BLOCK_GetBigBlockOffset(ULONG index)
258 if (index == 0xffffffff)
263 return index * BIG_BLOCK_SIZE;
266 /************************************************************************
267 ** Storage32BaseImpl implementation
269 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
270 ULARGE_INTEGER offset,
275 return BIGBLOCKFILE_ReadAt(This->bigBlockFile,offset,buffer,size,bytesRead);
278 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
279 ULARGE_INTEGER offset,
284 return BIGBLOCKFILE_WriteAt(This->bigBlockFile,offset,buffer,size,bytesWritten);
287 /************************************************************************
288 * Storage32BaseImpl_QueryInterface (IUnknown)
290 * This method implements the common QueryInterface for all IStorage32
291 * implementations contained in this file.
293 * See Windows documentation for more details on IUnknown methods.
295 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
300 StorageBaseImpl *This = (StorageBaseImpl *)iface;
302 * Perform a sanity check on the parameters.
304 if ( (This==0) || (ppvObject==0) )
308 * Initialize the return parameter.
313 * Compare the riid with the interface IDs implemented by this object.
315 if (IsEqualGUID(&IID_IUnknown, riid) ||
316 IsEqualGUID(&IID_IStorage, riid))
320 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
322 *ppvObject = &This->pssVtbl;
326 * Check that we obtained an interface.
329 return E_NOINTERFACE;
332 * Query Interface always increases the reference count by one when it is
335 IStorage_AddRef(iface);
340 /************************************************************************
341 * Storage32BaseImpl_AddRef (IUnknown)
343 * This method implements the common AddRef for all IStorage32
344 * implementations contained in this file.
346 * See Windows documentation for more details on IUnknown methods.
348 static ULONG WINAPI StorageBaseImpl_AddRef(
351 StorageBaseImpl *This = (StorageBaseImpl *)iface;
352 ULONG ref = InterlockedIncrement(&This->ref);
354 TRACE("(%p) AddRef to %d\n", This, ref);
359 /************************************************************************
360 * Storage32BaseImpl_Release (IUnknown)
362 * This method implements the common Release for all IStorage32
363 * implementations contained in this file.
365 * See Windows documentation for more details on IUnknown methods.
367 static ULONG WINAPI StorageBaseImpl_Release(
370 StorageBaseImpl *This = (StorageBaseImpl *)iface;
372 * Decrease the reference count on this object.
374 ULONG ref = InterlockedDecrement(&This->ref);
376 TRACE("(%p) ReleaseRef to %d\n", This, ref);
379 * If the reference count goes down to 0, perform suicide.
384 * Since we are using a system of base-classes, we want to call the
385 * destructor of the appropriate derived class. To do this, we are
386 * using virtual functions to implement the destructor.
388 This->v_destructor(This);
394 /************************************************************************
395 * Storage32BaseImpl_OpenStream (IStorage)
397 * This method will open the specified stream object from the current storage.
399 * See Windows documentation for more details on IStorage methods.
401 static HRESULT WINAPI StorageBaseImpl_OpenStream(
403 const OLECHAR* pwcsName, /* [string][in] */
404 void* reserved1, /* [unique][in] */
405 DWORD grfMode, /* [in] */
406 DWORD reserved2, /* [in] */
407 IStream** ppstm) /* [out] */
409 StorageBaseImpl *This = (StorageBaseImpl *)iface;
410 IEnumSTATSTGImpl* propertyEnumeration;
411 StgStreamImpl* newStream;
412 StgProperty currentProperty;
413 ULONG foundPropertyIndex;
414 HRESULT res = STG_E_UNKNOWN;
416 TRACE("(%p, %s, %p, %x, %d, %p)\n",
417 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
420 * Perform a sanity check on the parameters.
422 if ( (pwcsName==NULL) || (ppstm==0) )
429 * Initialize the out parameter
434 * Validate the STGM flags
436 if ( FAILED( validateSTGM(grfMode) ) ||
437 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
439 res = STG_E_INVALIDFLAG;
446 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
448 res = STG_E_INVALIDFUNCTION;
453 * Check that we're compatible with the parent's storage mode, but
454 * only if we are not in transacted mode
456 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
457 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
459 res = STG_E_ACCESSDENIED;
465 * Create a property enumeration to search the properties
467 propertyEnumeration = IEnumSTATSTGImpl_Construct(
468 This->ancestorStorage,
469 This->rootPropertySetIndex);
472 * Search the enumeration for the property with the given name
474 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
480 * Delete the property enumeration since we don't need it anymore
482 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
485 * If it was found, construct the stream object and return a pointer to it.
487 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
488 (currentProperty.propertyType==PROPTYPE_STREAM) )
490 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
494 newStream->grfMode = grfMode;
495 *ppstm = (IStream*)newStream;
498 * Since we are returning a pointer to the interface, we have to
499 * nail down the reference.
501 IStream_AddRef(*ppstm);
511 res = STG_E_FILENOTFOUND;
515 TRACE("<-- IStream %p\n", *ppstm);
516 TRACE("<-- %08x\n", res);
520 /************************************************************************
521 * Storage32BaseImpl_OpenStorage (IStorage)
523 * This method will open a new storage object from the current storage.
525 * See Windows documentation for more details on IStorage methods.
527 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
529 const OLECHAR* pwcsName, /* [string][unique][in] */
530 IStorage* pstgPriority, /* [unique][in] */
531 DWORD grfMode, /* [in] */
532 SNB snbExclude, /* [unique][in] */
533 DWORD reserved, /* [in] */
534 IStorage** ppstg) /* [out] */
536 StorageBaseImpl *This = (StorageBaseImpl *)iface;
537 StorageInternalImpl* newStorage;
538 IEnumSTATSTGImpl* propertyEnumeration;
539 StgProperty currentProperty;
540 ULONG foundPropertyIndex;
541 HRESULT res = STG_E_UNKNOWN;
543 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
544 iface, debugstr_w(pwcsName), pstgPriority,
545 grfMode, snbExclude, reserved, ppstg);
548 * Perform a sanity check on the parameters.
550 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
557 if (snbExclude != NULL)
559 res = STG_E_INVALIDPARAMETER;
564 * Validate the STGM flags
566 if ( FAILED( validateSTGM(grfMode) ))
568 res = STG_E_INVALIDFLAG;
575 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
576 (grfMode & STGM_DELETEONRELEASE) ||
577 (grfMode & STGM_PRIORITY) )
579 res = STG_E_INVALIDFUNCTION;
584 * Check that we're compatible with the parent's storage mode,
585 * but only if we are not transacted
587 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
588 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
590 res = STG_E_ACCESSDENIED;
596 * Initialize the out parameter
601 * Create a property enumeration to search the properties
603 propertyEnumeration = IEnumSTATSTGImpl_Construct(
604 This->ancestorStorage,
605 This->rootPropertySetIndex);
608 * Search the enumeration for the property with the given name
610 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
616 * Delete the property enumeration since we don't need it anymore
618 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
621 * If it was found, construct the stream object and return a pointer to it.
623 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
624 (currentProperty.propertyType==PROPTYPE_STORAGE) )
627 * Construct a new Storage object
629 newStorage = StorageInternalImpl_Construct(
630 This->ancestorStorage,
636 *ppstg = (IStorage*)newStorage;
639 * Since we are returning a pointer to the interface,
640 * we have to nail down the reference.
642 StorageBaseImpl_AddRef(*ppstg);
648 res = STG_E_INSUFFICIENTMEMORY;
652 res = STG_E_FILENOTFOUND;
655 TRACE("<-- %08x\n", res);
659 /************************************************************************
660 * Storage32BaseImpl_EnumElements (IStorage)
662 * This method will create an enumerator object that can be used to
663 * retrieve information about all the properties in the storage object.
665 * See Windows documentation for more details on IStorage methods.
667 static HRESULT WINAPI StorageBaseImpl_EnumElements(
669 DWORD reserved1, /* [in] */
670 void* reserved2, /* [size_is][unique][in] */
671 DWORD reserved3, /* [in] */
672 IEnumSTATSTG** ppenum) /* [out] */
674 StorageBaseImpl *This = (StorageBaseImpl *)iface;
675 IEnumSTATSTGImpl* newEnum;
677 TRACE("(%p, %d, %p, %d, %p)\n",
678 iface, reserved1, reserved2, reserved3, ppenum);
681 * Perform a sanity check on the parameters.
683 if ( (This==0) || (ppenum==0))
687 * Construct the enumerator.
689 newEnum = IEnumSTATSTGImpl_Construct(
690 This->ancestorStorage,
691 This->rootPropertySetIndex);
695 *ppenum = (IEnumSTATSTG*)newEnum;
698 * Don't forget to nail down a reference to the new object before
701 IEnumSTATSTG_AddRef(*ppenum);
706 return E_OUTOFMEMORY;
709 /************************************************************************
710 * Storage32BaseImpl_Stat (IStorage)
712 * This method will retrieve information about this storage object.
714 * See Windows documentation for more details on IStorage methods.
716 static HRESULT WINAPI StorageBaseImpl_Stat(
718 STATSTG* pstatstg, /* [out] */
719 DWORD grfStatFlag) /* [in] */
721 StorageBaseImpl *This = (StorageBaseImpl *)iface;
722 StgProperty curProperty;
724 HRESULT res = STG_E_UNKNOWN;
726 TRACE("(%p, %p, %x)\n",
727 iface, pstatstg, grfStatFlag);
730 * Perform a sanity check on the parameters.
732 if ( (This==0) || (pstatstg==0))
739 * Read the information from the property.
741 readSuccessful = StorageImpl_ReadProperty(
742 This->ancestorStorage,
743 This->rootPropertySetIndex,
748 StorageUtl_CopyPropertyToSTATSTG(
753 pstatstg->grfMode = This->openFlags;
754 pstatstg->grfStateBits = This->stateBits;
765 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);
767 TRACE("<-- %08x\n", res);
771 /************************************************************************
772 * Storage32BaseImpl_RenameElement (IStorage)
774 * This method will rename the specified element.
776 * See Windows documentation for more details on IStorage methods.
778 * Implementation notes: The method used to rename consists of creating a clone
779 * of the deleted StgProperty object setting it with the new name and to
780 * perform a DestroyElement of the old StgProperty.
782 static HRESULT WINAPI StorageBaseImpl_RenameElement(
784 const OLECHAR* pwcsOldName, /* [in] */
785 const OLECHAR* pwcsNewName) /* [in] */
787 StorageBaseImpl *This = (StorageBaseImpl *)iface;
788 IEnumSTATSTGImpl* propertyEnumeration;
789 StgProperty currentProperty;
790 ULONG foundPropertyIndex;
792 TRACE("(%p, %s, %s)\n",
793 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
796 * Create a property enumeration to search the properties
798 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
799 This->rootPropertySetIndex);
802 * Search the enumeration for the new property name
804 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
808 if (foundPropertyIndex != PROPERTY_NULL)
811 * There is already a property with the new name
813 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
814 return STG_E_FILEALREADYEXISTS;
817 IEnumSTATSTG_Reset((IEnumSTATSTG*)propertyEnumeration);
820 * Search the enumeration for the old property name
822 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
827 * Delete the property enumeration since we don't need it anymore
829 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
831 if (foundPropertyIndex != PROPERTY_NULL)
833 StgProperty renamedProperty;
834 ULONG renamedPropertyIndex;
837 * Setup a new property for the renamed property
839 renamedProperty.sizeOfNameString =
840 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
842 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
843 return STG_E_INVALIDNAME;
845 strcpyW(renamedProperty.name, pwcsNewName);
847 renamedProperty.propertyType = currentProperty.propertyType;
848 renamedProperty.startingBlock = currentProperty.startingBlock;
849 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
850 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
852 renamedProperty.previousProperty = PROPERTY_NULL;
853 renamedProperty.nextProperty = PROPERTY_NULL;
856 * Bring the dirProperty link in case it is a storage and in which
857 * case the renamed storage elements don't require to be reorganized.
859 renamedProperty.dirProperty = currentProperty.dirProperty;
861 /* call CoFileTime to get the current time
862 renamedProperty.timeStampS1
863 renamedProperty.timeStampD1
864 renamedProperty.timeStampS2
865 renamedProperty.timeStampD2
866 renamedProperty.propertyUniqueID
870 * Obtain a free property in the property chain
872 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
875 * Save the new property into the new property spot
877 StorageImpl_WriteProperty(
878 This->ancestorStorage,
879 renamedPropertyIndex,
883 * Find a spot in the property chain for our newly created property.
887 renamedPropertyIndex,
891 * At this point the renamed property has been inserted in the tree,
892 * now, before Destroying the old property we must zero its dirProperty
893 * otherwise the DestroyProperty below will zap it all and we do not want
895 * Also, we fake that the old property is a storage so the DestroyProperty
896 * will not do a SetSize(0) on the stream data.
898 * This means that we need to tweak the StgProperty if it is a stream or a
901 StorageImpl_ReadProperty(This->ancestorStorage,
905 currentProperty.dirProperty = PROPERTY_NULL;
906 currentProperty.propertyType = PROPTYPE_STORAGE;
907 StorageImpl_WriteProperty(
908 This->ancestorStorage,
913 * Invoke Destroy to get rid of the ole property and automatically redo
914 * the linking of its previous and next members...
916 IStorage_DestroyElement(iface, pwcsOldName);
922 * There is no property with the old name
924 return STG_E_FILENOTFOUND;
930 /************************************************************************
931 * Storage32BaseImpl_CreateStream (IStorage)
933 * This method will create a stream object within this storage
935 * See Windows documentation for more details on IStorage methods.
937 static HRESULT WINAPI StorageBaseImpl_CreateStream(
939 const OLECHAR* pwcsName, /* [string][in] */
940 DWORD grfMode, /* [in] */
941 DWORD reserved1, /* [in] */
942 DWORD reserved2, /* [in] */
943 IStream** ppstm) /* [out] */
945 StorageBaseImpl *This = (StorageBaseImpl *)iface;
946 IEnumSTATSTGImpl* propertyEnumeration;
947 StgStreamImpl* newStream;
948 StgProperty currentProperty, newStreamProperty;
949 ULONG foundPropertyIndex, newPropertyIndex;
951 TRACE("(%p, %s, %x, %d, %d, %p)\n",
952 iface, debugstr_w(pwcsName), grfMode,
953 reserved1, reserved2, ppstm);
956 * Validate parameters
959 return STG_E_INVALIDPOINTER;
962 return STG_E_INVALIDNAME;
964 if (reserved1 || reserved2)
965 return STG_E_INVALIDPARAMETER;
968 * Validate the STGM flags
970 if ( FAILED( validateSTGM(grfMode) ))
971 return STG_E_INVALIDFLAG;
973 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
974 return STG_E_INVALIDFLAG;
979 if ((grfMode & STGM_DELETEONRELEASE) ||
980 (grfMode & STGM_TRANSACTED))
981 return STG_E_INVALIDFUNCTION;
983 /* Can't create a stream on read-only storage */
984 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
985 return STG_E_ACCESSDENIED;
988 * Check that we're compatible with the parent's storage mode
989 * if not in transacted mode
991 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
992 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
993 return STG_E_ACCESSDENIED;
996 if(This->ancestorStorage->base.openFlags & STGM_SIMPLE)
997 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
1000 * Initialize the out parameter
1005 * Create a property enumeration to search the properties
1007 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
1008 This->rootPropertySetIndex);
1010 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1014 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1016 if (foundPropertyIndex != PROPERTY_NULL)
1019 * An element with this name already exists
1021 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1023 StgStreamImpl *strm;
1025 LIST_FOR_EACH_ENTRY(strm, &This->strmHead, StgStreamImpl, StrmListEntry)
1027 if (strm->ownerProperty == foundPropertyIndex)
1029 TRACE("Stream deleted %p\n", strm);
1030 strm->parentStorage = NULL;
1031 list_remove(&strm->StrmListEntry);
1034 IStorage_DestroyElement(iface, pwcsName);
1037 return STG_E_FILEALREADYEXISTS;
1039 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1041 WARN("read-only storage\n");
1042 return STG_E_ACCESSDENIED;
1046 * memset the empty property
1048 memset(&newStreamProperty, 0, sizeof(StgProperty));
1050 newStreamProperty.sizeOfNameString =
1051 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
1053 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1054 return STG_E_INVALIDNAME;
1056 strcpyW(newStreamProperty.name, pwcsName);
1058 newStreamProperty.propertyType = PROPTYPE_STREAM;
1059 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
1060 newStreamProperty.size.u.LowPart = 0;
1061 newStreamProperty.size.u.HighPart = 0;
1063 newStreamProperty.previousProperty = PROPERTY_NULL;
1064 newStreamProperty.nextProperty = PROPERTY_NULL;
1065 newStreamProperty.dirProperty = PROPERTY_NULL;
1067 /* call CoFileTime to get the current time
1068 newStreamProperty.timeStampS1
1069 newStreamProperty.timeStampD1
1070 newStreamProperty.timeStampS2
1071 newStreamProperty.timeStampD2
1074 /* newStreamProperty.propertyUniqueID */
1077 * Get a free property or create a new one
1079 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1082 * Save the new property into the new property spot
1084 StorageImpl_WriteProperty(
1085 This->ancestorStorage,
1087 &newStreamProperty);
1090 * Find a spot in the property chain for our newly created property.
1092 updatePropertyChain(
1098 * Open the stream to return it.
1100 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
1104 *ppstm = (IStream*)newStream;
1107 * Since we are returning a pointer to the interface, we have to nail down
1110 IStream_AddRef(*ppstm);
1114 return STG_E_INSUFFICIENTMEMORY;
1120 /************************************************************************
1121 * Storage32BaseImpl_SetClass (IStorage)
1123 * This method will write the specified CLSID in the property of this
1126 * See Windows documentation for more details on IStorage methods.
1128 static HRESULT WINAPI StorageBaseImpl_SetClass(
1130 REFCLSID clsid) /* [in] */
1132 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1133 HRESULT hRes = E_FAIL;
1134 StgProperty curProperty;
1137 TRACE("(%p, %p)\n", iface, clsid);
1139 success = StorageImpl_ReadProperty(This->ancestorStorage,
1140 This->rootPropertySetIndex,
1144 curProperty.propertyUniqueID = *clsid;
1146 success = StorageImpl_WriteProperty(This->ancestorStorage,
1147 This->rootPropertySetIndex,
1156 /************************************************************************
1157 ** Storage32Impl implementation
1160 /************************************************************************
1161 * Storage32Impl_CreateStorage (IStorage)
1163 * This method will create the storage object within the provided storage.
1165 * See Windows documentation for more details on IStorage methods.
1167 static HRESULT WINAPI StorageImpl_CreateStorage(
1169 const OLECHAR *pwcsName, /* [string][in] */
1170 DWORD grfMode, /* [in] */
1171 DWORD reserved1, /* [in] */
1172 DWORD reserved2, /* [in] */
1173 IStorage **ppstg) /* [out] */
1175 StorageImpl* const This=(StorageImpl*)iface;
1177 IEnumSTATSTGImpl *propertyEnumeration;
1178 StgProperty currentProperty;
1179 StgProperty newProperty;
1180 ULONG foundPropertyIndex;
1181 ULONG newPropertyIndex;
1184 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1185 iface, debugstr_w(pwcsName), grfMode,
1186 reserved1, reserved2, ppstg);
1189 * Validate parameters
1192 return STG_E_INVALIDPOINTER;
1195 return STG_E_INVALIDNAME;
1198 * Initialize the out parameter
1203 * Validate the STGM flags
1205 if ( FAILED( validateSTGM(grfMode) ) ||
1206 (grfMode & STGM_DELETEONRELEASE) )
1208 WARN("bad grfMode: 0x%x\n", grfMode);
1209 return STG_E_INVALIDFLAG;
1213 * Check that we're compatible with the parent's storage mode
1215 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->base.openFlags ) )
1217 WARN("access denied\n");
1218 return STG_E_ACCESSDENIED;
1222 * Create a property enumeration and search the properties
1224 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1225 This->base.rootPropertySetIndex);
1227 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1230 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1232 if (foundPropertyIndex != PROPERTY_NULL)
1235 * An element with this name already exists
1237 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1238 STGM_ACCESS_MODE(This->base.openFlags) != STGM_READ)
1240 hr = IStorage_DestroyElement(iface, pwcsName);
1246 WARN("file already exists\n");
1247 return STG_E_FILEALREADYEXISTS;
1250 else if (STGM_ACCESS_MODE(This->base.openFlags) == STGM_READ)
1252 WARN("read-only storage\n");
1253 return STG_E_ACCESSDENIED;
1257 * memset the empty property
1259 memset(&newProperty, 0, sizeof(StgProperty));
1261 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1263 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1265 FIXME("name too long\n");
1266 return STG_E_INVALIDNAME;
1269 strcpyW(newProperty.name, pwcsName);
1271 newProperty.propertyType = PROPTYPE_STORAGE;
1272 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1273 newProperty.size.u.LowPart = 0;
1274 newProperty.size.u.HighPart = 0;
1276 newProperty.previousProperty = PROPERTY_NULL;
1277 newProperty.nextProperty = PROPERTY_NULL;
1278 newProperty.dirProperty = PROPERTY_NULL;
1280 /* call CoFileTime to get the current time
1281 newProperty.timeStampS1
1282 newProperty.timeStampD1
1283 newProperty.timeStampS2
1284 newProperty.timeStampD2
1287 /* newStorageProperty.propertyUniqueID */
1290 * Obtain a free property in the property chain
1292 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1295 * Save the new property into the new property spot
1297 StorageImpl_WriteProperty(
1298 This->base.ancestorStorage,
1303 * Find a spot in the property chain for our newly created property.
1305 updatePropertyChain(
1311 * Open it to get a pointer to return.
1313 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1315 if( (hr != S_OK) || (*ppstg == NULL))
1325 /***************************************************************************
1329 * Get a free property or create a new one.
1331 static ULONG getFreeProperty(
1332 StorageImpl *storage)
1334 ULONG currentPropertyIndex = 0;
1335 ULONG newPropertyIndex = PROPERTY_NULL;
1336 BOOL readSuccessful = TRUE;
1337 StgProperty currentProperty;
1342 * Start by reading the root property
1344 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1345 currentPropertyIndex,
1349 if (currentProperty.sizeOfNameString == 0)
1352 * The property existis and is available, we found it.
1354 newPropertyIndex = currentPropertyIndex;
1360 * We exhausted the property list, we will create more space below
1362 newPropertyIndex = currentPropertyIndex;
1364 currentPropertyIndex++;
1366 } while (newPropertyIndex == PROPERTY_NULL);
1369 * grow the property chain
1371 if (! readSuccessful)
1373 StgProperty emptyProperty;
1374 ULARGE_INTEGER newSize;
1375 ULONG propertyIndex;
1376 ULONG lastProperty = 0;
1377 ULONG blockCount = 0;
1380 * obtain the new count of property blocks
1382 blockCount = BlockChainStream_GetCount(
1383 storage->base.ancestorStorage->rootBlockChain)+1;
1386 * initialize the size used by the property stream
1388 newSize.u.HighPart = 0;
1389 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1392 * add a property block to the property chain
1394 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1397 * memset the empty property in order to initialize the unused newly
1400 memset(&emptyProperty, 0, sizeof(StgProperty));
1405 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1408 propertyIndex = newPropertyIndex;
1409 propertyIndex < lastProperty;
1412 StorageImpl_WriteProperty(
1413 storage->base.ancestorStorage,
1419 return newPropertyIndex;
1422 /****************************************************************************
1426 * Case insensitive comparison of StgProperty.name by first considering
1429 * Returns <0 when newProperty < currentProperty
1430 * >0 when newProperty > currentProperty
1431 * 0 when newProperty == currentProperty
1433 static LONG propertyNameCmp(
1434 const OLECHAR *newProperty,
1435 const OLECHAR *currentProperty)
1437 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1442 * We compare the string themselves only when they are of the same length
1444 diff = lstrcmpiW( newProperty, currentProperty);
1450 /****************************************************************************
1454 * Properly link this new element in the property chain.
1456 static void updatePropertyChain(
1457 StorageImpl *storage,
1458 ULONG newPropertyIndex,
1459 StgProperty newProperty)
1461 StgProperty currentProperty;
1464 * Read the root property
1466 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1467 storage->base.rootPropertySetIndex,
1470 if (currentProperty.dirProperty != PROPERTY_NULL)
1473 * The root storage contains some element, therefore, start the research
1474 * for the appropriate location.
1477 ULONG current, next, previous, currentPropertyId;
1480 * Keep the StgProperty sequence number of the storage first property
1482 currentPropertyId = currentProperty.dirProperty;
1487 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1488 currentProperty.dirProperty,
1491 previous = currentProperty.previousProperty;
1492 next = currentProperty.nextProperty;
1493 current = currentPropertyId;
1497 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1501 if (previous != PROPERTY_NULL)
1503 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1510 currentProperty.previousProperty = newPropertyIndex;
1511 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1519 if (next != PROPERTY_NULL)
1521 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1528 currentProperty.nextProperty = newPropertyIndex;
1529 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1538 * Trying to insert an item with the same name in the
1539 * subtree structure.
1544 previous = currentProperty.previousProperty;
1545 next = currentProperty.nextProperty;
1551 * The root storage is empty, link the new property to its dir property
1553 currentProperty.dirProperty = newPropertyIndex;
1554 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1555 storage->base.rootPropertySetIndex,
1561 /*************************************************************************
1564 static HRESULT WINAPI StorageImpl_CopyTo(
1566 DWORD ciidExclude, /* [in] */
1567 const IID* rgiidExclude, /* [size_is][unique][in] */
1568 SNB snbExclude, /* [unique][in] */
1569 IStorage* pstgDest) /* [unique][in] */
1571 IEnumSTATSTG *elements = 0;
1572 STATSTG curElement, strStat;
1574 IStorage *pstgTmp, *pstgChild;
1575 IStream *pstrTmp, *pstrChild;
1577 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1578 FIXME("Exclude option not implemented\n");
1580 TRACE("(%p, %d, %p, %p, %p)\n",
1581 iface, ciidExclude, rgiidExclude,
1582 snbExclude, pstgDest);
1585 * Perform a sanity check
1587 if ( pstgDest == 0 )
1588 return STG_E_INVALIDPOINTER;
1591 * Enumerate the elements
1593 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1601 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1602 IStorage_SetClass( pstgDest, &curElement.clsid );
1607 * Obtain the next element
1609 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1611 if ( hr == S_FALSE )
1613 hr = S_OK; /* done, every element has been copied */
1617 if (curElement.type == STGTY_STORAGE)
1620 * open child source storage
1622 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1623 STGM_READ|STGM_SHARE_EXCLUSIVE,
1624 NULL, 0, &pstgChild );
1630 * Check if destination storage is not a child of the source
1631 * storage, which will cause an infinite loop
1633 if (pstgChild == pstgDest)
1635 IEnumSTATSTG_Release(elements);
1637 return STG_E_ACCESSDENIED;
1641 * create a new storage in destination storage
1643 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1644 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1648 * if it already exist, don't create a new one use this one
1650 if (hr == STG_E_FILEALREADYEXISTS)
1652 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1653 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1654 NULL, 0, &pstgTmp );
1662 * do the copy recursively
1664 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1665 snbExclude, pstgTmp );
1667 IStorage_Release( pstgTmp );
1668 IStorage_Release( pstgChild );
1670 else if (curElement.type == STGTY_STREAM)
1673 * create a new stream in destination storage. If the stream already
1674 * exist, it will be deleted and a new one will be created.
1676 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1677 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1684 * open child stream storage
1686 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1687 STGM_READ|STGM_SHARE_EXCLUSIVE,
1694 * Get the size of the source stream
1696 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1699 * Set the size of the destination stream.
1701 IStream_SetSize(pstrTmp, strStat.cbSize);
1706 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1709 IStream_Release( pstrTmp );
1710 IStream_Release( pstrChild );
1714 WARN("unknown element type: %d\n", curElement.type);
1717 } while (hr == S_OK);
1722 IEnumSTATSTG_Release(elements);
1727 /*************************************************************************
1728 * MoveElementTo (IStorage)
1730 static HRESULT WINAPI StorageImpl_MoveElementTo(
1732 const OLECHAR *pwcsName, /* [string][in] */
1733 IStorage *pstgDest, /* [unique][in] */
1734 const OLECHAR *pwcsNewName,/* [string][in] */
1735 DWORD grfFlags) /* [in] */
1737 FIXME("(%p %s %p %s %u): stub\n", iface,
1738 debugstr_w(pwcsName), pstgDest,
1739 debugstr_w(pwcsNewName), grfFlags);
1743 /*************************************************************************
1746 * Ensures that any changes made to a storage object open in transacted mode
1747 * are reflected in the parent storage
1750 * Wine doesn't implement transacted mode, which seems to be a basic
1751 * optimization, so we can ignore this stub for now.
1753 static HRESULT WINAPI StorageImpl_Commit(
1755 DWORD grfCommitFlags)/* [in] */
1757 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1761 /*************************************************************************
1764 * Discard all changes that have been made since the last commit operation
1766 static HRESULT WINAPI StorageImpl_Revert(
1769 FIXME("(%p): stub\n", iface);
1773 /*************************************************************************
1774 * DestroyElement (IStorage)
1776 * Strategy: This implementation is built this way for simplicity not for speed.
1777 * I always delete the topmost element of the enumeration and adjust
1778 * the deleted element pointer all the time. This takes longer to
1779 * do but allow to reinvoke DestroyElement whenever we encounter a
1780 * storage object. The optimisation resides in the usage of another
1781 * enumeration strategy that would give all the leaves of a storage
1782 * first. (postfix order)
1784 static HRESULT WINAPI StorageImpl_DestroyElement(
1786 const OLECHAR *pwcsName)/* [string][in] */
1788 StorageImpl* const This=(StorageImpl*)iface;
1790 IEnumSTATSTGImpl* propertyEnumeration;
1793 StgProperty propertyToDelete;
1794 StgProperty parentProperty;
1795 ULONG foundPropertyIndexToDelete;
1796 ULONG typeOfRelation;
1797 ULONG parentPropertyId = 0;
1800 iface, debugstr_w(pwcsName));
1803 * Perform a sanity check on the parameters.
1806 return STG_E_INVALIDPOINTER;
1808 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
1809 return STG_E_ACCESSDENIED;
1812 * Create a property enumeration to search the property with the given name
1814 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1815 This->base.ancestorStorage,
1816 This->base.rootPropertySetIndex);
1818 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1819 propertyEnumeration,
1823 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1825 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1827 return STG_E_FILENOTFOUND;
1831 * Find the parent property of the property to delete (the one that
1832 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1833 * the parent is This. Otherwise, the parent is one of its sibling...
1837 * First, read This's StgProperty..
1839 res = StorageImpl_ReadProperty(
1840 This->base.ancestorStorage,
1841 This->base.rootPropertySetIndex,
1847 * Second, check to see if by any chance the actual storage (This) is not
1848 * the parent of the property to delete... We never know...
1850 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1853 * Set data as it would have been done in the else part...
1855 typeOfRelation = PROPERTY_RELATION_DIR;
1856 parentPropertyId = This->base.rootPropertySetIndex;
1861 * Create a property enumeration to search the parent properties, and
1862 * delete it once done.
1864 IEnumSTATSTGImpl* propertyEnumeration2;
1866 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1867 This->base.ancestorStorage,
1868 This->base.rootPropertySetIndex);
1870 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1871 propertyEnumeration2,
1872 foundPropertyIndexToDelete,
1876 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1879 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1881 hr = deleteStorageProperty(
1883 foundPropertyIndexToDelete,
1886 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1888 hr = deleteStreamProperty(
1890 foundPropertyIndexToDelete,
1898 * Adjust the property chain
1900 hr = adjustPropertyChain(
1911 /************************************************************************
1912 * StorageImpl_Stat (IStorage)
1914 * This method will retrieve information about this storage object.
1916 * See Windows documentation for more details on IStorage methods.
1918 static HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1919 STATSTG* pstatstg, /* [out] */
1920 DWORD grfStatFlag) /* [in] */
1922 StorageImpl* const This = (StorageImpl*)iface;
1923 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1925 if ( SUCCEEDED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1927 CoTaskMemFree(pstatstg->pwcsName);
1928 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1929 strcpyW(pstatstg->pwcsName, This->pwcsName);
1935 /******************************************************************************
1936 * Internal stream list handlers
1939 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1941 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1942 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1945 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1947 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1948 list_remove(&(strm->StrmListEntry));
1951 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1953 struct list *cur, *cur2;
1954 StgStreamImpl *strm=NULL;
1956 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1957 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1958 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1959 strm->parentStorage = NULL;
1965 /*********************************************************************
1969 * Perform the deletion of a complete storage node
1972 static HRESULT deleteStorageProperty(
1973 StorageImpl *parentStorage,
1974 ULONG indexOfPropertyToDelete,
1975 StgProperty propertyToDelete)
1977 IEnumSTATSTG *elements = 0;
1978 IStorage *childStorage = 0;
1979 STATSTG currentElement;
1981 HRESULT destroyHr = S_OK;
1984 * Open the storage and enumerate it
1986 hr = StorageBaseImpl_OpenStorage(
1987 (IStorage*)parentStorage,
1988 propertyToDelete.name,
1990 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2001 * Enumerate the elements
2003 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2008 * Obtain the next element
2010 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
2013 destroyHr = StorageImpl_DestroyElement(childStorage, currentElement.pwcsName);
2015 CoTaskMemFree(currentElement.pwcsName);
2019 * We need to Reset the enumeration every time because we delete elements
2020 * and the enumeration could be invalid
2022 IEnumSTATSTG_Reset(elements);
2024 } while ((hr == S_OK) && (destroyHr == S_OK));
2027 * Invalidate the property by zeroing its name member.
2029 propertyToDelete.sizeOfNameString = 0;
2031 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
2032 indexOfPropertyToDelete,
2035 IStorage_Release(childStorage);
2036 IEnumSTATSTG_Release(elements);
2041 /*********************************************************************
2045 * Perform the deletion of a stream node
2048 static HRESULT deleteStreamProperty(
2049 StorageImpl *parentStorage,
2050 ULONG indexOfPropertyToDelete,
2051 StgProperty propertyToDelete)
2055 ULARGE_INTEGER size;
2057 size.u.HighPart = 0;
2060 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2061 propertyToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2071 hr = IStream_SetSize(pis, size);
2079 * Release the stream object.
2081 IStream_Release(pis);
2084 * Invalidate the property by zeroing its name member.
2086 propertyToDelete.sizeOfNameString = 0;
2089 * Here we should re-read the property so we get the updated pointer
2090 * but since we are here to zap it, I don't do it...
2092 StorageImpl_WriteProperty(
2093 parentStorage->base.ancestorStorage,
2094 indexOfPropertyToDelete,
2100 /*********************************************************************
2104 * Finds a placeholder for the StgProperty within the Storage
2107 static HRESULT findPlaceholder(
2108 StorageImpl *storage,
2109 ULONG propertyIndexToStore,
2110 ULONG storePropertyIndex,
2113 StgProperty storeProperty;
2117 * Read the storage property
2119 res = StorageImpl_ReadProperty(
2120 storage->base.ancestorStorage,
2129 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2131 if (storeProperty.previousProperty != PROPERTY_NULL)
2133 return findPlaceholder(
2135 propertyIndexToStore,
2136 storeProperty.previousProperty,
2141 storeProperty.previousProperty = propertyIndexToStore;
2144 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2146 if (storeProperty.nextProperty != PROPERTY_NULL)
2148 return findPlaceholder(
2150 propertyIndexToStore,
2151 storeProperty.nextProperty,
2156 storeProperty.nextProperty = propertyIndexToStore;
2159 else if (typeOfRelation == PROPERTY_RELATION_DIR)
2161 if (storeProperty.dirProperty != PROPERTY_NULL)
2163 return findPlaceholder(
2165 propertyIndexToStore,
2166 storeProperty.dirProperty,
2171 storeProperty.dirProperty = propertyIndexToStore;
2175 res = StorageImpl_WriteProperty(
2176 storage->base.ancestorStorage,
2188 /*************************************************************************
2192 * This method takes the previous and the next property link of a property
2193 * to be deleted and find them a place in the Storage.
2195 static HRESULT adjustPropertyChain(
2197 StgProperty propertyToDelete,
2198 StgProperty parentProperty,
2199 ULONG parentPropertyId,
2202 ULONG newLinkProperty = PROPERTY_NULL;
2203 BOOL needToFindAPlaceholder = FALSE;
2204 ULONG storeNode = PROPERTY_NULL;
2205 ULONG toStoreNode = PROPERTY_NULL;
2206 INT relationType = 0;
2210 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2212 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2215 * Set the parent previous to the property to delete previous
2217 newLinkProperty = propertyToDelete.previousProperty;
2219 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2222 * We also need to find a storage for the other link, setup variables
2223 * to do this at the end...
2225 needToFindAPlaceholder = TRUE;
2226 storeNode = propertyToDelete.previousProperty;
2227 toStoreNode = propertyToDelete.nextProperty;
2228 relationType = PROPERTY_RELATION_NEXT;
2231 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2234 * Set the parent previous to the property to delete next
2236 newLinkProperty = propertyToDelete.nextProperty;
2240 * Link it for real...
2242 parentProperty.previousProperty = newLinkProperty;
2245 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2247 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2250 * Set the parent next to the property to delete next previous
2252 newLinkProperty = propertyToDelete.previousProperty;
2254 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2257 * We also need to find a storage for the other link, setup variables
2258 * to do this at the end...
2260 needToFindAPlaceholder = TRUE;
2261 storeNode = propertyToDelete.previousProperty;
2262 toStoreNode = propertyToDelete.nextProperty;
2263 relationType = PROPERTY_RELATION_NEXT;
2266 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2269 * Set the parent next to the property to delete next
2271 newLinkProperty = propertyToDelete.nextProperty;
2275 * Link it for real...
2277 parentProperty.nextProperty = newLinkProperty;
2279 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2281 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2284 * Set the parent dir to the property to delete previous
2286 newLinkProperty = propertyToDelete.previousProperty;
2288 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2291 * We also need to find a storage for the other link, setup variables
2292 * to do this at the end...
2294 needToFindAPlaceholder = TRUE;
2295 storeNode = propertyToDelete.previousProperty;
2296 toStoreNode = propertyToDelete.nextProperty;
2297 relationType = PROPERTY_RELATION_NEXT;
2300 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2303 * Set the parent dir to the property to delete next
2305 newLinkProperty = propertyToDelete.nextProperty;
2309 * Link it for real...
2311 parentProperty.dirProperty = newLinkProperty;
2315 * Write back the parent property
2317 res = StorageImpl_WriteProperty(
2318 This->base.ancestorStorage,
2327 * If a placeholder is required for the other link, then, find one and
2328 * get out of here...
2330 if (needToFindAPlaceholder)
2332 hr = findPlaceholder(
2343 /******************************************************************************
2344 * SetElementTimes (IStorage)
2346 static HRESULT WINAPI StorageImpl_SetElementTimes(
2348 const OLECHAR *pwcsName,/* [string][in] */
2349 const FILETIME *pctime, /* [in] */
2350 const FILETIME *patime, /* [in] */
2351 const FILETIME *pmtime) /* [in] */
2353 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2357 /******************************************************************************
2358 * SetStateBits (IStorage)
2360 static HRESULT WINAPI StorageImpl_SetStateBits(
2362 DWORD grfStateBits,/* [in] */
2363 DWORD grfMask) /* [in] */
2365 StorageImpl* const This = (StorageImpl*)iface;
2366 This->base.stateBits = (This->base.stateBits & ~grfMask) | (grfStateBits & grfMask);
2371 * Virtual function table for the IStorage32Impl class.
2373 static const IStorageVtbl Storage32Impl_Vtbl =
2375 StorageBaseImpl_QueryInterface,
2376 StorageBaseImpl_AddRef,
2377 StorageBaseImpl_Release,
2378 StorageBaseImpl_CreateStream,
2379 StorageBaseImpl_OpenStream,
2380 StorageImpl_CreateStorage,
2381 StorageBaseImpl_OpenStorage,
2383 StorageImpl_MoveElementTo,
2386 StorageBaseImpl_EnumElements,
2387 StorageImpl_DestroyElement,
2388 StorageBaseImpl_RenameElement,
2389 StorageImpl_SetElementTimes,
2390 StorageBaseImpl_SetClass,
2391 StorageImpl_SetStateBits,
2395 static HRESULT StorageImpl_Construct(
2405 StgProperty currentProperty;
2406 BOOL readSuccessful;
2407 ULONG currentPropertyIndex;
2409 if ( FAILED( validateSTGM(openFlags) ))
2410 return STG_E_INVALIDFLAG;
2412 memset(This, 0, sizeof(StorageImpl));
2414 list_init(&This->base.strmHead);
2416 This->base.lpVtbl = &Storage32Impl_Vtbl;
2417 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2418 This->base.v_destructor = StorageImpl_Destroy;
2419 This->base.openFlags = (openFlags & ~STGM_CREATE);
2420 This->create = create;
2423 * This is the top-level storage so initialize the ancestor pointer
2426 This->base.ancestorStorage = This;
2428 This->hFile = hFile;
2431 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2432 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2433 if (!This->pwcsName)
2434 return STG_E_INSUFFICIENTMEMORY;
2435 strcpyW(This->pwcsName, pwcsName);
2439 * Initialize the big block cache.
2441 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2442 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2443 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2449 if (This->bigBlockFile == 0)
2454 ULARGE_INTEGER size;
2455 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2458 * Initialize all header variables:
2459 * - The big block depot consists of one block and it is at block 0
2460 * - The properties start at block 1
2461 * - There is no small block depot
2463 memset( This->bigBlockDepotStart,
2465 sizeof(This->bigBlockDepotStart));
2467 This->bigBlockDepotCount = 1;
2468 This->bigBlockDepotStart[0] = 0;
2469 This->rootStartBlock = 1;
2470 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2471 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2472 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2473 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2474 This->extBigBlockDepotCount = 0;
2476 StorageImpl_SaveFileHeader(This);
2479 * Add one block for the big block depot and one block for the properties
2481 size.u.HighPart = 0;
2482 size.u.LowPart = This->bigBlockSize * 3;
2483 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2486 * Initialize the big block depot
2488 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2489 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2490 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2491 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2496 * Load the header for the file.
2498 hr = StorageImpl_LoadFileHeader(This);
2502 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2509 * There is no block depot cached yet.
2511 This->indexBlockDepotCached = 0xFFFFFFFF;
2514 * Start searching for free blocks with block 0.
2516 This->prevFreeBlock = 0;
2519 * Create the block chain abstractions.
2521 if(!(This->rootBlockChain =
2522 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2523 return STG_E_READFAULT;
2525 if(!(This->smallBlockDepotChain =
2526 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2528 return STG_E_READFAULT;
2531 * Write the root property (memory only)
2535 StgProperty rootProp;
2537 * Initialize the property chain
2539 memset(&rootProp, 0, sizeof(rootProp));
2540 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2541 sizeof(rootProp.name)/sizeof(WCHAR) );
2542 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2543 rootProp.propertyType = PROPTYPE_ROOT;
2544 rootProp.previousProperty = PROPERTY_NULL;
2545 rootProp.nextProperty = PROPERTY_NULL;
2546 rootProp.dirProperty = PROPERTY_NULL;
2547 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2548 rootProp.size.u.HighPart = 0;
2549 rootProp.size.u.LowPart = 0;
2551 StorageImpl_WriteProperty(This, 0, &rootProp);
2555 * Find the ID of the root in the property sets.
2557 currentPropertyIndex = 0;
2561 readSuccessful = StorageImpl_ReadProperty(
2563 currentPropertyIndex,
2568 if ( (currentProperty.sizeOfNameString != 0 ) &&
2569 (currentProperty.propertyType == PROPTYPE_ROOT) )
2571 This->base.rootPropertySetIndex = currentPropertyIndex;
2575 currentPropertyIndex++;
2577 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2579 if (!readSuccessful)
2582 return STG_E_READFAULT;
2586 * Create the block chain abstraction for the small block root chain.
2588 if(!(This->smallBlockRootChain =
2589 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2590 return STG_E_READFAULT;
2595 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2597 StorageImpl *This = (StorageImpl*) iface;
2598 TRACE("(%p)\n", This);
2600 StorageBaseImpl_DeleteAll(&This->base);
2602 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2604 BlockChainStream_Destroy(This->smallBlockRootChain);
2605 BlockChainStream_Destroy(This->rootBlockChain);
2606 BlockChainStream_Destroy(This->smallBlockDepotChain);
2608 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2609 HeapFree(GetProcessHeap(), 0, This);
2612 /******************************************************************************
2613 * Storage32Impl_GetNextFreeBigBlock
2615 * Returns the index of the next free big block.
2616 * If the big block depot is filled, this method will enlarge it.
2619 static ULONG StorageImpl_GetNextFreeBigBlock(
2622 ULONG depotBlockIndexPos;
2623 BYTE depotBuffer[BIG_BLOCK_SIZE];
2625 ULONG depotBlockOffset;
2626 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2627 ULONG nextBlockIndex = BLOCK_SPECIAL;
2629 ULONG freeBlock = BLOCK_UNUSED;
2631 depotIndex = This->prevFreeBlock / blocksPerDepot;
2632 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2635 * Scan the entire big block depot until we find a block marked free
2637 while (nextBlockIndex != BLOCK_UNUSED)
2639 if (depotIndex < COUNT_BBDEPOTINHEADER)
2641 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2644 * Grow the primary depot.
2646 if (depotBlockIndexPos == BLOCK_UNUSED)
2648 depotBlockIndexPos = depotIndex*blocksPerDepot;
2651 * Add a block depot.
2653 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2654 This->bigBlockDepotCount++;
2655 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2658 * Flag it as a block depot.
2660 StorageImpl_SetNextBlockInChain(This,
2664 /* Save new header information.
2666 StorageImpl_SaveFileHeader(This);
2671 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2673 if (depotBlockIndexPos == BLOCK_UNUSED)
2676 * Grow the extended depot.
2678 ULONG extIndex = BLOCK_UNUSED;
2679 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2680 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2682 if (extBlockOffset == 0)
2684 /* We need an extended block.
2686 extIndex = Storage32Impl_AddExtBlockDepot(This);
2687 This->extBigBlockDepotCount++;
2688 depotBlockIndexPos = extIndex + 1;
2691 depotBlockIndexPos = depotIndex * blocksPerDepot;
2694 * Add a block depot and mark it in the extended block.
2696 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2697 This->bigBlockDepotCount++;
2698 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2700 /* Flag the block depot.
2702 StorageImpl_SetNextBlockInChain(This,
2706 /* If necessary, flag the extended depot block.
2708 if (extIndex != BLOCK_UNUSED)
2709 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2711 /* Save header information.
2713 StorageImpl_SaveFileHeader(This);
2717 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2721 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2722 ( nextBlockIndex != BLOCK_UNUSED))
2724 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2726 if (nextBlockIndex == BLOCK_UNUSED)
2728 freeBlock = (depotIndex * blocksPerDepot) +
2729 (depotBlockOffset/sizeof(ULONG));
2732 depotBlockOffset += sizeof(ULONG);
2737 depotBlockOffset = 0;
2741 * make sure that the block physically exists before using it
2743 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2745 This->prevFreeBlock = freeBlock;
2750 /******************************************************************************
2751 * Storage32Impl_AddBlockDepot
2753 * This will create a depot block, essentially it is a block initialized
2756 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2758 BYTE blockBuffer[BIG_BLOCK_SIZE];
2761 * Initialize blocks as free
2763 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2764 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2767 /******************************************************************************
2768 * Storage32Impl_GetExtDepotBlock
2770 * Returns the index of the block that corresponds to the specified depot
2771 * index. This method is only for depot indexes equal or greater than
2772 * COUNT_BBDEPOTINHEADER.
2774 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2776 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2777 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2778 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2779 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2780 ULONG blockIndex = BLOCK_UNUSED;
2781 ULONG extBlockIndex = This->extBigBlockDepotStart;
2783 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2785 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2786 return BLOCK_UNUSED;
2788 while (extBlockCount > 0)
2790 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2794 if (extBlockIndex != BLOCK_UNUSED)
2795 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
2796 extBlockOffset * sizeof(ULONG), &blockIndex);
2801 /******************************************************************************
2802 * Storage32Impl_SetExtDepotBlock
2804 * Associates the specified block index to the specified depot index.
2805 * This method is only for depot indexes equal or greater than
2806 * COUNT_BBDEPOTINHEADER.
2808 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2810 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2811 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2812 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2813 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2814 ULONG extBlockIndex = This->extBigBlockDepotStart;
2816 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2818 while (extBlockCount > 0)
2820 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2824 if (extBlockIndex != BLOCK_UNUSED)
2826 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
2827 extBlockOffset * sizeof(ULONG),
2832 /******************************************************************************
2833 * Storage32Impl_AddExtBlockDepot
2835 * Creates an extended depot block.
2837 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2839 ULONG numExtBlocks = This->extBigBlockDepotCount;
2840 ULONG nextExtBlock = This->extBigBlockDepotStart;
2841 BYTE depotBuffer[BIG_BLOCK_SIZE];
2842 ULONG index = BLOCK_UNUSED;
2843 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2844 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2845 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2847 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2848 blocksPerDepotBlock;
2850 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2853 * The first extended block.
2855 This->extBigBlockDepotStart = index;
2861 * Follow the chain to the last one.
2863 for (i = 0; i < (numExtBlocks - 1); i++)
2865 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2869 * Add the new extended block to the chain.
2871 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
2876 * Initialize this block.
2878 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2879 StorageImpl_WriteBigBlock(This, index, depotBuffer);
2884 /******************************************************************************
2885 * Storage32Impl_FreeBigBlock
2887 * This method will flag the specified block as free in the big block depot.
2889 static void StorageImpl_FreeBigBlock(
2893 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2895 if (blockIndex < This->prevFreeBlock)
2896 This->prevFreeBlock = blockIndex;
2899 /************************************************************************
2900 * Storage32Impl_GetNextBlockInChain
2902 * This method will retrieve the block index of the next big block in
2905 * Params: This - Pointer to the Storage object.
2906 * blockIndex - Index of the block to retrieve the chain
2908 * nextBlockIndex - receives the return value.
2910 * Returns: This method returns the index of the next block in the chain.
2911 * It will return the constants:
2912 * BLOCK_SPECIAL - If the block given was not part of a
2914 * BLOCK_END_OF_CHAIN - If the block given was the last in
2916 * BLOCK_UNUSED - If the block given was not past of a chain
2918 * BLOCK_EXTBBDEPOT - This block is part of the extended
2921 * See Windows documentation for more details on IStorage methods.
2923 static HRESULT StorageImpl_GetNextBlockInChain(
2926 ULONG* nextBlockIndex)
2928 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2929 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2930 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2931 BYTE depotBuffer[BIG_BLOCK_SIZE];
2933 ULONG depotBlockIndexPos;
2936 *nextBlockIndex = BLOCK_SPECIAL;
2938 if(depotBlockCount >= This->bigBlockDepotCount)
2940 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
2941 This->bigBlockDepotCount);
2942 return STG_E_READFAULT;
2946 * Cache the currently accessed depot block.
2948 if (depotBlockCount != This->indexBlockDepotCached)
2950 This->indexBlockDepotCached = depotBlockCount;
2952 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2954 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2959 * We have to look in the extended depot.
2961 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2964 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2967 return STG_E_READFAULT;
2969 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2971 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2972 This->blockDepotCached[index] = *nextBlockIndex;
2976 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2981 /******************************************************************************
2982 * Storage32Impl_GetNextExtendedBlock
2984 * Given an extended block this method will return the next extended block.
2987 * The last ULONG of an extended block is the block index of the next
2988 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2992 * - The index of the next extended block
2993 * - BLOCK_UNUSED: there is no next extended block.
2994 * - Any other return values denotes failure.
2996 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2998 ULONG nextBlockIndex = BLOCK_SPECIAL;
2999 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3001 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3004 return nextBlockIndex;
3007 /******************************************************************************
3008 * Storage32Impl_SetNextBlockInChain
3010 * This method will write the index of the specified block's next block
3011 * in the big block depot.
3013 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3016 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3017 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3018 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3021 static void StorageImpl_SetNextBlockInChain(
3026 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3027 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3028 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3029 ULONG depotBlockIndexPos;
3031 assert(depotBlockCount < This->bigBlockDepotCount);
3032 assert(blockIndex != nextBlock);
3034 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3036 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3041 * We have to look in the extended depot.
3043 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3046 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3049 * Update the cached block depot, if necessary.
3051 if (depotBlockCount == This->indexBlockDepotCached)
3053 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3057 /******************************************************************************
3058 * Storage32Impl_LoadFileHeader
3060 * This method will read in the file header, i.e. big block index -1.
3062 static HRESULT StorageImpl_LoadFileHeader(
3065 HRESULT hr = STG_E_FILENOTFOUND;
3066 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3072 * Get a pointer to the big block of data containing the header.
3074 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3077 * Extract the information from the header.
3082 * Check for the "magic number" signature and return an error if it is not
3085 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3087 return STG_E_OLDFORMAT;
3090 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3092 return STG_E_INVALIDHEADER;
3095 StorageUtl_ReadWord(
3097 OFFSET_BIGBLOCKSIZEBITS,
3098 &This->bigBlockSizeBits);
3100 StorageUtl_ReadWord(
3102 OFFSET_SMALLBLOCKSIZEBITS,
3103 &This->smallBlockSizeBits);
3105 StorageUtl_ReadDWord(
3107 OFFSET_BBDEPOTCOUNT,
3108 &This->bigBlockDepotCount);
3110 StorageUtl_ReadDWord(
3112 OFFSET_ROOTSTARTBLOCK,
3113 &This->rootStartBlock);
3115 StorageUtl_ReadDWord(
3117 OFFSET_SBDEPOTSTART,
3118 &This->smallBlockDepotStart);
3120 StorageUtl_ReadDWord(
3122 OFFSET_EXTBBDEPOTSTART,
3123 &This->extBigBlockDepotStart);
3125 StorageUtl_ReadDWord(
3127 OFFSET_EXTBBDEPOTCOUNT,
3128 &This->extBigBlockDepotCount);
3130 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3132 StorageUtl_ReadDWord(
3134 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3135 &(This->bigBlockDepotStart[index]));
3139 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3141 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3142 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3145 * Right now, the code is making some assumptions about the size of the
3146 * blocks, just make sure they are what we're expecting.
3148 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3149 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3151 WARN("Broken OLE storage file\n");
3152 hr = STG_E_INVALIDHEADER;
3161 /******************************************************************************
3162 * Storage32Impl_SaveFileHeader
3164 * This method will save to the file the header, i.e. big block -1.
3166 static void StorageImpl_SaveFileHeader(
3169 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3174 * Get a pointer to the big block of data containing the header.
3176 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3179 * If the block read failed, the file is probably new.
3184 * Initialize for all unknown fields.
3186 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3189 * Initialize the magic number.
3191 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3194 * And a bunch of things we don't know what they mean
3196 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3197 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3198 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3199 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3203 * Write the information to the header.
3205 StorageUtl_WriteWord(
3207 OFFSET_BIGBLOCKSIZEBITS,
3208 This->bigBlockSizeBits);
3210 StorageUtl_WriteWord(
3212 OFFSET_SMALLBLOCKSIZEBITS,
3213 This->smallBlockSizeBits);
3215 StorageUtl_WriteDWord(
3217 OFFSET_BBDEPOTCOUNT,
3218 This->bigBlockDepotCount);
3220 StorageUtl_WriteDWord(
3222 OFFSET_ROOTSTARTBLOCK,
3223 This->rootStartBlock);
3225 StorageUtl_WriteDWord(
3227 OFFSET_SBDEPOTSTART,
3228 This->smallBlockDepotStart);
3230 StorageUtl_WriteDWord(
3232 OFFSET_SBDEPOTCOUNT,
3233 This->smallBlockDepotChain ?
3234 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3236 StorageUtl_WriteDWord(
3238 OFFSET_EXTBBDEPOTSTART,
3239 This->extBigBlockDepotStart);
3241 StorageUtl_WriteDWord(
3243 OFFSET_EXTBBDEPOTCOUNT,
3244 This->extBigBlockDepotCount);
3246 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3248 StorageUtl_WriteDWord(
3250 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3251 (This->bigBlockDepotStart[index]));
3255 * Write the big block back to the file.
3257 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3260 /******************************************************************************
3261 * Storage32Impl_ReadProperty
3263 * This method will read the specified property from the property chain.
3265 BOOL StorageImpl_ReadProperty(
3268 StgProperty* buffer)
3270 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3271 ULARGE_INTEGER offsetInPropSet;
3275 offsetInPropSet.u.HighPart = 0;
3276 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3278 readRes = BlockChainStream_ReadAt(
3279 This->rootBlockChain,
3285 if (SUCCEEDED(readRes))
3287 /* replace the name of root entry (often "Root Entry") by the file name */
3288 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3289 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3291 memset(buffer->name, 0, sizeof(buffer->name));
3295 PROPERTY_NAME_BUFFER_LEN );
3296 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3298 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3300 StorageUtl_ReadWord(
3302 OFFSET_PS_NAMELENGTH,
3303 &buffer->sizeOfNameString);
3305 StorageUtl_ReadDWord(
3307 OFFSET_PS_PREVIOUSPROP,
3308 &buffer->previousProperty);
3310 StorageUtl_ReadDWord(
3313 &buffer->nextProperty);
3315 StorageUtl_ReadDWord(
3318 &buffer->dirProperty);
3320 StorageUtl_ReadGUID(
3323 &buffer->propertyUniqueID);
3325 StorageUtl_ReadDWord(
3328 &buffer->timeStampS1);
3330 StorageUtl_ReadDWord(
3333 &buffer->timeStampD1);
3335 StorageUtl_ReadDWord(
3338 &buffer->timeStampS2);
3340 StorageUtl_ReadDWord(
3343 &buffer->timeStampD2);
3345 StorageUtl_ReadDWord(
3347 OFFSET_PS_STARTBLOCK,
3348 &buffer->startingBlock);
3350 StorageUtl_ReadDWord(
3353 &buffer->size.u.LowPart);
3355 buffer->size.u.HighPart = 0;
3358 return SUCCEEDED(readRes) ? TRUE : FALSE;
3361 /*********************************************************************
3362 * Write the specified property into the property chain
3364 BOOL StorageImpl_WriteProperty(
3367 const StgProperty* buffer)
3369 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3370 ULARGE_INTEGER offsetInPropSet;
3374 offsetInPropSet.u.HighPart = 0;
3375 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3377 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3380 currentProperty + OFFSET_PS_NAME,
3382 PROPERTY_NAME_BUFFER_LEN );
3384 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3386 StorageUtl_WriteWord(
3388 OFFSET_PS_NAMELENGTH,
3389 buffer->sizeOfNameString);
3391 StorageUtl_WriteDWord(
3393 OFFSET_PS_PREVIOUSPROP,
3394 buffer->previousProperty);
3396 StorageUtl_WriteDWord(
3399 buffer->nextProperty);
3401 StorageUtl_WriteDWord(
3404 buffer->dirProperty);
3406 StorageUtl_WriteGUID(
3409 &buffer->propertyUniqueID);
3411 StorageUtl_WriteDWord(
3414 buffer->timeStampS1);
3416 StorageUtl_WriteDWord(
3419 buffer->timeStampD1);
3421 StorageUtl_WriteDWord(
3424 buffer->timeStampS2);
3426 StorageUtl_WriteDWord(
3429 buffer->timeStampD2);
3431 StorageUtl_WriteDWord(
3433 OFFSET_PS_STARTBLOCK,
3434 buffer->startingBlock);
3436 StorageUtl_WriteDWord(
3439 buffer->size.u.LowPart);
3441 writeRes = BlockChainStream_WriteAt(This->rootBlockChain,
3446 return SUCCEEDED(writeRes) ? TRUE : FALSE;
3449 static BOOL StorageImpl_ReadBigBlock(
3454 ULARGE_INTEGER ulOffset;
3457 ulOffset.u.HighPart = 0;
3458 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3460 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3461 return (read == This->bigBlockSize);
3464 static BOOL StorageImpl_ReadDWordFromBigBlock(
3470 ULARGE_INTEGER ulOffset;
3474 ulOffset.u.HighPart = 0;
3475 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3476 ulOffset.u.LowPart += offset;
3478 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3479 *value = lendian32toh(tmp);
3480 return (read == sizeof(DWORD));
3483 static BOOL StorageImpl_WriteBigBlock(
3488 ULARGE_INTEGER ulOffset;
3491 ulOffset.u.HighPart = 0;
3492 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3494 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3495 return (wrote == This->bigBlockSize);
3498 static BOOL StorageImpl_WriteDWordToBigBlock(
3504 ULARGE_INTEGER ulOffset;
3507 ulOffset.u.HighPart = 0;
3508 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3509 ulOffset.u.LowPart += offset;
3511 value = htole32(value);
3512 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3513 return (wrote == sizeof(DWORD));
3516 /******************************************************************************
3517 * Storage32Impl_SmallBlocksToBigBlocks
3519 * This method will convert a small block chain to a big block chain.
3520 * The small block chain will be destroyed.
3522 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3524 SmallBlockChainStream** ppsbChain)
3526 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3527 ULARGE_INTEGER size, offset;
3528 ULONG cbRead, cbWritten;
3529 ULARGE_INTEGER cbTotalRead;
3530 ULONG propertyIndex;
3531 HRESULT resWrite = S_OK;
3533 StgProperty chainProperty;
3535 BlockChainStream *bbTempChain = NULL;
3536 BlockChainStream *bigBlockChain = NULL;
3539 * Create a temporary big block chain that doesn't have
3540 * an associated property. This temporary chain will be
3541 * used to copy data from small blocks to big blocks.
3543 bbTempChain = BlockChainStream_Construct(This,
3546 if(!bbTempChain) return NULL;
3548 * Grow the big block chain.
3550 size = SmallBlockChainStream_GetSize(*ppsbChain);
3551 BlockChainStream_SetSize(bbTempChain, size);
3554 * Copy the contents of the small block chain to the big block chain
3555 * by small block size increments.
3557 offset.u.LowPart = 0;
3558 offset.u.HighPart = 0;
3559 cbTotalRead.QuadPart = 0;
3561 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3564 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3566 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3569 if (FAILED(resRead))
3574 cbTotalRead.QuadPart += cbRead;
3576 resWrite = BlockChainStream_WriteAt(bbTempChain,
3582 if (FAILED(resWrite))
3585 offset.u.LowPart += cbRead;
3587 } while (cbTotalRead.QuadPart < size.QuadPart);
3588 HeapFree(GetProcessHeap(),0,buffer);
3590 size.u.HighPart = 0;
3593 if (FAILED(resRead) || FAILED(resWrite))
3595 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3596 BlockChainStream_SetSize(bbTempChain, size);
3597 BlockChainStream_Destroy(bbTempChain);
3602 * Destroy the small block chain.
3604 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3605 SmallBlockChainStream_SetSize(*ppsbChain, size);
3606 SmallBlockChainStream_Destroy(*ppsbChain);
3610 * Change the property information. This chain is now a big block chain
3611 * and it doesn't reside in the small blocks chain anymore.
3613 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3615 chainProperty.startingBlock = bbHeadOfChain;
3617 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3620 * Destroy the temporary propertyless big block chain.
3621 * Create a new big block chain associated with this property.
3623 BlockChainStream_Destroy(bbTempChain);
3624 bigBlockChain = BlockChainStream_Construct(This,
3628 return bigBlockChain;
3631 /******************************************************************************
3632 * Storage32Impl_BigBlocksToSmallBlocks
3634 * This method will convert a big block chain to a small block chain.
3635 * The big block chain will be destroyed on success.
3637 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3639 BlockChainStream** ppbbChain)
3641 ULARGE_INTEGER size, offset, cbTotalRead;
3642 ULONG cbRead, cbWritten, propertyIndex, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3643 HRESULT resWrite = S_OK, resRead;
3644 StgProperty chainProperty;
3646 SmallBlockChainStream* sbTempChain;
3648 TRACE("%p %p\n", This, ppbbChain);
3650 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3656 size = BlockChainStream_GetSize(*ppbbChain);
3657 SmallBlockChainStream_SetSize(sbTempChain, size);
3659 offset.u.HighPart = 0;
3660 offset.u.LowPart = 0;
3661 cbTotalRead.QuadPart = 0;
3662 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3665 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3666 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
3674 cbTotalRead.QuadPart += cbRead;
3676 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3677 cbRead, buffer, &cbWritten);
3679 if(FAILED(resWrite))
3682 offset.u.LowPart += cbRead;
3684 }while(cbTotalRead.QuadPart < size.QuadPart);
3685 HeapFree(GetProcessHeap(), 0, buffer);
3687 size.u.HighPart = 0;
3690 if(FAILED(resRead) || FAILED(resWrite))
3692 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3693 SmallBlockChainStream_SetSize(sbTempChain, size);
3694 SmallBlockChainStream_Destroy(sbTempChain);
3698 /* destroy the original big block chain */
3699 propertyIndex = (*ppbbChain)->ownerPropertyIndex;
3700 BlockChainStream_SetSize(*ppbbChain, size);
3701 BlockChainStream_Destroy(*ppbbChain);
3704 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3705 chainProperty.startingBlock = sbHeadOfChain;
3706 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3708 SmallBlockChainStream_Destroy(sbTempChain);
3709 return SmallBlockChainStream_Construct(This, NULL, propertyIndex);
3712 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3714 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3716 HeapFree(GetProcessHeap(), 0, This);
3719 /******************************************************************************
3721 ** Storage32InternalImpl_Commit
3724 static HRESULT WINAPI StorageInternalImpl_Commit(
3726 DWORD grfCommitFlags) /* [in] */
3728 FIXME("(%p,%x): stub\n", iface, grfCommitFlags);
3732 /******************************************************************************
3734 ** Storage32InternalImpl_Revert
3737 static HRESULT WINAPI StorageInternalImpl_Revert(
3740 FIXME("(%p): stub\n", iface);
3744 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3746 IStorage_Release((IStorage*)This->parentStorage);
3747 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3748 HeapFree(GetProcessHeap(), 0, This);
3751 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3752 IEnumSTATSTG* iface,
3756 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3759 * Perform a sanity check on the parameters.
3762 return E_INVALIDARG;
3765 * Initialize the return parameter.
3770 * Compare the riid with the interface IDs implemented by this object.
3772 if (IsEqualGUID(&IID_IUnknown, riid) ||
3773 IsEqualGUID(&IID_IEnumSTATSTG, riid))
3776 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3780 return E_NOINTERFACE;
3783 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3784 IEnumSTATSTG* iface)
3786 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3787 return InterlockedIncrement(&This->ref);
3790 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3791 IEnumSTATSTG* iface)
3793 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3797 newRef = InterlockedDecrement(&This->ref);
3800 * If the reference count goes down to 0, perform suicide.
3804 IEnumSTATSTGImpl_Destroy(This);
3810 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3811 IEnumSTATSTG* iface,
3814 ULONG* pceltFetched)
3816 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3818 StgProperty currentProperty;
3819 STATSTG* currentReturnStruct = rgelt;
3820 ULONG objectFetched = 0;
3821 ULONG currentSearchNode;
3824 * Perform a sanity check on the parameters.
3826 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3827 return E_INVALIDARG;
3830 * To avoid the special case, get another pointer to a ULONG value if
3831 * the caller didn't supply one.
3833 if (pceltFetched==0)
3834 pceltFetched = &objectFetched;
3837 * Start the iteration, we will iterate until we hit the end of the
3838 * linked list or until we hit the number of items to iterate through
3843 * Start with the node at the top of the stack.
3845 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3847 while ( ( *pceltFetched < celt) &&
3848 ( currentSearchNode!=PROPERTY_NULL) )
3851 * Remove the top node from the stack
3853 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3856 * Read the property from the storage.
3858 StorageImpl_ReadProperty(This->parentStorage,
3863 * Copy the information to the return buffer.
3865 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3870 * Step to the next item in the iteration
3873 currentReturnStruct++;
3876 * Push the next search node in the search stack.
3878 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3881 * continue the iteration.
3883 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3886 if (*pceltFetched == celt)
3893 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3894 IEnumSTATSTG* iface,
3897 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3899 StgProperty currentProperty;
3900 ULONG objectFetched = 0;
3901 ULONG currentSearchNode;
3904 * Start with the node at the top of the stack.
3906 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3908 while ( (objectFetched < celt) &&
3909 (currentSearchNode!=PROPERTY_NULL) )
3912 * Remove the top node from the stack
3914 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3917 * Read the property from the storage.
3919 StorageImpl_ReadProperty(This->parentStorage,
3924 * Step to the next item in the iteration
3929 * Push the next search node in the search stack.
3931 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3934 * continue the iteration.
3936 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3939 if (objectFetched == celt)
3945 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3946 IEnumSTATSTG* iface)
3948 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3950 StgProperty rootProperty;
3951 BOOL readSuccessful;
3954 * Re-initialize the search stack to an empty stack
3956 This->stackSize = 0;
3959 * Read the root property from the storage.
3961 readSuccessful = StorageImpl_ReadProperty(
3962 This->parentStorage,
3963 This->firstPropertyNode,
3968 assert(rootProperty.sizeOfNameString!=0);
3971 * Push the search node in the search stack.
3973 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3979 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3980 IEnumSTATSTG* iface,
3981 IEnumSTATSTG** ppenum)
3983 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3985 IEnumSTATSTGImpl* newClone;
3988 * Perform a sanity check on the parameters.
3991 return E_INVALIDARG;
3993 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3994 This->firstPropertyNode);
3998 * The new clone enumeration must point to the same current node as
4001 newClone->stackSize = This->stackSize ;
4002 newClone->stackMaxSize = This->stackMaxSize ;
4003 newClone->stackToVisit =
4004 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
4007 newClone->stackToVisit,
4009 sizeof(ULONG) * newClone->stackSize);
4011 *ppenum = (IEnumSTATSTG*)newClone;
4014 * Don't forget to nail down a reference to the clone before
4017 IEnumSTATSTGImpl_AddRef(*ppenum);
4022 static INT IEnumSTATSTGImpl_FindParentProperty(
4023 IEnumSTATSTGImpl *This,
4024 ULONG childProperty,
4025 StgProperty *currentProperty,
4028 ULONG currentSearchNode;
4032 * To avoid the special case, get another pointer to a ULONG value if
4033 * the caller didn't supply one.
4036 thisNodeId = &foundNode;
4039 * Start with the node at the top of the stack.
4041 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4044 while (currentSearchNode!=PROPERTY_NULL)
4047 * Store the current node in the returned parameters
4049 *thisNodeId = currentSearchNode;
4052 * Remove the top node from the stack
4054 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4057 * Read the property from the storage.
4059 StorageImpl_ReadProperty(
4060 This->parentStorage,
4064 if (currentProperty->previousProperty == childProperty)
4065 return PROPERTY_RELATION_PREVIOUS;
4067 else if (currentProperty->nextProperty == childProperty)
4068 return PROPERTY_RELATION_NEXT;
4070 else if (currentProperty->dirProperty == childProperty)
4071 return PROPERTY_RELATION_DIR;
4074 * Push the next search node in the search stack.
4076 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4079 * continue the iteration.
4081 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4084 return PROPERTY_NULL;
4087 static ULONG IEnumSTATSTGImpl_FindProperty(
4088 IEnumSTATSTGImpl* This,
4089 const OLECHAR* lpszPropName,
4090 StgProperty* currentProperty)
4092 ULONG currentSearchNode;
4095 * Start with the node at the top of the stack.
4097 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4099 while (currentSearchNode!=PROPERTY_NULL)
4102 * Remove the top node from the stack
4104 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4107 * Read the property from the storage.
4109 StorageImpl_ReadProperty(This->parentStorage,
4113 if (propertyNameCmp(currentProperty->name, lpszPropName) == 0)
4114 return currentSearchNode;
4117 * Push the next search node in the search stack.
4119 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4122 * continue the iteration.
4124 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4127 return PROPERTY_NULL;
4130 static void IEnumSTATSTGImpl_PushSearchNode(
4131 IEnumSTATSTGImpl* This,
4134 StgProperty rootProperty;
4135 BOOL readSuccessful;
4138 * First, make sure we're not trying to push an unexisting node.
4140 if (nodeToPush==PROPERTY_NULL)
4144 * First push the node to the stack
4146 if (This->stackSize == This->stackMaxSize)
4148 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4150 This->stackToVisit = HeapReAlloc(
4154 sizeof(ULONG) * This->stackMaxSize);
4157 This->stackToVisit[This->stackSize] = nodeToPush;
4161 * Read the root property from the storage.
4163 readSuccessful = StorageImpl_ReadProperty(
4164 This->parentStorage,
4170 assert(rootProperty.sizeOfNameString!=0);
4173 * Push the previous search node in the search stack.
4175 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4179 static ULONG IEnumSTATSTGImpl_PopSearchNode(
4180 IEnumSTATSTGImpl* This,
4185 if (This->stackSize == 0)
4186 return PROPERTY_NULL;
4188 topNode = This->stackToVisit[This->stackSize-1];
4197 * Virtual function table for the IEnumSTATSTGImpl class.
4199 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4201 IEnumSTATSTGImpl_QueryInterface,
4202 IEnumSTATSTGImpl_AddRef,
4203 IEnumSTATSTGImpl_Release,
4204 IEnumSTATSTGImpl_Next,
4205 IEnumSTATSTGImpl_Skip,
4206 IEnumSTATSTGImpl_Reset,
4207 IEnumSTATSTGImpl_Clone
4210 /******************************************************************************
4211 ** IEnumSTATSTGImpl implementation
4214 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4215 StorageImpl* parentStorage,
4216 ULONG firstPropertyNode)
4218 IEnumSTATSTGImpl* newEnumeration;
4220 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4222 if (newEnumeration!=0)
4225 * Set-up the virtual function table and reference count.
4227 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4228 newEnumeration->ref = 0;
4231 * We want to nail-down the reference to the storage in case the
4232 * enumeration out-lives the storage in the client application.
4234 newEnumeration->parentStorage = parentStorage;
4235 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4237 newEnumeration->firstPropertyNode = firstPropertyNode;
4240 * Initialize the search stack
4242 newEnumeration->stackSize = 0;
4243 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4244 newEnumeration->stackToVisit =
4245 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4248 * Make sure the current node of the iterator is the first one.
4250 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4253 return newEnumeration;
4257 * Virtual function table for the Storage32InternalImpl class.
4259 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4261 StorageBaseImpl_QueryInterface,
4262 StorageBaseImpl_AddRef,
4263 StorageBaseImpl_Release,
4264 StorageBaseImpl_CreateStream,
4265 StorageBaseImpl_OpenStream,
4266 StorageImpl_CreateStorage,
4267 StorageBaseImpl_OpenStorage,
4269 StorageImpl_MoveElementTo,
4270 StorageInternalImpl_Commit,
4271 StorageInternalImpl_Revert,
4272 StorageBaseImpl_EnumElements,
4273 StorageImpl_DestroyElement,
4274 StorageBaseImpl_RenameElement,
4275 StorageImpl_SetElementTimes,
4276 StorageBaseImpl_SetClass,
4277 StorageImpl_SetStateBits,
4278 StorageBaseImpl_Stat
4281 /******************************************************************************
4282 ** Storage32InternalImpl implementation
4285 static StorageInternalImpl* StorageInternalImpl_Construct(
4286 StorageImpl* ancestorStorage,
4288 ULONG rootPropertyIndex)
4290 StorageInternalImpl* newStorage;
4293 * Allocate space for the new storage object
4295 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
4300 * Initialize the stream list
4302 list_init(&newStorage->base.strmHead);
4305 * Initialize the virtual function table.
4307 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4308 newStorage->base.v_destructor = StorageInternalImpl_Destroy;
4309 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4312 * Keep the ancestor storage pointer but do not nail a reference to it.
4314 newStorage->base.ancestorStorage = ancestorStorage;
4317 * Keep the index of the root property set for this storage,
4319 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4327 /******************************************************************************
4328 ** StorageUtl implementation
4331 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4335 memcpy(&tmp, buffer+offset, sizeof(WORD));
4336 *value = lendian16toh(tmp);
4339 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4341 value = htole16(value);
4342 memcpy(buffer+offset, &value, sizeof(WORD));
4345 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4349 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4350 *value = lendian32toh(tmp);
4353 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4355 value = htole32(value);
4356 memcpy(buffer+offset, &value, sizeof(DWORD));
4359 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4360 ULARGE_INTEGER* value)
4362 #ifdef WORDS_BIGENDIAN
4365 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4366 value->u.LowPart = htole32(tmp.u.HighPart);
4367 value->u.HighPart = htole32(tmp.u.LowPart);
4369 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4373 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4374 const ULARGE_INTEGER *value)
4376 #ifdef WORDS_BIGENDIAN
4379 tmp.u.LowPart = htole32(value->u.HighPart);
4380 tmp.u.HighPart = htole32(value->u.LowPart);
4381 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4383 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4387 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4389 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4390 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4391 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4393 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4396 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4398 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4399 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4400 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4402 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4405 void StorageUtl_CopyPropertyToSTATSTG(
4406 STATSTG* destination,
4407 const StgProperty* source,
4411 * The copy of the string occurs only when the flag is not set
4413 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4414 (source->name == NULL) ||
4415 (source->name[0] == 0) )
4417 destination->pwcsName = 0;
4421 destination->pwcsName =
4422 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4424 strcpyW(destination->pwcsName, source->name);
4427 switch (source->propertyType)
4429 case PROPTYPE_STORAGE:
4431 destination->type = STGTY_STORAGE;
4433 case PROPTYPE_STREAM:
4434 destination->type = STGTY_STREAM;
4437 destination->type = STGTY_STREAM;
4441 destination->cbSize = source->size;
4443 currentReturnStruct->mtime = {0}; TODO
4444 currentReturnStruct->ctime = {0};
4445 currentReturnStruct->atime = {0};
4447 destination->grfMode = 0;
4448 destination->grfLocksSupported = 0;
4449 destination->clsid = source->propertyUniqueID;
4450 destination->grfStateBits = 0;
4451 destination->reserved = 0;
4454 /******************************************************************************
4455 ** BlockChainStream implementation
4458 BlockChainStream* BlockChainStream_Construct(
4459 StorageImpl* parentStorage,
4460 ULONG* headOfStreamPlaceHolder,
4461 ULONG propertyIndex)
4463 BlockChainStream* newStream;
4466 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4468 newStream->parentStorage = parentStorage;
4469 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4470 newStream->ownerPropertyIndex = propertyIndex;
4471 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4472 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4473 newStream->numBlocks = 0;
4475 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4477 while (blockIndex != BLOCK_END_OF_CHAIN)
4479 newStream->numBlocks++;
4480 newStream->tailIndex = blockIndex;
4482 if(FAILED(StorageImpl_GetNextBlockInChain(
4487 HeapFree(GetProcessHeap(), 0, newStream);
4495 void BlockChainStream_Destroy(BlockChainStream* This)
4497 HeapFree(GetProcessHeap(), 0, This);
4500 /******************************************************************************
4501 * BlockChainStream_GetHeadOfChain
4503 * Returns the head of this stream chain.
4504 * Some special chains don't have properties, their heads are kept in
4505 * This->headOfStreamPlaceHolder.
4508 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4510 StgProperty chainProperty;
4511 BOOL readSuccessful;
4513 if (This->headOfStreamPlaceHolder != 0)
4514 return *(This->headOfStreamPlaceHolder);
4516 if (This->ownerPropertyIndex != PROPERTY_NULL)
4518 readSuccessful = StorageImpl_ReadProperty(
4519 This->parentStorage,
4520 This->ownerPropertyIndex,
4525 return chainProperty.startingBlock;
4529 return BLOCK_END_OF_CHAIN;
4532 /******************************************************************************
4533 * BlockChainStream_GetCount
4535 * Returns the number of blocks that comprises this chain.
4536 * This is not the size of the stream as the last block may not be full!
4539 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
4544 blockIndex = BlockChainStream_GetHeadOfChain(This);
4546 while (blockIndex != BLOCK_END_OF_CHAIN)
4550 if(FAILED(StorageImpl_GetNextBlockInChain(
4551 This->parentStorage,
4560 /******************************************************************************
4561 * BlockChainStream_ReadAt
4563 * Reads a specified number of bytes from this chain at the specified offset.
4564 * bytesRead may be NULL.
4565 * Failure will be returned if the specified number of bytes has not been read.
4567 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
4568 ULARGE_INTEGER offset,
4573 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4574 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4575 ULONG bytesToReadInBuffer;
4579 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
4582 * Find the first block in the stream that contains part of the buffer.
4584 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4585 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4586 (blockNoInSequence < This->lastBlockNoInSequence) )
4588 blockIndex = BlockChainStream_GetHeadOfChain(This);
4589 This->lastBlockNoInSequence = blockNoInSequence;
4593 ULONG temp = blockNoInSequence;
4595 blockIndex = This->lastBlockNoInSequenceIndex;
4596 blockNoInSequence -= This->lastBlockNoInSequence;
4597 This->lastBlockNoInSequence = temp;
4600 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4602 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4603 return STG_E_DOCFILECORRUPT;
4604 blockNoInSequence--;
4607 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4608 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
4610 This->lastBlockNoInSequenceIndex = blockIndex;
4613 * Start reading the buffer.
4616 bufferWalker = buffer;
4618 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4620 ULARGE_INTEGER ulOffset;
4623 * Calculate how many bytes we can copy from this big block.
4625 bytesToReadInBuffer =
4626 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4628 TRACE("block %i\n",blockIndex);
4629 ulOffset.u.HighPart = 0;
4630 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4633 StorageImpl_ReadAt(This->parentStorage,
4636 bytesToReadInBuffer,
4639 * Step to the next big block.
4641 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4642 return STG_E_DOCFILECORRUPT;
4644 bufferWalker += bytesReadAt;
4645 size -= bytesReadAt;
4646 *bytesRead += bytesReadAt;
4647 offsetInBlock = 0; /* There is no offset on the next block */
4649 if (bytesToReadInBuffer != bytesReadAt)
4653 return (size == 0) ? S_OK : STG_E_READFAULT;
4656 /******************************************************************************
4657 * BlockChainStream_WriteAt
4659 * Writes the specified number of bytes to this chain at the specified offset.
4660 * Will fail if not all specified number of bytes have been written.
4662 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
4663 ULARGE_INTEGER offset,
4666 ULONG* bytesWritten)
4668 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4669 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4672 const BYTE* bufferWalker;
4675 * Find the first block in the stream that contains part of the buffer.
4677 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4678 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4679 (blockNoInSequence < This->lastBlockNoInSequence) )
4681 blockIndex = BlockChainStream_GetHeadOfChain(This);
4682 This->lastBlockNoInSequence = blockNoInSequence;
4686 ULONG temp = blockNoInSequence;
4688 blockIndex = This->lastBlockNoInSequenceIndex;
4689 blockNoInSequence -= This->lastBlockNoInSequence;
4690 This->lastBlockNoInSequence = temp;
4693 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4695 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4697 return STG_E_DOCFILECORRUPT;
4698 blockNoInSequence--;
4701 This->lastBlockNoInSequenceIndex = blockIndex;
4703 /* BlockChainStream_SetSize should have already been called to ensure we have
4704 * enough blocks in the chain to write into */
4705 if (blockIndex == BLOCK_END_OF_CHAIN)
4707 ERR("not enough blocks in chain to write data\n");
4708 return STG_E_DOCFILECORRUPT;
4712 bufferWalker = buffer;
4714 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4716 ULARGE_INTEGER ulOffset;
4717 DWORD bytesWrittenAt;
4719 * Calculate how many bytes we can copy from this big block.
4722 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4724 TRACE("block %i\n",blockIndex);
4725 ulOffset.u.HighPart = 0;
4726 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4729 StorageImpl_WriteAt(This->parentStorage,
4736 * Step to the next big block.
4738 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4740 return STG_E_DOCFILECORRUPT;
4742 bufferWalker += bytesWrittenAt;
4743 size -= bytesWrittenAt;
4744 *bytesWritten += bytesWrittenAt;
4745 offsetInBlock = 0; /* There is no offset on the next block */
4747 if (bytesWrittenAt != bytesToWrite)
4751 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
4754 /******************************************************************************
4755 * BlockChainStream_Shrink
4757 * Shrinks this chain in the big block depot.
4759 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
4760 ULARGE_INTEGER newSize)
4762 ULONG blockIndex, extraBlock;
4767 * Reset the last accessed block cache.
4769 This->lastBlockNoInSequence = 0xFFFFFFFF;
4770 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4773 * Figure out how many blocks are needed to contain the new size
4775 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4777 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4780 blockIndex = BlockChainStream_GetHeadOfChain(This);
4783 * Go to the new end of chain
4785 while (count < numBlocks)
4787 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4793 /* Get the next block before marking the new end */
4794 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4798 /* Mark the new end of chain */
4799 StorageImpl_SetNextBlockInChain(
4800 This->parentStorage,
4802 BLOCK_END_OF_CHAIN);
4804 This->tailIndex = blockIndex;
4805 This->numBlocks = numBlocks;
4808 * Mark the extra blocks as free
4810 while (extraBlock != BLOCK_END_OF_CHAIN)
4812 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4815 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4816 extraBlock = blockIndex;
4822 /******************************************************************************
4823 * BlockChainStream_Enlarge
4825 * Grows this chain in the big block depot.
4827 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4828 ULARGE_INTEGER newSize)
4830 ULONG blockIndex, currentBlock;
4832 ULONG oldNumBlocks = 0;
4834 blockIndex = BlockChainStream_GetHeadOfChain(This);
4837 * Empty chain. Create the head.
4839 if (blockIndex == BLOCK_END_OF_CHAIN)
4841 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4842 StorageImpl_SetNextBlockInChain(This->parentStorage,
4844 BLOCK_END_OF_CHAIN);
4846 if (This->headOfStreamPlaceHolder != 0)
4848 *(This->headOfStreamPlaceHolder) = blockIndex;
4852 StgProperty chainProp;
4853 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4855 StorageImpl_ReadProperty(
4856 This->parentStorage,
4857 This->ownerPropertyIndex,
4860 chainProp.startingBlock = blockIndex;
4862 StorageImpl_WriteProperty(
4863 This->parentStorage,
4864 This->ownerPropertyIndex,
4868 This->tailIndex = blockIndex;
4869 This->numBlocks = 1;
4873 * Figure out how many blocks are needed to contain this stream
4875 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4877 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4881 * Go to the current end of chain
4883 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4885 currentBlock = blockIndex;
4887 while (blockIndex != BLOCK_END_OF_CHAIN)
4890 currentBlock = blockIndex;
4892 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4897 This->tailIndex = currentBlock;
4900 currentBlock = This->tailIndex;
4901 oldNumBlocks = This->numBlocks;
4904 * Add new blocks to the chain
4906 if (oldNumBlocks < newNumBlocks)
4908 while (oldNumBlocks < newNumBlocks)
4910 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4912 StorageImpl_SetNextBlockInChain(
4913 This->parentStorage,
4917 StorageImpl_SetNextBlockInChain(
4918 This->parentStorage,
4920 BLOCK_END_OF_CHAIN);
4922 currentBlock = blockIndex;
4926 This->tailIndex = blockIndex;
4927 This->numBlocks = newNumBlocks;
4933 /******************************************************************************
4934 * BlockChainStream_SetSize
4936 * Sets the size of this stream. The big block depot will be updated.
4937 * The file will grow if we grow the chain.
4939 * TODO: Free the actual blocks in the file when we shrink the chain.
4940 * Currently, the blocks are still in the file. So the file size
4941 * doesn't shrink even if we shrink streams.
4943 BOOL BlockChainStream_SetSize(
4944 BlockChainStream* This,
4945 ULARGE_INTEGER newSize)
4947 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4949 if (newSize.u.LowPart == size.u.LowPart)
4952 if (newSize.u.LowPart < size.u.LowPart)
4954 BlockChainStream_Shrink(This, newSize);
4958 BlockChainStream_Enlarge(This, newSize);
4964 /******************************************************************************
4965 * BlockChainStream_GetSize
4967 * Returns the size of this chain.
4968 * Will return the block count if this chain doesn't have a property.
4970 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4972 StgProperty chainProperty;
4974 if(This->headOfStreamPlaceHolder == NULL)
4977 * This chain is a data stream read the property and return
4978 * the appropriate size
4980 StorageImpl_ReadProperty(
4981 This->parentStorage,
4982 This->ownerPropertyIndex,
4985 return chainProperty.size;
4990 * this chain is a chain that does not have a property, figure out the
4991 * size by making the product number of used blocks times the
4994 ULARGE_INTEGER result;
4995 result.u.HighPart = 0;
4998 BlockChainStream_GetCount(This) *
4999 This->parentStorage->bigBlockSize;
5005 /******************************************************************************
5006 ** SmallBlockChainStream implementation
5009 SmallBlockChainStream* SmallBlockChainStream_Construct(
5010 StorageImpl* parentStorage,
5011 ULONG* headOfStreamPlaceHolder,
5012 ULONG propertyIndex)
5014 SmallBlockChainStream* newStream;
5016 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
5018 newStream->parentStorage = parentStorage;
5019 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
5020 newStream->ownerPropertyIndex = propertyIndex;
5025 void SmallBlockChainStream_Destroy(
5026 SmallBlockChainStream* This)
5028 HeapFree(GetProcessHeap(), 0, This);
5031 /******************************************************************************
5032 * SmallBlockChainStream_GetHeadOfChain
5034 * Returns the head of this chain of small blocks.
5036 static ULONG SmallBlockChainStream_GetHeadOfChain(
5037 SmallBlockChainStream* This)
5039 StgProperty chainProperty;
5040 BOOL readSuccessful;
5042 if (This->headOfStreamPlaceHolder != NULL)
5043 return *(This->headOfStreamPlaceHolder);
5045 if (This->ownerPropertyIndex)
5047 readSuccessful = StorageImpl_ReadProperty(
5048 This->parentStorage,
5049 This->ownerPropertyIndex,
5054 return chainProperty.startingBlock;
5059 return BLOCK_END_OF_CHAIN;
5062 /******************************************************************************
5063 * SmallBlockChainStream_GetNextBlockInChain
5065 * Returns the index of the next small block in this chain.
5068 * - BLOCK_END_OF_CHAIN: end of this chain
5069 * - BLOCK_UNUSED: small block 'blockIndex' is free
5071 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
5072 SmallBlockChainStream* This,
5074 ULONG* nextBlockInChain)
5076 ULARGE_INTEGER offsetOfBlockInDepot;
5081 *nextBlockInChain = BLOCK_END_OF_CHAIN;
5083 offsetOfBlockInDepot.u.HighPart = 0;
5084 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5087 * Read those bytes in the buffer from the small block file.
5089 res = BlockChainStream_ReadAt(
5090 This->parentStorage->smallBlockDepotChain,
5091 offsetOfBlockInDepot,
5098 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
5105 /******************************************************************************
5106 * SmallBlockChainStream_SetNextBlockInChain
5108 * Writes the index of the next block of the specified block in the small
5110 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5111 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5113 static void SmallBlockChainStream_SetNextBlockInChain(
5114 SmallBlockChainStream* This,
5118 ULARGE_INTEGER offsetOfBlockInDepot;
5122 offsetOfBlockInDepot.u.HighPart = 0;
5123 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5125 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
5128 * Read those bytes in the buffer from the small block file.
5130 BlockChainStream_WriteAt(
5131 This->parentStorage->smallBlockDepotChain,
5132 offsetOfBlockInDepot,
5138 /******************************************************************************
5139 * SmallBlockChainStream_FreeBlock
5141 * Flag small block 'blockIndex' as free in the small block depot.
5143 static void SmallBlockChainStream_FreeBlock(
5144 SmallBlockChainStream* This,
5147 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5150 /******************************************************************************
5151 * SmallBlockChainStream_GetNextFreeBlock
5153 * Returns the index of a free small block. The small block depot will be
5154 * enlarged if necessary. The small block chain will also be enlarged if
5157 static ULONG SmallBlockChainStream_GetNextFreeBlock(
5158 SmallBlockChainStream* This)
5160 ULARGE_INTEGER offsetOfBlockInDepot;
5163 ULONG blockIndex = 0;
5164 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5166 ULONG smallBlocksPerBigBlock;
5168 offsetOfBlockInDepot.u.HighPart = 0;
5171 * Scan the small block depot for a free block
5173 while (nextBlockIndex != BLOCK_UNUSED)
5175 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5177 res = BlockChainStream_ReadAt(
5178 This->parentStorage->smallBlockDepotChain,
5179 offsetOfBlockInDepot,
5185 * If we run out of space for the small block depot, enlarge it
5189 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5191 if (nextBlockIndex != BLOCK_UNUSED)
5197 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5199 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5200 ULONG nextBlock, newsbdIndex;
5201 BYTE smallBlockDepot[BIG_BLOCK_SIZE];
5203 nextBlock = sbdIndex;
5204 while (nextBlock != BLOCK_END_OF_CHAIN)
5206 sbdIndex = nextBlock;
5207 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5210 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5211 if (sbdIndex != BLOCK_END_OF_CHAIN)
5212 StorageImpl_SetNextBlockInChain(
5213 This->parentStorage,
5217 StorageImpl_SetNextBlockInChain(
5218 This->parentStorage,
5220 BLOCK_END_OF_CHAIN);
5223 * Initialize all the small blocks to free
5225 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5226 StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
5231 * We have just created the small block depot.
5233 StgProperty rootProp;
5237 * Save it in the header
5239 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5240 StorageImpl_SaveFileHeader(This->parentStorage);
5243 * And allocate the first big block that will contain small blocks
5246 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5248 StorageImpl_SetNextBlockInChain(
5249 This->parentStorage,
5251 BLOCK_END_OF_CHAIN);
5253 StorageImpl_ReadProperty(
5254 This->parentStorage,
5255 This->parentStorage->base.rootPropertySetIndex,
5258 rootProp.startingBlock = sbStartIndex;
5259 rootProp.size.u.HighPart = 0;
5260 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
5262 StorageImpl_WriteProperty(
5263 This->parentStorage,
5264 This->parentStorage->base.rootPropertySetIndex,
5268 StorageImpl_SaveFileHeader(This->parentStorage);
5272 smallBlocksPerBigBlock =
5273 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5276 * Verify if we have to allocate big blocks to contain small blocks
5278 if (blockIndex % smallBlocksPerBigBlock == 0)
5280 StgProperty rootProp;
5281 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5283 StorageImpl_ReadProperty(
5284 This->parentStorage,
5285 This->parentStorage->base.rootPropertySetIndex,
5288 if (rootProp.size.u.LowPart <
5289 (blocksRequired * This->parentStorage->bigBlockSize))
5291 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5293 BlockChainStream_SetSize(
5294 This->parentStorage->smallBlockRootChain,
5297 StorageImpl_WriteProperty(
5298 This->parentStorage,
5299 This->parentStorage->base.rootPropertySetIndex,
5307 /******************************************************************************
5308 * SmallBlockChainStream_ReadAt
5310 * Reads a specified number of bytes from this chain at the specified offset.
5311 * bytesRead may be NULL.
5312 * Failure will be returned if the specified number of bytes has not been read.
5314 HRESULT SmallBlockChainStream_ReadAt(
5315 SmallBlockChainStream* This,
5316 ULARGE_INTEGER offset,
5322 ULARGE_INTEGER offsetInBigBlockFile;
5323 ULONG blockNoInSequence =
5324 offset.u.LowPart / This->parentStorage->smallBlockSize;
5326 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5327 ULONG bytesToReadInBuffer;
5329 ULONG bytesReadFromBigBlockFile;
5333 * This should never happen on a small block file.
5335 assert(offset.u.HighPart==0);
5338 * Find the first block in the stream that contains part of the buffer.
5340 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5342 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5344 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5347 blockNoInSequence--;
5351 * Start reading the buffer.
5354 bufferWalker = buffer;
5356 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5359 * Calculate how many bytes we can copy from this small block.
5361 bytesToReadInBuffer =
5362 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5365 * Calculate the offset of the small block in the small block file.
5367 offsetInBigBlockFile.u.HighPart = 0;
5368 offsetInBigBlockFile.u.LowPart =
5369 blockIndex * This->parentStorage->smallBlockSize;
5371 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5374 * Read those bytes in the buffer from the small block file.
5375 * The small block has already been identified so it shouldn't fail
5376 * unless the file is corrupt.
5378 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5379 offsetInBigBlockFile,
5380 bytesToReadInBuffer,
5382 &bytesReadFromBigBlockFile);
5388 * Step to the next big block.
5390 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5392 return STG_E_DOCFILECORRUPT;
5394 bufferWalker += bytesReadFromBigBlockFile;
5395 size -= bytesReadFromBigBlockFile;
5396 *bytesRead += bytesReadFromBigBlockFile;
5397 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5400 return (size == 0) ? S_OK : STG_E_READFAULT;
5403 /******************************************************************************
5404 * SmallBlockChainStream_WriteAt
5406 * Writes the specified number of bytes to this chain at the specified offset.
5407 * Will fail if not all specified number of bytes have been written.
5409 HRESULT SmallBlockChainStream_WriteAt(
5410 SmallBlockChainStream* This,
5411 ULARGE_INTEGER offset,
5414 ULONG* bytesWritten)
5416 ULARGE_INTEGER offsetInBigBlockFile;
5417 ULONG blockNoInSequence =
5418 offset.u.LowPart / This->parentStorage->smallBlockSize;
5420 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5421 ULONG bytesToWriteInBuffer;
5423 ULONG bytesWrittenToBigBlockFile;
5424 const BYTE* bufferWalker;
5428 * This should never happen on a small block file.
5430 assert(offset.u.HighPart==0);
5433 * Find the first block in the stream that contains part of the buffer.
5435 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5437 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5439 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5440 return STG_E_DOCFILECORRUPT;
5441 blockNoInSequence--;
5445 * Start writing the buffer.
5448 bufferWalker = buffer;
5449 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5452 * Calculate how many bytes we can copy to this small block.
5454 bytesToWriteInBuffer =
5455 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5458 * Calculate the offset of the small block in the small block file.
5460 offsetInBigBlockFile.u.HighPart = 0;
5461 offsetInBigBlockFile.u.LowPart =
5462 blockIndex * This->parentStorage->smallBlockSize;
5464 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5467 * Write those bytes in the buffer to the small block file.
5469 res = BlockChainStream_WriteAt(
5470 This->parentStorage->smallBlockRootChain,
5471 offsetInBigBlockFile,
5472 bytesToWriteInBuffer,
5474 &bytesWrittenToBigBlockFile);
5479 * Step to the next big block.
5481 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5484 bufferWalker += bytesWrittenToBigBlockFile;
5485 size -= bytesWrittenToBigBlockFile;
5486 *bytesWritten += bytesWrittenToBigBlockFile;
5487 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
5490 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5493 /******************************************************************************
5494 * SmallBlockChainStream_Shrink
5496 * Shrinks this chain in the small block depot.
5498 static BOOL SmallBlockChainStream_Shrink(
5499 SmallBlockChainStream* This,
5500 ULARGE_INTEGER newSize)
5502 ULONG blockIndex, extraBlock;
5506 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5508 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5511 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5514 * Go to the new end of chain
5516 while (count < numBlocks)
5518 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5525 * If the count is 0, we have a special case, the head of the chain was
5530 StgProperty chainProp;
5532 StorageImpl_ReadProperty(This->parentStorage,
5533 This->ownerPropertyIndex,
5536 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5538 StorageImpl_WriteProperty(This->parentStorage,
5539 This->ownerPropertyIndex,
5543 * We start freeing the chain at the head block.
5545 extraBlock = blockIndex;
5549 /* Get the next block before marking the new end */
5550 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5554 /* Mark the new end of chain */
5555 SmallBlockChainStream_SetNextBlockInChain(
5558 BLOCK_END_OF_CHAIN);
5562 * Mark the extra blocks as free
5564 while (extraBlock != BLOCK_END_OF_CHAIN)
5566 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5569 SmallBlockChainStream_FreeBlock(This, extraBlock);
5570 extraBlock = blockIndex;
5576 /******************************************************************************
5577 * SmallBlockChainStream_Enlarge
5579 * Grows this chain in the small block depot.
5581 static BOOL SmallBlockChainStream_Enlarge(
5582 SmallBlockChainStream* This,
5583 ULARGE_INTEGER newSize)
5585 ULONG blockIndex, currentBlock;
5587 ULONG oldNumBlocks = 0;
5589 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5592 * Empty chain. Create the head.
5594 if (blockIndex == BLOCK_END_OF_CHAIN)
5596 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5597 SmallBlockChainStream_SetNextBlockInChain(
5600 BLOCK_END_OF_CHAIN);
5602 if (This->headOfStreamPlaceHolder != NULL)
5604 *(This->headOfStreamPlaceHolder) = blockIndex;
5608 StgProperty chainProp;
5610 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5613 chainProp.startingBlock = blockIndex;
5615 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5620 currentBlock = blockIndex;
5623 * Figure out how many blocks are needed to contain this stream
5625 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5627 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5631 * Go to the current end of chain
5633 while (blockIndex != BLOCK_END_OF_CHAIN)
5636 currentBlock = blockIndex;
5637 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5642 * Add new blocks to the chain
5644 while (oldNumBlocks < newNumBlocks)
5646 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5647 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5649 SmallBlockChainStream_SetNextBlockInChain(
5652 BLOCK_END_OF_CHAIN);
5654 currentBlock = blockIndex;
5661 /******************************************************************************
5662 * SmallBlockChainStream_SetSize
5664 * Sets the size of this stream.
5665 * The file will grow if we grow the chain.
5667 * TODO: Free the actual blocks in the file when we shrink the chain.
5668 * Currently, the blocks are still in the file. So the file size
5669 * doesn't shrink even if we shrink streams.
5671 BOOL SmallBlockChainStream_SetSize(
5672 SmallBlockChainStream* This,
5673 ULARGE_INTEGER newSize)
5675 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5677 if (newSize.u.LowPart == size.u.LowPart)
5680 if (newSize.u.LowPart < size.u.LowPart)
5682 SmallBlockChainStream_Shrink(This, newSize);
5686 SmallBlockChainStream_Enlarge(This, newSize);
5692 /******************************************************************************
5693 * SmallBlockChainStream_GetCount
5695 * Returns the number of small blocks that comprises this chain.
5696 * This is not the size of the stream as the last block may not be full!
5699 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5704 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5706 while(blockIndex != BLOCK_END_OF_CHAIN)
5710 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
5711 blockIndex, &blockIndex)))
5718 /******************************************************************************
5719 * SmallBlockChainStream_GetSize
5721 * Returns the size of this chain.
5723 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5725 StgProperty chainProperty;
5727 if(This->headOfStreamPlaceHolder != NULL)
5729 ULARGE_INTEGER result;
5730 result.u.HighPart = 0;
5732 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
5733 This->parentStorage->smallBlockSize;
5738 StorageImpl_ReadProperty(
5739 This->parentStorage,
5740 This->ownerPropertyIndex,
5743 return chainProperty.size;
5746 /******************************************************************************
5747 * StgCreateDocfile [OLE32.@]
5748 * Creates a new compound file storage object
5751 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5752 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5753 * reserved [ ?] unused?, usually 0
5754 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5757 * S_OK if the file was successfully created
5758 * some STG_E_ value if error
5760 * if pwcsName is NULL, create file with new unique name
5761 * the function can returns
5762 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5765 HRESULT WINAPI StgCreateDocfile(
5769 IStorage **ppstgOpen)
5771 StorageImpl* newStorage = 0;
5772 HANDLE hFile = INVALID_HANDLE_VALUE;
5773 HRESULT hr = STG_E_INVALIDFLAG;
5777 DWORD fileAttributes;
5778 WCHAR tempFileName[MAX_PATH];
5780 TRACE("(%s, %x, %d, %p)\n",
5781 debugstr_w(pwcsName), grfMode,
5782 reserved, ppstgOpen);
5785 * Validate the parameters
5788 return STG_E_INVALIDPOINTER;
5790 return STG_E_INVALIDPARAMETER;
5792 /* if no share mode given then DENY_NONE is the default */
5793 if (STGM_SHARE_MODE(grfMode) == 0)
5794 grfMode |= STGM_SHARE_DENY_NONE;
5797 * Validate the STGM flags
5799 if ( FAILED( validateSTGM(grfMode) ))
5802 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5803 switch(STGM_ACCESS_MODE(grfMode))
5806 case STGM_READWRITE:
5812 /* in direct mode, can only use SHARE_EXCLUSIVE */
5813 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5816 /* but in transacted mode, any share mode is valid */
5819 * Generate a unique name.
5823 WCHAR tempPath[MAX_PATH];
5824 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5826 memset(tempPath, 0, sizeof(tempPath));
5827 memset(tempFileName, 0, sizeof(tempFileName));
5829 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5832 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5833 pwcsName = tempFileName;
5836 hr = STG_E_INSUFFICIENTMEMORY;
5840 creationMode = TRUNCATE_EXISTING;
5844 creationMode = GetCreationModeFromSTGM(grfMode);
5848 * Interpret the STGM value grfMode
5850 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
5851 accessMode = GetAccessModeFromSTGM(grfMode);
5853 if (grfMode & STGM_DELETEONRELEASE)
5854 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5856 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5858 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
5859 FIXME("Storage share mode not implemented.\n");
5861 if (grfMode & STGM_TRANSACTED)
5862 FIXME("Transacted mode not implemented.\n");
5865 * Initialize the "out" parameter.
5869 hFile = CreateFileW(pwcsName,
5877 if (hFile == INVALID_HANDLE_VALUE)
5879 if(GetLastError() == ERROR_FILE_EXISTS)
5880 hr = STG_E_FILEALREADYEXISTS;
5887 * Allocate and initialize the new IStorage32object.
5889 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5891 if (newStorage == 0)
5893 hr = STG_E_INSUFFICIENTMEMORY;
5897 hr = StorageImpl_Construct(
5908 HeapFree(GetProcessHeap(), 0, newStorage);
5913 * Get an "out" pointer for the caller.
5915 hr = StorageBaseImpl_QueryInterface(
5916 (IStorage*)newStorage,
5920 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
5925 /******************************************************************************
5926 * StgCreateStorageEx [OLE32.@]
5928 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5930 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5931 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5933 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5935 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5936 return STG_E_INVALIDPARAMETER;
5939 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5941 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5942 return STG_E_INVALIDPARAMETER;
5945 if (stgfmt == STGFMT_FILE)
5947 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5948 return STG_E_INVALIDPARAMETER;
5951 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5953 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5954 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5957 ERR("Invalid stgfmt argument\n");
5958 return STG_E_INVALIDPARAMETER;
5961 /******************************************************************************
5962 * StgCreatePropSetStg [OLE32.@]
5964 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5965 IPropertySetStorage **ppPropSetStg)
5969 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
5971 hr = STG_E_INVALIDPARAMETER;
5973 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5974 (void**)ppPropSetStg);
5978 /******************************************************************************
5979 * StgOpenStorageEx [OLE32.@]
5981 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5983 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5984 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5986 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5988 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5989 return STG_E_INVALIDPARAMETER;
5995 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5996 return STG_E_INVALIDPARAMETER;
5998 case STGFMT_STORAGE:
6001 case STGFMT_DOCFILE:
6002 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
6004 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
6005 return STG_E_INVALIDPARAMETER;
6007 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
6011 WARN("STGFMT_ANY assuming storage\n");
6015 return STG_E_INVALIDPARAMETER;
6018 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
6022 /******************************************************************************
6023 * StgOpenStorage [OLE32.@]
6025 HRESULT WINAPI StgOpenStorage(
6026 const OLECHAR *pwcsName,
6027 IStorage *pstgPriority,
6031 IStorage **ppstgOpen)
6033 StorageImpl* newStorage = 0;
6038 WCHAR fullname[MAX_PATH];
6040 TRACE("(%s, %p, %x, %p, %d, %p)\n",
6041 debugstr_w(pwcsName), pstgPriority, grfMode,
6042 snbExclude, reserved, ppstgOpen);
6045 * Perform sanity checks
6049 hr = STG_E_INVALIDNAME;
6055 hr = STG_E_INVALIDPOINTER;
6061 hr = STG_E_INVALIDPARAMETER;
6065 if (grfMode & STGM_PRIORITY)
6067 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
6068 return STG_E_INVALIDFLAG;
6069 if (grfMode & STGM_DELETEONRELEASE)
6070 return STG_E_INVALIDFUNCTION;
6071 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
6072 return STG_E_INVALIDFLAG;
6073 grfMode &= ~0xf0; /* remove the existing sharing mode */
6074 grfMode |= STGM_SHARE_DENY_NONE;
6076 /* STGM_PRIORITY stops other IStorage objects on the same file from
6077 * committing until the STGM_PRIORITY IStorage is closed. it also
6078 * stops non-transacted mode StgOpenStorage calls with write access from
6079 * succeeding. obviously, both of these cannot be achieved through just
6080 * file share flags */
6081 FIXME("STGM_PRIORITY mode not implemented correctly\n");
6085 * Validate the sharing mode
6087 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
6088 switch(STGM_SHARE_MODE(grfMode))
6090 case STGM_SHARE_EXCLUSIVE:
6091 case STGM_SHARE_DENY_WRITE:
6094 hr = STG_E_INVALIDFLAG;
6099 * Validate the STGM flags
6101 if ( FAILED( validateSTGM(grfMode) ) ||
6102 (grfMode&STGM_CREATE))
6104 hr = STG_E_INVALIDFLAG;
6108 /* shared reading requires transacted mode */
6109 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
6110 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
6111 !(grfMode&STGM_TRANSACTED) )
6113 hr = STG_E_INVALIDFLAG;
6118 * Interpret the STGM value grfMode
6120 shareMode = GetShareModeFromSTGM(grfMode);
6121 accessMode = GetAccessModeFromSTGM(grfMode);
6124 * Initialize the "out" parameter.
6128 hFile = CreateFileW( pwcsName,
6133 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
6136 if (hFile==INVALID_HANDLE_VALUE)
6138 DWORD last_error = GetLastError();
6144 case ERROR_FILE_NOT_FOUND:
6145 hr = STG_E_FILENOTFOUND;
6148 case ERROR_PATH_NOT_FOUND:
6149 hr = STG_E_PATHNOTFOUND;
6152 case ERROR_ACCESS_DENIED:
6153 case ERROR_WRITE_PROTECT:
6154 hr = STG_E_ACCESSDENIED;
6157 case ERROR_SHARING_VIOLATION:
6158 hr = STG_E_SHAREVIOLATION;
6169 * Refuse to open the file if it's too small to be a structured storage file
6170 * FIXME: verify the file when reading instead of here
6172 if (GetFileSize(hFile, NULL) < 0x100)
6175 hr = STG_E_FILEALREADYEXISTS;
6180 * Allocate and initialize the new IStorage32object.
6182 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6184 if (newStorage == 0)
6186 hr = STG_E_INSUFFICIENTMEMORY;
6190 /* Initialize the storage */
6191 hr = StorageImpl_Construct(
6202 HeapFree(GetProcessHeap(), 0, newStorage);
6204 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6206 if(hr == STG_E_INVALIDHEADER)
6207 hr = STG_E_FILEALREADYEXISTS;
6211 /* prepare the file name string given in lieu of the root property name */
6212 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
6213 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
6214 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
6217 * Get an "out" pointer for the caller.
6219 hr = StorageBaseImpl_QueryInterface(
6220 (IStorage*)newStorage,
6225 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6229 /******************************************************************************
6230 * StgCreateDocfileOnILockBytes [OLE32.@]
6232 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6236 IStorage** ppstgOpen)
6238 StorageImpl* newStorage = 0;
6242 * Validate the parameters
6244 if ((ppstgOpen == 0) || (plkbyt == 0))
6245 return STG_E_INVALIDPOINTER;
6248 * Allocate and initialize the new IStorage object.
6250 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6252 if (newStorage == 0)
6253 return STG_E_INSUFFICIENTMEMORY;
6255 hr = StorageImpl_Construct(
6266 HeapFree(GetProcessHeap(), 0, newStorage);
6271 * Get an "out" pointer for the caller.
6273 hr = StorageBaseImpl_QueryInterface(
6274 (IStorage*)newStorage,
6281 /******************************************************************************
6282 * StgOpenStorageOnILockBytes [OLE32.@]
6284 HRESULT WINAPI StgOpenStorageOnILockBytes(
6286 IStorage *pstgPriority,
6290 IStorage **ppstgOpen)
6292 StorageImpl* newStorage = 0;
6296 * Perform a sanity check
6298 if ((plkbyt == 0) || (ppstgOpen == 0))
6299 return STG_E_INVALIDPOINTER;
6302 * Validate the STGM flags
6304 if ( FAILED( validateSTGM(grfMode) ))
6305 return STG_E_INVALIDFLAG;
6308 * Initialize the "out" parameter.
6313 * Allocate and initialize the new IStorage object.
6315 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6317 if (newStorage == 0)
6318 return STG_E_INSUFFICIENTMEMORY;
6320 hr = StorageImpl_Construct(
6331 HeapFree(GetProcessHeap(), 0, newStorage);
6336 * Get an "out" pointer for the caller.
6338 hr = StorageBaseImpl_QueryInterface(
6339 (IStorage*)newStorage,
6346 /******************************************************************************
6347 * StgSetTimes [ole32.@]
6348 * StgSetTimes [OLE32.@]
6352 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6353 FILETIME const *patime, FILETIME const *pmtime)
6355 IStorage *stg = NULL;
6358 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6360 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6364 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6365 IStorage_Release(stg);
6371 /******************************************************************************
6372 * StgIsStorageILockBytes [OLE32.@]
6374 * Determines if the ILockBytes contains a storage object.
6376 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6379 ULARGE_INTEGER offset;
6381 offset.u.HighPart = 0;
6382 offset.u.LowPart = 0;
6384 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6386 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6392 /******************************************************************************
6393 * WriteClassStg [OLE32.@]
6395 * This method will store the specified CLSID in the specified storage object
6397 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6402 return E_INVALIDARG;
6405 return STG_E_INVALIDPOINTER;
6407 hRes = IStorage_SetClass(pStg, rclsid);
6412 /***********************************************************************
6413 * ReadClassStg (OLE32.@)
6415 * This method reads the CLSID previously written to a storage object with
6416 * the WriteClassStg.
6419 * pstg [I] IStorage pointer
6420 * pclsid [O] Pointer to where the CLSID is written
6424 * Failure: HRESULT code.
6426 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6431 TRACE("(%p, %p)\n", pstg, pclsid);
6433 if(!pstg || !pclsid)
6434 return E_INVALIDARG;
6437 * read a STATSTG structure (contains the clsid) from the storage
6439 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
6442 *pclsid=pstatstg.clsid;
6447 /***********************************************************************
6448 * OleLoadFromStream (OLE32.@)
6450 * This function loads an object from stream
6452 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6456 LPPERSISTSTREAM xstm;
6458 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6460 res=ReadClassStm(pStm,&clsid);
6463 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6466 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6468 IUnknown_Release((IUnknown*)*ppvObj);
6471 res=IPersistStream_Load(xstm,pStm);
6472 IPersistStream_Release(xstm);
6473 /* FIXME: all refcounts ok at this point? I think they should be:
6476 * xstm : 0 (released)
6481 /***********************************************************************
6482 * OleSaveToStream (OLE32.@)
6484 * This function saves an object with the IPersistStream interface on it
6485 * to the specified stream.
6487 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6493 TRACE("(%p,%p)\n",pPStm,pStm);
6495 res=IPersistStream_GetClassID(pPStm,&clsid);
6497 if (SUCCEEDED(res)){
6499 res=WriteClassStm(pStm,&clsid);
6503 res=IPersistStream_Save(pPStm,pStm,TRUE);
6506 TRACE("Finished Save\n");
6510 /****************************************************************************
6511 * This method validate a STGM parameter that can contain the values below
6513 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6514 * The stgm values contained in 0xffff0000 are bitmasks.
6516 * STGM_DIRECT 0x00000000
6517 * STGM_TRANSACTED 0x00010000
6518 * STGM_SIMPLE 0x08000000
6520 * STGM_READ 0x00000000
6521 * STGM_WRITE 0x00000001
6522 * STGM_READWRITE 0x00000002
6524 * STGM_SHARE_DENY_NONE 0x00000040
6525 * STGM_SHARE_DENY_READ 0x00000030
6526 * STGM_SHARE_DENY_WRITE 0x00000020
6527 * STGM_SHARE_EXCLUSIVE 0x00000010
6529 * STGM_PRIORITY 0x00040000
6530 * STGM_DELETEONRELEASE 0x04000000
6532 * STGM_CREATE 0x00001000
6533 * STGM_CONVERT 0x00020000
6534 * STGM_FAILIFTHERE 0x00000000
6536 * STGM_NOSCRATCH 0x00100000
6537 * STGM_NOSNAPSHOT 0x00200000
6539 static HRESULT validateSTGM(DWORD stgm)
6541 DWORD access = STGM_ACCESS_MODE(stgm);
6542 DWORD share = STGM_SHARE_MODE(stgm);
6543 DWORD create = STGM_CREATE_MODE(stgm);
6545 if (stgm&~STGM_KNOWN_FLAGS)
6547 ERR("unknown flags %08x\n", stgm);
6555 case STGM_READWRITE:
6563 case STGM_SHARE_DENY_NONE:
6564 case STGM_SHARE_DENY_READ:
6565 case STGM_SHARE_DENY_WRITE:
6566 case STGM_SHARE_EXCLUSIVE:
6575 case STGM_FAILIFTHERE:
6582 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6584 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6588 * STGM_CREATE | STGM_CONVERT
6589 * if both are false, STGM_FAILIFTHERE is set to TRUE
6591 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6595 * STGM_NOSCRATCH requires STGM_TRANSACTED
6597 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6601 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6602 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6604 if ( (stgm & STGM_NOSNAPSHOT) &&
6605 (!(stgm & STGM_TRANSACTED) ||
6606 share == STGM_SHARE_EXCLUSIVE ||
6607 share == STGM_SHARE_DENY_WRITE) )
6613 /****************************************************************************
6614 * GetShareModeFromSTGM
6616 * This method will return a share mode flag from a STGM value.
6617 * The STGM value is assumed valid.
6619 static DWORD GetShareModeFromSTGM(DWORD stgm)
6621 switch (STGM_SHARE_MODE(stgm))
6623 case STGM_SHARE_DENY_NONE:
6624 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6625 case STGM_SHARE_DENY_READ:
6626 return FILE_SHARE_WRITE;
6627 case STGM_SHARE_DENY_WRITE:
6628 return FILE_SHARE_READ;
6629 case STGM_SHARE_EXCLUSIVE:
6632 ERR("Invalid share mode!\n");
6637 /****************************************************************************
6638 * GetAccessModeFromSTGM
6640 * This method will return an access mode flag from a STGM value.
6641 * The STGM value is assumed valid.
6643 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6645 switch (STGM_ACCESS_MODE(stgm))
6648 return GENERIC_READ;
6650 case STGM_READWRITE:
6651 return GENERIC_READ | GENERIC_WRITE;
6653 ERR("Invalid access mode!\n");
6658 /****************************************************************************
6659 * GetCreationModeFromSTGM
6661 * This method will return a creation mode flag from a STGM value.
6662 * The STGM value is assumed valid.
6664 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6666 switch(STGM_CREATE_MODE(stgm))
6669 return CREATE_ALWAYS;
6671 FIXME("STGM_CONVERT not implemented!\n");
6673 case STGM_FAILIFTHERE:
6676 ERR("Invalid create mode!\n");
6682 /*************************************************************************
6683 * OLECONVERT_LoadOLE10 [Internal]
6685 * Loads the OLE10 STREAM to memory
6688 * pOleStream [I] The OLESTREAM
6689 * pData [I] Data Structure for the OLESTREAM Data
6693 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6694 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
6697 * This function is used by OleConvertOLESTREAMToIStorage only.
6699 * Memory allocated for pData must be freed by the caller
6701 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6704 HRESULT hRes = S_OK;
6708 pData->pData = NULL;
6709 pData->pstrOleObjFileName = NULL;
6711 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6714 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6715 if(dwSize != sizeof(pData->dwOleID))
6717 hRes = CONVERT10_E_OLESTREAM_GET;
6719 else if(pData->dwOleID != OLESTREAM_ID)
6721 hRes = CONVERT10_E_OLESTREAM_FMT;
6732 /* Get the TypeID... more info needed for this field */
6733 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6734 if(dwSize != sizeof(pData->dwTypeID))
6736 hRes = CONVERT10_E_OLESTREAM_GET;
6741 if(pData->dwTypeID != 0)
6743 /* Get the length of the OleTypeName */
6744 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6745 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6747 hRes = CONVERT10_E_OLESTREAM_GET;
6752 if(pData->dwOleTypeNameLength > 0)
6754 /* Get the OleTypeName */
6755 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
6756 if(dwSize != pData->dwOleTypeNameLength)
6758 hRes = CONVERT10_E_OLESTREAM_GET;
6764 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6765 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6767 hRes = CONVERT10_E_OLESTREAM_GET;
6771 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6772 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6773 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6774 if(pData->pstrOleObjFileName)
6776 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
6777 if(dwSize != pData->dwOleObjFileNameLength)
6779 hRes = CONVERT10_E_OLESTREAM_GET;
6783 hRes = CONVERT10_E_OLESTREAM_GET;
6788 /* Get the Width of the Metafile */
6789 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6790 if(dwSize != sizeof(pData->dwMetaFileWidth))
6792 hRes = CONVERT10_E_OLESTREAM_GET;
6796 /* Get the Height of the Metafile */
6797 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6798 if(dwSize != sizeof(pData->dwMetaFileHeight))
6800 hRes = CONVERT10_E_OLESTREAM_GET;
6806 /* Get the Length of the Data */
6807 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6808 if(dwSize != sizeof(pData->dwDataLength))
6810 hRes = CONVERT10_E_OLESTREAM_GET;
6814 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
6816 if(!bStrem1) /* if it is a second OLE stream data */
6818 pData->dwDataLength -= 8;
6819 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
6820 if(dwSize != sizeof(pData->strUnknown))
6822 hRes = CONVERT10_E_OLESTREAM_GET;
6828 if(pData->dwDataLength > 0)
6830 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6832 /* Get Data (ex. IStorage, Metafile, or BMP) */
6835 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6836 if(dwSize != pData->dwDataLength)
6838 hRes = CONVERT10_E_OLESTREAM_GET;
6843 hRes = CONVERT10_E_OLESTREAM_GET;
6852 /*************************************************************************
6853 * OLECONVERT_SaveOLE10 [Internal]
6855 * Saves the OLE10 STREAM From memory
6858 * pData [I] Data Structure for the OLESTREAM Data
6859 * pOleStream [I] The OLESTREAM to save
6863 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6866 * This function is used by OleConvertIStorageToOLESTREAM only.
6869 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6872 HRESULT hRes = S_OK;
6876 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6877 if(dwSize != sizeof(pData->dwOleID))
6879 hRes = CONVERT10_E_OLESTREAM_PUT;
6884 /* Set the TypeID */
6885 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6886 if(dwSize != sizeof(pData->dwTypeID))
6888 hRes = CONVERT10_E_OLESTREAM_PUT;
6892 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6894 /* Set the Length of the OleTypeName */
6895 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6896 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6898 hRes = CONVERT10_E_OLESTREAM_PUT;
6903 if(pData->dwOleTypeNameLength > 0)
6905 /* Set the OleTypeName */
6906 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
6907 if(dwSize != pData->dwOleTypeNameLength)
6909 hRes = CONVERT10_E_OLESTREAM_PUT;
6916 /* Set the width of the Metafile */
6917 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6918 if(dwSize != sizeof(pData->dwMetaFileWidth))
6920 hRes = CONVERT10_E_OLESTREAM_PUT;
6926 /* Set the height of the Metafile */
6927 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6928 if(dwSize != sizeof(pData->dwMetaFileHeight))
6930 hRes = CONVERT10_E_OLESTREAM_PUT;
6936 /* Set the length of the Data */
6937 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6938 if(dwSize != sizeof(pData->dwDataLength))
6940 hRes = CONVERT10_E_OLESTREAM_PUT;
6946 if(pData->dwDataLength > 0)
6948 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6949 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6950 if(dwSize != pData->dwDataLength)
6952 hRes = CONVERT10_E_OLESTREAM_PUT;
6960 /*************************************************************************
6961 * OLECONVERT_GetOLE20FromOLE10[Internal]
6963 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6964 * opens it, and copies the content to the dest IStorage for
6965 * OleConvertOLESTREAMToIStorage
6969 * pDestStorage [I] The IStorage to copy the data to
6970 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6971 * nBufferLength [I] The size of the buffer
6980 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
6984 IStorage *pTempStorage;
6985 DWORD dwNumOfBytesWritten;
6986 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6987 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6989 /* Create a temp File */
6990 GetTempPathW(MAX_PATH, wstrTempDir);
6991 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6992 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6994 if(hFile != INVALID_HANDLE_VALUE)
6996 /* Write IStorage Data to File */
6997 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
7000 /* Open and copy temp storage to the Dest Storage */
7001 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
7004 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
7005 StorageBaseImpl_Release(pTempStorage);
7007 DeleteFileW(wstrTempFile);
7012 /*************************************************************************
7013 * OLECONVERT_WriteOLE20ToBuffer [Internal]
7015 * Saves the OLE10 STREAM From memory
7018 * pStorage [I] The Src IStorage to copy
7019 * pData [I] The Dest Memory to write to.
7022 * The size in bytes allocated for pData
7025 * Memory allocated for pData must be freed by the caller
7027 * Used by OleConvertIStorageToOLESTREAM only.
7030 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
7034 DWORD nDataLength = 0;
7035 IStorage *pTempStorage;
7036 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
7037 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
7041 /* Create temp Storage */
7042 GetTempPathW(MAX_PATH, wstrTempDir);
7043 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
7044 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
7048 /* Copy Src Storage to the Temp Storage */
7049 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
7050 StorageBaseImpl_Release(pTempStorage);
7052 /* Open Temp Storage as a file and copy to memory */
7053 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7054 if(hFile != INVALID_HANDLE_VALUE)
7056 nDataLength = GetFileSize(hFile, NULL);
7057 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
7058 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
7061 DeleteFileW(wstrTempFile);
7066 /*************************************************************************
7067 * OLECONVERT_CreateOleStream [Internal]
7069 * Creates the "\001OLE" stream in the IStorage if necessary.
7072 * pStorage [I] Dest storage to create the stream in
7078 * This function is used by OleConvertOLESTREAMToIStorage only.
7080 * This stream is still unknown, MS Word seems to have extra data
7081 * but since the data is stored in the OLESTREAM there should be
7082 * no need to recreate the stream. If the stream is manually
7083 * deleted it will create it with this default data.
7086 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
7090 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
7091 BYTE pOleStreamHeader [] =
7093 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
7094 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7095 0x00, 0x00, 0x00, 0x00
7098 /* Create stream if not present */
7099 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7100 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7104 /* Write default Data */
7105 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
7106 IStream_Release(pStream);
7110 /* write a string to a stream, preceded by its length */
7111 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
7118 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
7119 r = IStream_Write( stm, &len, sizeof(len), NULL);
7124 str = CoTaskMemAlloc( len );
7125 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
7126 r = IStream_Write( stm, str, len, NULL);
7127 CoTaskMemFree( str );
7131 /* read a string preceded by its length from a stream */
7132 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
7135 DWORD len, count = 0;
7139 r = IStream_Read( stm, &len, sizeof(len), &count );
7142 if( count != sizeof(len) )
7143 return E_OUTOFMEMORY;
7145 TRACE("%d bytes\n",len);
7147 str = CoTaskMemAlloc( len );
7149 return E_OUTOFMEMORY;
7151 r = IStream_Read( stm, str, len, &count );
7156 CoTaskMemFree( str );
7157 return E_OUTOFMEMORY;
7160 TRACE("Read string %s\n",debugstr_an(str,len));
7162 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7163 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7165 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7166 CoTaskMemFree( str );
7174 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7175 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7179 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7181 static const BYTE unknown1[12] =
7182 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7183 0xFF, 0xFF, 0xFF, 0xFF};
7184 static const BYTE unknown2[16] =
7185 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7186 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7188 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7189 debugstr_w(lpszUserType), debugstr_w(szClipName),
7190 debugstr_w(szProgIDName));
7192 /* Create a CompObj stream */
7193 r = IStorage_CreateStream(pstg, szwStreamName,
7194 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7198 /* Write CompObj Structure to stream */
7199 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7201 if( SUCCEEDED( r ) )
7202 r = WriteClassStm( pstm, clsid );
7204 if( SUCCEEDED( r ) )
7205 r = STREAM_WriteString( pstm, lpszUserType );
7206 if( SUCCEEDED( r ) )
7207 r = STREAM_WriteString( pstm, szClipName );
7208 if( SUCCEEDED( r ) )
7209 r = STREAM_WriteString( pstm, szProgIDName );
7210 if( SUCCEEDED( r ) )
7211 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7213 IStream_Release( pstm );
7218 /***********************************************************************
7219 * WriteFmtUserTypeStg (OLE32.@)
7221 HRESULT WINAPI WriteFmtUserTypeStg(
7222 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7225 WCHAR szwClipName[0x40];
7226 CLSID clsid = CLSID_NULL;
7227 LPWSTR wstrProgID = NULL;
7230 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7232 /* get the clipboard format name */
7233 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
7236 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7238 /* FIXME: There's room to save a CLSID and its ProgID, but
7239 the CLSID is not looked up in the registry and in all the
7240 tests I wrote it was CLSID_NULL. Where does it come from?
7243 /* get the real program ID. This may fail, but that's fine */
7244 ProgIDFromCLSID(&clsid, &wstrProgID);
7246 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7248 r = STORAGE_WriteCompObj( pstg, &clsid,
7249 lpszUserType, szwClipName, wstrProgID );
7251 CoTaskMemFree(wstrProgID);
7257 /******************************************************************************
7258 * ReadFmtUserTypeStg [OLE32.@]
7260 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7264 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7265 unsigned char unknown1[12];
7266 unsigned char unknown2[16];
7268 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7271 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7273 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7274 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7277 WARN("Failed to open stream r = %08x\n", r);
7281 /* read the various parts of the structure */
7282 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7283 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7285 r = ReadClassStm( stm, &clsid );
7289 r = STREAM_ReadString( stm, &szCLSIDName );
7293 r = STREAM_ReadString( stm, &szOleTypeName );
7297 r = STREAM_ReadString( stm, &szProgIDName );
7301 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7302 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7305 /* ok, success... now we just need to store what we found */
7307 *pcf = RegisterClipboardFormatW( szOleTypeName );
7308 CoTaskMemFree( szOleTypeName );
7310 if( lplpszUserType )
7311 *lplpszUserType = szCLSIDName;
7312 CoTaskMemFree( szProgIDName );
7315 IStream_Release( stm );
7321 /*************************************************************************
7322 * OLECONVERT_CreateCompObjStream [Internal]
7324 * Creates a "\001CompObj" is the destination IStorage if necessary.
7327 * pStorage [I] The dest IStorage to create the CompObj Stream
7329 * strOleTypeName [I] The ProgID
7333 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7336 * This function is used by OleConvertOLESTREAMToIStorage only.
7338 * The stream data is stored in the OLESTREAM and there should be
7339 * no need to recreate the stream. If the stream is manually
7340 * deleted it will attempt to create it by querying the registry.
7344 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7347 HRESULT hStorageRes, hRes = S_OK;
7348 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7349 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7350 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7352 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7353 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7355 /* Initialize the CompObj structure */
7356 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7357 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
7358 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
7361 /* Create a CompObj stream if it doesn't exist */
7362 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7363 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7364 if(hStorageRes == S_OK)
7366 /* copy the OleTypeName to the compobj struct */
7367 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7368 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7370 /* copy the OleTypeName to the compobj struct */
7371 /* Note: in the test made, these were Identical */
7372 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7373 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7376 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7377 bufferW, OLESTREAM_MAX_STR_LEN );
7378 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7384 /* Get the CLSID Default Name from the Registry */
7385 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7386 if(hErr == ERROR_SUCCESS)
7388 char strTemp[OLESTREAM_MAX_STR_LEN];
7389 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7390 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7391 if(hErr == ERROR_SUCCESS)
7393 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7399 /* Write CompObj Structure to stream */
7400 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7402 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7404 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7405 if(IStorageCompObj.dwCLSIDNameLength > 0)
7407 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7409 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7410 if(IStorageCompObj.dwOleTypeNameLength > 0)
7412 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7414 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7415 if(IStorageCompObj.dwProgIDNameLength > 0)
7417 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7419 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7420 IStream_Release(pStream);
7426 /*************************************************************************
7427 * OLECONVERT_CreateOlePresStream[Internal]
7429 * Creates the "\002OlePres000" Stream with the Metafile data
7432 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7433 * dwExtentX [I] Width of the Metafile
7434 * dwExtentY [I] Height of the Metafile
7435 * pData [I] Metafile data
7436 * dwDataLength [I] Size of the Metafile data
7440 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7443 * This function is used by OleConvertOLESTREAMToIStorage only.
7446 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7450 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7451 BYTE pOlePresStreamHeader [] =
7453 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7454 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7455 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7456 0x00, 0x00, 0x00, 0x00
7459 BYTE pOlePresStreamHeaderEmpty [] =
7461 0x00, 0x00, 0x00, 0x00,
7462 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7463 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7464 0x00, 0x00, 0x00, 0x00
7467 /* Create the OlePres000 Stream */
7468 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7469 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7474 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7476 memset(&OlePres, 0, sizeof(OlePres));
7477 /* Do we have any metafile data to save */
7478 if(dwDataLength > 0)
7480 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7481 nHeaderSize = sizeof(pOlePresStreamHeader);
7485 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7486 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7488 /* Set width and height of the metafile */
7489 OlePres.dwExtentX = dwExtentX;
7490 OlePres.dwExtentY = -dwExtentY;
7492 /* Set Data and Length */
7493 if(dwDataLength > sizeof(METAFILEPICT16))
7495 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7496 OlePres.pData = &(pData[8]);
7498 /* Save OlePres000 Data to Stream */
7499 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7500 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7501 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7502 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7503 if(OlePres.dwSize > 0)
7505 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7507 IStream_Release(pStream);
7511 /*************************************************************************
7512 * OLECONVERT_CreateOle10NativeStream [Internal]
7514 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7517 * pStorage [I] Dest storage to create the stream in
7518 * pData [I] Ole10 Native Data (ex. bmp)
7519 * dwDataLength [I] Size of the Ole10 Native Data
7525 * This function is used by OleConvertOLESTREAMToIStorage only.
7527 * Might need to verify the data and return appropriate error message
7530 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
7534 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7536 /* Create the Ole10Native Stream */
7537 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7538 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7542 /* Write info to stream */
7543 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7544 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7545 IStream_Release(pStream);
7550 /*************************************************************************
7551 * OLECONVERT_GetOLE10ProgID [Internal]
7553 * Finds the ProgID (or OleTypeID) from the IStorage
7556 * pStorage [I] The Src IStorage to get the ProgID
7557 * strProgID [I] the ProgID string to get
7558 * dwSize [I] the size of the string
7562 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7565 * This function is used by OleConvertIStorageToOLESTREAM only.
7569 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7573 LARGE_INTEGER iSeekPos;
7574 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7575 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7577 /* Open the CompObj Stream */
7578 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7579 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7583 /*Get the OleType from the CompObj Stream */
7584 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7585 iSeekPos.u.HighPart = 0;
7587 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7588 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7589 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7590 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7591 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7592 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7593 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7595 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7598 IStream_Read(pStream, strProgID, *dwSize, NULL);
7600 IStream_Release(pStream);
7605 LPOLESTR wstrProgID;
7607 /* Get the OleType from the registry */
7608 REFCLSID clsid = &(stat.clsid);
7609 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7610 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7613 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7620 /*************************************************************************
7621 * OLECONVERT_GetOle10PresData [Internal]
7623 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7626 * pStorage [I] Src IStroage
7627 * pOleStream [I] Dest OleStream Mem Struct
7633 * This function is used by OleConvertIStorageToOLESTREAM only.
7635 * Memory allocated for pData must be freed by the caller
7639 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7644 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7646 /* Initialize Default data for OLESTREAM */
7647 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7648 pOleStreamData[0].dwTypeID = 2;
7649 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7650 pOleStreamData[1].dwTypeID = 0;
7651 pOleStreamData[0].dwMetaFileWidth = 0;
7652 pOleStreamData[0].dwMetaFileHeight = 0;
7653 pOleStreamData[0].pData = NULL;
7654 pOleStreamData[1].pData = NULL;
7656 /* Open Ole10Native Stream */
7657 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7658 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7662 /* Read Size and Data */
7663 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7664 if(pOleStreamData->dwDataLength > 0)
7666 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7667 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7669 IStream_Release(pStream);
7675 /*************************************************************************
7676 * OLECONVERT_GetOle20PresData[Internal]
7678 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7681 * pStorage [I] Src IStroage
7682 * pOleStreamData [I] Dest OleStream Mem Struct
7688 * This function is used by OleConvertIStorageToOLESTREAM only.
7690 * Memory allocated for pData must be freed by the caller
7692 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7696 OLECONVERT_ISTORAGE_OLEPRES olePress;
7697 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7699 /* Initialize Default data for OLESTREAM */
7700 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7701 pOleStreamData[0].dwTypeID = 2;
7702 pOleStreamData[0].dwMetaFileWidth = 0;
7703 pOleStreamData[0].dwMetaFileHeight = 0;
7704 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7705 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7706 pOleStreamData[1].dwTypeID = 0;
7707 pOleStreamData[1].dwOleTypeNameLength = 0;
7708 pOleStreamData[1].strOleTypeName[0] = 0;
7709 pOleStreamData[1].dwMetaFileWidth = 0;
7710 pOleStreamData[1].dwMetaFileHeight = 0;
7711 pOleStreamData[1].pData = NULL;
7712 pOleStreamData[1].dwDataLength = 0;
7715 /* Open OlePress000 stream */
7716 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7717 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7720 LARGE_INTEGER iSeekPos;
7721 METAFILEPICT16 MetaFilePict;
7722 static const char strMetafilePictName[] = "METAFILEPICT";
7724 /* Set the TypeID for a Metafile */
7725 pOleStreamData[1].dwTypeID = 5;
7727 /* Set the OleTypeName to Metafile */
7728 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7729 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7731 iSeekPos.u.HighPart = 0;
7732 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7734 /* Get Presentation Data */
7735 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7736 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7737 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7738 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7740 /*Set width and Height */
7741 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7742 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7743 if(olePress.dwSize > 0)
7746 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7748 /* Set MetaFilePict struct */
7749 MetaFilePict.mm = 8;
7750 MetaFilePict.xExt = olePress.dwExtentX;
7751 MetaFilePict.yExt = olePress.dwExtentY;
7752 MetaFilePict.hMF = 0;
7754 /* Get Metafile Data */
7755 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7756 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7757 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7759 IStream_Release(pStream);
7763 /*************************************************************************
7764 * OleConvertOLESTREAMToIStorage [OLE32.@]
7769 * DVTARGETDEVICE parameter is not handled
7770 * Still unsure of some mem fields for OLE 10 Stream
7771 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7772 * and "\001OLE" streams
7775 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7776 LPOLESTREAM pOleStream,
7778 const DVTARGETDEVICE* ptd)
7782 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7784 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
7786 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7790 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7793 if(pstg == NULL || pOleStream == NULL)
7795 hRes = E_INVALIDARG;
7800 /* Load the OLESTREAM to Memory */
7801 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7806 /* Load the OLESTREAM to Memory (part 2)*/
7807 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7813 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7815 /* Do we have the IStorage Data in the OLESTREAM */
7816 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7818 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7819 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7823 /* It must be an original OLE 1.0 source */
7824 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7829 /* It must be an original OLE 1.0 source */
7830 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7833 /* Create CompObj Stream if necessary */
7834 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7837 /*Create the Ole Stream if necessary */
7838 OLECONVERT_CreateOleStream(pstg);
7843 /* Free allocated memory */
7844 for(i=0; i < 2; i++)
7846 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7847 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7848 pOleStreamData[i].pstrOleObjFileName = NULL;
7853 /*************************************************************************
7854 * OleConvertIStorageToOLESTREAM [OLE32.@]
7861 * Still unsure of some mem fields for OLE 10 Stream
7862 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7863 * and "\001OLE" streams.
7866 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7868 LPOLESTREAM pOleStream)
7871 HRESULT hRes = S_OK;
7873 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7874 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7876 TRACE("%p %p\n", pstg, pOleStream);
7878 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7880 if(pstg == NULL || pOleStream == NULL)
7882 hRes = E_INVALIDARG;
7886 /* Get the ProgID */
7887 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7888 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7892 /* Was it originally Ole10 */
7893 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7896 IStream_Release(pStream);
7897 /* Get Presentation Data for Ole10Native */
7898 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7902 /* Get Presentation Data (OLE20) */
7903 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7906 /* Save OLESTREAM */
7907 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7910 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7915 /* Free allocated memory */
7916 for(i=0; i < 2; i++)
7918 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7924 /***********************************************************************
7925 * GetConvertStg (OLE32.@)
7927 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7928 FIXME("unimplemented stub!\n");
7932 /******************************************************************************
7933 * StgIsStorageFile [OLE32.@]
7934 * Verify if the file contains a storage object
7940 * S_OK if file has magic bytes as a storage object
7941 * S_FALSE if file is not storage
7944 StgIsStorageFile(LPCOLESTR fn)
7950 TRACE("%s\n", debugstr_w(fn));
7951 hf = CreateFileW(fn, GENERIC_READ,
7952 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7953 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7955 if (hf == INVALID_HANDLE_VALUE)
7956 return STG_E_FILENOTFOUND;
7958 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7960 WARN(" unable to read file\n");
7967 if (bytes_read != 8) {
7968 WARN(" too short\n");
7972 if (!memcmp(magic,STORAGE_magic,8)) {
7977 WARN(" -> Invalid header.\n");
7981 /***********************************************************************
7982 * WriteClassStm (OLE32.@)
7984 * Writes a CLSID to a stream.
7987 * pStm [I] Stream to write to.
7988 * rclsid [I] CLSID to write.
7992 * Failure: HRESULT code.
7994 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
7996 TRACE("(%p,%p)\n",pStm,rclsid);
7998 if (!pStm || !rclsid)
7999 return E_INVALIDARG;
8001 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
8004 /***********************************************************************
8005 * ReadClassStm (OLE32.@)
8007 * Reads a CLSID from a stream.
8010 * pStm [I] Stream to read from.
8011 * rclsid [O] CLSID to read.
8015 * Failure: HRESULT code.
8017 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
8022 TRACE("(%p,%p)\n",pStm,pclsid);
8024 if (!pStm || !pclsid)
8025 return E_INVALIDARG;
8027 /* clear the output args */
8028 *pclsid = CLSID_NULL;
8030 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
8035 if (nbByte != sizeof(CLSID))
8036 return STG_E_READFAULT;