2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
33 * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/stg/stg/istorage_compound_file_implementation.asp
43 #define NONAMELESSUNION
44 #define NONAMELESSSTRUCT
50 #include "wine/unicode.h"
51 #include "wine/debug.h"
53 #include "storage32.h"
54 #include "ole2.h" /* For Write/ReadClassStm */
57 #include "wine/wingdi16.h"
59 WINE_DEFAULT_DEBUG_CHANNEL(storage);
61 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
62 #define OLESTREAM_ID 0x501
63 #define OLESTREAM_MAX_STR_LEN 255
66 * These are signatures to detect the type of Document file.
68 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
69 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
71 static const char rootPropertyName[] = "Root Entry";
73 /****************************************************************************
74 * Storage32InternalImpl definitions.
76 * Definition of the implementation structure for the IStorage32 interface.
77 * This one implements the IStorage32 interface for storage that are
78 * inside another storage.
80 struct StorageInternalImpl
82 struct StorageBaseImpl base;
84 * There is no specific data for this class.
87 typedef struct StorageInternalImpl StorageInternalImpl;
89 /* Method definitions for the Storage32InternalImpl class. */
90 static StorageInternalImpl* StorageInternalImpl_Construct(StorageImpl* ancestorStorage,
91 DWORD openFlags, ULONG rootTropertyIndex);
92 static void StorageImpl_Destroy(StorageBaseImpl* iface);
93 static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
94 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
95 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
96 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
97 static void StorageImpl_SaveFileHeader(StorageImpl* This);
99 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
100 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
101 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
102 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
103 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
105 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
106 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
107 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
109 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
110 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
111 ULONG blockIndex, ULONG offset, DWORD value);
112 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This,
113 ULONG blockIndex, ULONG offset, DWORD* value);
115 /* OLESTREAM memory structure to use for Get and Put Routines */
116 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
121 DWORD dwOleTypeNameLength;
122 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
123 CHAR *pstrOleObjFileName;
124 DWORD dwOleObjFileNameLength;
125 DWORD dwMetaFileWidth;
126 DWORD dwMetaFileHeight;
127 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
130 }OLECONVERT_OLESTREAM_DATA;
132 /* CompObj Stream structure */
133 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
138 DWORD dwCLSIDNameLength;
139 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
140 DWORD dwOleTypeNameLength;
141 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
142 DWORD dwProgIDNameLength;
143 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
145 }OLECONVERT_ISTORAGE_COMPOBJ;
148 /* Ole Presention Stream structure */
149 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
157 }OLECONVERT_ISTORAGE_OLEPRES;
161 /***********************************************************************
162 * Forward declaration of internal functions used by the method DestroyElement
164 static HRESULT deleteStorageProperty(
165 StorageImpl *parentStorage,
166 ULONG foundPropertyIndexToDelete,
167 StgProperty propertyToDelete);
169 static HRESULT deleteStreamProperty(
170 StorageImpl *parentStorage,
171 ULONG foundPropertyIndexToDelete,
172 StgProperty propertyToDelete);
174 static HRESULT findPlaceholder(
175 StorageImpl *storage,
176 ULONG propertyIndexToStore,
177 ULONG storagePropertyIndex,
180 static HRESULT adjustPropertyChain(
182 StgProperty propertyToDelete,
183 StgProperty parentProperty,
184 ULONG parentPropertyId,
187 /***********************************************************************
188 * Declaration of the functions used to manipulate StgProperty
191 static ULONG getFreeProperty(
192 StorageImpl *storage);
194 static void updatePropertyChain(
195 StorageImpl *storage,
196 ULONG newPropertyIndex,
197 StgProperty newProperty);
199 static LONG propertyNameCmp(
200 const OLECHAR *newProperty,
201 const OLECHAR *currentProperty);
204 /***********************************************************************
205 * Declaration of miscellaneous functions...
207 static HRESULT validateSTGM(DWORD stgmValue);
209 static DWORD GetShareModeFromSTGM(DWORD stgm);
210 static DWORD GetAccessModeFromSTGM(DWORD stgm);
211 static DWORD GetCreationModeFromSTGM(DWORD stgm);
213 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
216 /****************************************************************************
217 * IEnumSTATSTGImpl definitions.
219 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
220 * This class allows iterating through the content of a storage and to find
221 * specific items inside it.
223 struct IEnumSTATSTGImpl
225 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
226 * since we want to cast this in an IEnumSTATSTG pointer */
228 LONG ref; /* Reference count */
229 StorageImpl* parentStorage; /* Reference to the parent storage */
230 ULONG firstPropertyNode; /* Index of the root of the storage to enumerate */
233 * The current implementation of the IEnumSTATSTGImpl class uses a stack
234 * to walk the property sets to get the content of a storage. This stack
235 * is implemented by the following 3 data members
241 #define ENUMSTATSGT_SIZE_INCREMENT 10
245 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageImpl* This, ULONG firstPropertyNode);
246 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
247 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl* This, ULONG nodeToPush);
248 static ULONG IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl* This, BOOL remove);
249 static ULONG IEnumSTATSTGImpl_FindProperty(IEnumSTATSTGImpl* This, const OLECHAR* lpszPropName,
250 StgProperty* buffer);
251 static INT IEnumSTATSTGImpl_FindParentProperty(IEnumSTATSTGImpl *This, ULONG childProperty,
252 StgProperty *currentProperty, ULONG *propertyId);
254 /************************************************************************
258 static ULONG BLOCK_GetBigBlockOffset(ULONG index)
260 if (index == 0xffffffff)
265 return index * BIG_BLOCK_SIZE;
268 /************************************************************************
269 ** Storage32BaseImpl implementatiion
271 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
272 ULARGE_INTEGER offset,
277 return BIGBLOCKFILE_ReadAt(This->bigBlockFile,offset,buffer,size,bytesRead);
280 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
281 ULARGE_INTEGER offset,
286 return BIGBLOCKFILE_WriteAt(This->bigBlockFile,offset,buffer,size,bytesWritten);
289 /************************************************************************
290 * Storage32BaseImpl_QueryInterface (IUnknown)
292 * This method implements the common QueryInterface for all IStorage32
293 * implementations contained in this file.
295 * See Windows documentation for more details on IUnknown methods.
297 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
302 StorageBaseImpl *This = (StorageBaseImpl *)iface;
304 * Perform a sanity check on the parameters.
306 if ( (This==0) || (ppvObject==0) )
310 * Initialize the return parameter.
315 * Compare the riid with the interface IDs implemented by this object.
317 if (IsEqualGUID(&IID_IUnknown, riid) ||
318 IsEqualGUID(&IID_IStorage, riid))
320 *ppvObject = (IStorage*)This;
322 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
324 *ppvObject = (IStorage*)&This->pssVtbl;
328 * Check that we obtained an interface.
331 return E_NOINTERFACE;
334 * Query Interface always increases the reference count by one when it is
337 IStorage_AddRef(iface);
342 /************************************************************************
343 * Storage32BaseImpl_AddRef (IUnknown)
345 * This method implements the common AddRef for all IStorage32
346 * implementations contained in this file.
348 * See Windows documentation for more details on IUnknown methods.
350 static ULONG WINAPI StorageBaseImpl_AddRef(
353 StorageBaseImpl *This = (StorageBaseImpl *)iface;
354 ULONG ref = InterlockedIncrement(&This->ref);
356 TRACE("(%p) AddRef to %d\n", This, ref);
361 /************************************************************************
362 * Storage32BaseImpl_Release (IUnknown)
364 * This method implements the common Release for all IStorage32
365 * implementations contained in this file.
367 * See Windows documentation for more details on IUnknown methods.
369 static ULONG WINAPI StorageBaseImpl_Release(
372 StorageBaseImpl *This = (StorageBaseImpl *)iface;
374 * Decrease the reference count on this object.
376 ULONG ref = InterlockedDecrement(&This->ref);
378 TRACE("(%p) ReleaseRef to %d\n", This, ref);
381 * If the reference count goes down to 0, perform suicide.
386 * Since we are using a system of base-classes, we want to call the
387 * destructor of the appropriate derived class. To do this, we are
388 * using virtual functions to implement the destructor.
390 This->v_destructor(This);
396 /************************************************************************
397 * Storage32BaseImpl_OpenStream (IStorage)
399 * This method will open the specified stream object from the current storage.
401 * See Windows documentation for more details on IStorage methods.
403 static HRESULT WINAPI StorageBaseImpl_OpenStream(
405 const OLECHAR* pwcsName, /* [string][in] */
406 void* reserved1, /* [unique][in] */
407 DWORD grfMode, /* [in] */
408 DWORD reserved2, /* [in] */
409 IStream** ppstm) /* [out] */
411 StorageBaseImpl *This = (StorageBaseImpl *)iface;
412 IEnumSTATSTGImpl* propertyEnumeration;
413 StgStreamImpl* newStream;
414 StgProperty currentProperty;
415 ULONG foundPropertyIndex;
416 HRESULT res = STG_E_UNKNOWN;
418 TRACE("(%p, %s, %p, %x, %d, %p)\n",
419 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
422 * Perform a sanity check on the parameters.
424 if ( (pwcsName==NULL) || (ppstm==0) )
431 * Initialize the out parameter
436 * Validate the STGM flags
438 if ( FAILED( validateSTGM(grfMode) ) ||
439 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
441 res = STG_E_INVALIDFLAG;
448 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
450 res = STG_E_INVALIDFUNCTION;
455 * Check that we're compatible with the parent's storage mode, but
456 * only if we are not in transacted mode
458 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
459 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
461 res = STG_E_ACCESSDENIED;
467 * Create a property enumeration to search the properties
469 propertyEnumeration = IEnumSTATSTGImpl_Construct(
470 This->ancestorStorage,
471 This->rootPropertySetIndex);
474 * Search the enumeration for the property with the given name
476 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
482 * Delete the property enumeration since we don't need it anymore
484 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
487 * If it was found, construct the stream object and return a pointer to it.
489 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
490 (currentProperty.propertyType==PROPTYPE_STREAM) )
492 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
496 newStream->grfMode = grfMode;
497 *ppstm = (IStream*)newStream;
500 * Since we are returning a pointer to the interface, we have to
501 * nail down the reference.
503 IStream_AddRef(*ppstm);
513 res = STG_E_FILENOTFOUND;
517 TRACE("<-- IStream %p\n", *ppstm);
518 TRACE("<-- %08x\n", res);
522 /************************************************************************
523 * Storage32BaseImpl_OpenStorage (IStorage)
525 * This method will open a new storage object from the current storage.
527 * See Windows documentation for more details on IStorage methods.
529 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
531 const OLECHAR* pwcsName, /* [string][unique][in] */
532 IStorage* pstgPriority, /* [unique][in] */
533 DWORD grfMode, /* [in] */
534 SNB snbExclude, /* [unique][in] */
535 DWORD reserved, /* [in] */
536 IStorage** ppstg) /* [out] */
538 StorageBaseImpl *This = (StorageBaseImpl *)iface;
539 StorageInternalImpl* newStorage;
540 IEnumSTATSTGImpl* propertyEnumeration;
541 StgProperty currentProperty;
542 ULONG foundPropertyIndex;
543 HRESULT res = STG_E_UNKNOWN;
545 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
546 iface, debugstr_w(pwcsName), pstgPriority,
547 grfMode, snbExclude, reserved, ppstg);
550 * Perform a sanity check on the parameters.
552 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
559 if (snbExclude != NULL)
561 res = STG_E_INVALIDPARAMETER;
566 * Validate the STGM flags
568 if ( FAILED( validateSTGM(grfMode) ))
570 res = STG_E_INVALIDFLAG;
577 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
578 (grfMode & STGM_DELETEONRELEASE) ||
579 (grfMode & STGM_PRIORITY) )
581 res = STG_E_INVALIDFUNCTION;
586 * Check that we're compatible with the parent's storage mode,
587 * but only if we are not transacted
589 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
590 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
592 res = STG_E_ACCESSDENIED;
598 * Initialize the out parameter
603 * Create a property enumeration to search the properties
605 propertyEnumeration = IEnumSTATSTGImpl_Construct(
606 This->ancestorStorage,
607 This->rootPropertySetIndex);
610 * Search the enumeration for the property with the given name
612 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
618 * Delete the property enumeration since we don't need it anymore
620 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
623 * If it was found, construct the stream object and return a pointer to it.
625 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
626 (currentProperty.propertyType==PROPTYPE_STORAGE) )
629 * Construct a new Storage object
631 newStorage = StorageInternalImpl_Construct(
632 This->ancestorStorage,
638 *ppstg = (IStorage*)newStorage;
641 * Since we are returning a pointer to the interface,
642 * we have to nail down the reference.
644 StorageBaseImpl_AddRef(*ppstg);
650 res = STG_E_INSUFFICIENTMEMORY;
654 res = STG_E_FILENOTFOUND;
657 TRACE("<-- %08x\n", res);
661 /************************************************************************
662 * Storage32BaseImpl_EnumElements (IStorage)
664 * This method will create an enumerator object that can be used to
665 * retrieve informatino about all the properties in the storage object.
667 * See Windows documentation for more details on IStorage methods.
669 static HRESULT WINAPI StorageBaseImpl_EnumElements(
671 DWORD reserved1, /* [in] */
672 void* reserved2, /* [size_is][unique][in] */
673 DWORD reserved3, /* [in] */
674 IEnumSTATSTG** ppenum) /* [out] */
676 StorageBaseImpl *This = (StorageBaseImpl *)iface;
677 IEnumSTATSTGImpl* newEnum;
679 TRACE("(%p, %d, %p, %d, %p)\n",
680 iface, reserved1, reserved2, reserved3, ppenum);
683 * Perform a sanity check on the parameters.
685 if ( (This==0) || (ppenum==0))
689 * Construct the enumerator.
691 newEnum = IEnumSTATSTGImpl_Construct(
692 This->ancestorStorage,
693 This->rootPropertySetIndex);
697 *ppenum = (IEnumSTATSTG*)newEnum;
700 * Don't forget to nail down a reference to the new object before
703 IEnumSTATSTG_AddRef(*ppenum);
708 return E_OUTOFMEMORY;
711 /************************************************************************
712 * Storage32BaseImpl_Stat (IStorage)
714 * This method will retrieve information about this storage object.
716 * See Windows documentation for more details on IStorage methods.
718 static HRESULT WINAPI StorageBaseImpl_Stat(
720 STATSTG* pstatstg, /* [out] */
721 DWORD grfStatFlag) /* [in] */
723 StorageBaseImpl *This = (StorageBaseImpl *)iface;
724 StgProperty curProperty;
726 HRESULT res = STG_E_UNKNOWN;
728 TRACE("(%p, %p, %x)\n",
729 iface, pstatstg, grfStatFlag);
732 * Perform a sanity check on the parameters.
734 if ( (This==0) || (pstatstg==0))
741 * Read the information from the property.
743 readSuccessful = StorageImpl_ReadProperty(
744 This->ancestorStorage,
745 This->rootPropertySetIndex,
750 StorageUtl_CopyPropertyToSTATSTG(
755 pstatstg->grfMode = This->openFlags;
766 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);
768 TRACE("<-- %08x\n", res);
772 /************************************************************************
773 * Storage32BaseImpl_RenameElement (IStorage)
775 * This method will rename the specified element.
777 * See Windows documentation for more details on IStorage methods.
779 * Implementation notes: The method used to rename consists of creating a clone
780 * of the deleted StgProperty object setting it with the new name and to
781 * perform a DestroyElement of the old StgProperty.
783 static HRESULT WINAPI StorageBaseImpl_RenameElement(
785 const OLECHAR* pwcsOldName, /* [in] */
786 const OLECHAR* pwcsNewName) /* [in] */
788 StorageBaseImpl *This = (StorageBaseImpl *)iface;
789 IEnumSTATSTGImpl* propertyEnumeration;
790 StgProperty currentProperty;
791 ULONG foundPropertyIndex;
793 TRACE("(%p, %s, %s)\n",
794 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
797 * Create a property enumeration to search the properties
799 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
800 This->rootPropertySetIndex);
803 * Search the enumeration for the new property name
805 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
809 if (foundPropertyIndex != PROPERTY_NULL)
812 * There is already a property with the new name
814 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
815 return STG_E_FILEALREADYEXISTS;
818 IEnumSTATSTG_Reset((IEnumSTATSTG*)propertyEnumeration);
821 * Search the enumeration for the old property name
823 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
828 * Delete the property enumeration since we don't need it anymore
830 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
832 if (foundPropertyIndex != PROPERTY_NULL)
834 StgProperty renamedProperty;
835 ULONG renamedPropertyIndex;
838 * Setup a new property for the renamed property
840 renamedProperty.sizeOfNameString =
841 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
843 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
844 return STG_E_INVALIDNAME;
846 strcpyW(renamedProperty.name, pwcsNewName);
848 renamedProperty.propertyType = currentProperty.propertyType;
849 renamedProperty.startingBlock = currentProperty.startingBlock;
850 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
851 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
853 renamedProperty.previousProperty = PROPERTY_NULL;
854 renamedProperty.nextProperty = PROPERTY_NULL;
857 * Bring the dirProperty link in case it is a storage and in which
858 * case the renamed storage elements don't require to be reorganized.
860 renamedProperty.dirProperty = currentProperty.dirProperty;
862 /* call CoFileTime to get the current time
863 renamedProperty.timeStampS1
864 renamedProperty.timeStampD1
865 renamedProperty.timeStampS2
866 renamedProperty.timeStampD2
867 renamedProperty.propertyUniqueID
871 * Obtain a free property in the property chain
873 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
876 * Save the new property into the new property spot
878 StorageImpl_WriteProperty(
879 This->ancestorStorage,
880 renamedPropertyIndex,
884 * Find a spot in the property chain for our newly created property.
888 renamedPropertyIndex,
892 * At this point the renamed property has been inserted in the tree,
893 * now, before Destroying the old property we must zero its dirProperty
894 * otherwise the DestroyProperty below will zap it all and we do not want
896 * Also, we fake that the old property is a storage so the DestroyProperty
897 * will not do a SetSize(0) on the stream data.
899 * This means that we need to tweak the StgProperty if it is a stream or a
902 StorageImpl_ReadProperty(This->ancestorStorage,
906 currentProperty.dirProperty = PROPERTY_NULL;
907 currentProperty.propertyType = PROPTYPE_STORAGE;
908 StorageImpl_WriteProperty(
909 This->ancestorStorage,
914 * Invoke Destroy to get rid of the ole property and automatically redo
915 * the linking of its previous and next members...
917 IStorage_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
923 * There is no property with the old name
925 return STG_E_FILENOTFOUND;
931 /************************************************************************
932 * Storage32BaseImpl_CreateStream (IStorage)
934 * This method will create a stream object within this storage
936 * See Windows documentation for more details on IStorage methods.
938 static HRESULT WINAPI StorageBaseImpl_CreateStream(
940 const OLECHAR* pwcsName, /* [string][in] */
941 DWORD grfMode, /* [in] */
942 DWORD reserved1, /* [in] */
943 DWORD reserved2, /* [in] */
944 IStream** ppstm) /* [out] */
946 StorageBaseImpl *This = (StorageBaseImpl *)iface;
947 IEnumSTATSTGImpl* propertyEnumeration;
948 StgStreamImpl* newStream;
949 StgProperty currentProperty, newStreamProperty;
950 ULONG foundPropertyIndex, newPropertyIndex;
952 TRACE("(%p, %s, %x, %d, %d, %p)\n",
953 iface, debugstr_w(pwcsName), grfMode,
954 reserved1, reserved2, ppstm);
957 * Validate parameters
960 return STG_E_INVALIDPOINTER;
963 return STG_E_INVALIDNAME;
965 if (reserved1 || reserved2)
966 return STG_E_INVALIDPARAMETER;
969 * Validate the STGM flags
971 if ( FAILED( validateSTGM(grfMode) ))
972 return STG_E_INVALIDFLAG;
974 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
975 return STG_E_INVALIDFLAG;
980 if ((grfMode & STGM_DELETEONRELEASE) ||
981 (grfMode & STGM_TRANSACTED))
982 return STG_E_INVALIDFUNCTION;
985 * Check that we're compatible with the parent's storage mode
986 * if not in transacted mode
988 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
989 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
990 return STG_E_ACCESSDENIED;
994 * Initialize the out parameter
999 * Create a property enumeration to search the properties
1001 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
1002 This->rootPropertySetIndex);
1004 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1008 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1010 if (foundPropertyIndex != PROPERTY_NULL)
1013 * An element with this name already exists
1015 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1017 IStorage_DestroyElement(iface, pwcsName);
1020 return STG_E_FILEALREADYEXISTS;
1022 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1024 WARN("read-only storage\n");
1025 return STG_E_ACCESSDENIED;
1029 * memset the empty property
1031 memset(&newStreamProperty, 0, sizeof(StgProperty));
1033 newStreamProperty.sizeOfNameString =
1034 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
1036 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1037 return STG_E_INVALIDNAME;
1039 strcpyW(newStreamProperty.name, pwcsName);
1041 newStreamProperty.propertyType = PROPTYPE_STREAM;
1042 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
1043 newStreamProperty.size.u.LowPart = 0;
1044 newStreamProperty.size.u.HighPart = 0;
1046 newStreamProperty.previousProperty = PROPERTY_NULL;
1047 newStreamProperty.nextProperty = PROPERTY_NULL;
1048 newStreamProperty.dirProperty = PROPERTY_NULL;
1050 /* call CoFileTime to get the current time
1051 newStreamProperty.timeStampS1
1052 newStreamProperty.timeStampD1
1053 newStreamProperty.timeStampS2
1054 newStreamProperty.timeStampD2
1057 /* newStreamProperty.propertyUniqueID */
1060 * Get a free property or create a new one
1062 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1065 * Save the new property into the new property spot
1067 StorageImpl_WriteProperty(
1068 This->ancestorStorage,
1070 &newStreamProperty);
1073 * Find a spot in the property chain for our newly created property.
1075 updatePropertyChain(
1081 * Open the stream to return it.
1083 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
1087 *ppstm = (IStream*)newStream;
1090 * Since we are returning a pointer to the interface, we have to nail down
1093 IStream_AddRef(*ppstm);
1097 return STG_E_INSUFFICIENTMEMORY;
1103 /************************************************************************
1104 * Storage32BaseImpl_SetClass (IStorage)
1106 * This method will write the specified CLSID in the property of this
1109 * See Windows documentation for more details on IStorage methods.
1111 static HRESULT WINAPI StorageBaseImpl_SetClass(
1113 REFCLSID clsid) /* [in] */
1115 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1116 HRESULT hRes = E_FAIL;
1117 StgProperty curProperty;
1120 TRACE("(%p, %p)\n", iface, clsid);
1122 success = StorageImpl_ReadProperty(This->ancestorStorage,
1123 This->rootPropertySetIndex,
1127 curProperty.propertyUniqueID = *clsid;
1129 success = StorageImpl_WriteProperty(This->ancestorStorage,
1130 This->rootPropertySetIndex,
1139 /************************************************************************
1140 ** Storage32Impl implementation
1143 /************************************************************************
1144 * Storage32Impl_CreateStorage (IStorage)
1146 * This method will create the storage object within the provided storage.
1148 * See Windows documentation for more details on IStorage methods.
1150 static HRESULT WINAPI StorageImpl_CreateStorage(
1152 const OLECHAR *pwcsName, /* [string][in] */
1153 DWORD grfMode, /* [in] */
1154 DWORD reserved1, /* [in] */
1155 DWORD reserved2, /* [in] */
1156 IStorage **ppstg) /* [out] */
1158 StorageImpl* const This=(StorageImpl*)iface;
1160 IEnumSTATSTGImpl *propertyEnumeration;
1161 StgProperty currentProperty;
1162 StgProperty newProperty;
1163 ULONG foundPropertyIndex;
1164 ULONG newPropertyIndex;
1167 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1168 iface, debugstr_w(pwcsName), grfMode,
1169 reserved1, reserved2, ppstg);
1172 * Validate parameters
1175 return STG_E_INVALIDPOINTER;
1178 return STG_E_INVALIDNAME;
1181 * Initialize the out parameter
1186 * Validate the STGM flags
1188 if ( FAILED( validateSTGM(grfMode) ) ||
1189 (grfMode & STGM_DELETEONRELEASE) )
1191 WARN("bad grfMode: 0x%x\n", grfMode);
1192 return STG_E_INVALIDFLAG;
1196 * Check that we're compatible with the parent's storage mode
1198 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->base.openFlags ) )
1200 WARN("access denied\n");
1201 return STG_E_ACCESSDENIED;
1205 * Create a property enumeration and search the properties
1207 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1208 This->base.rootPropertySetIndex);
1210 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1213 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1215 if (foundPropertyIndex != PROPERTY_NULL)
1218 * An element with this name already exists
1220 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1221 IStorage_DestroyElement(iface, pwcsName);
1224 WARN("file already exists\n");
1225 return STG_E_FILEALREADYEXISTS;
1228 else if (STGM_ACCESS_MODE(This->base.openFlags) == STGM_READ)
1230 WARN("read-only storage\n");
1231 return STG_E_ACCESSDENIED;
1235 * memset the empty property
1237 memset(&newProperty, 0, sizeof(StgProperty));
1239 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1241 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1243 FIXME("name too long\n");
1244 return STG_E_INVALIDNAME;
1247 strcpyW(newProperty.name, pwcsName);
1249 newProperty.propertyType = PROPTYPE_STORAGE;
1250 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1251 newProperty.size.u.LowPart = 0;
1252 newProperty.size.u.HighPart = 0;
1254 newProperty.previousProperty = PROPERTY_NULL;
1255 newProperty.nextProperty = PROPERTY_NULL;
1256 newProperty.dirProperty = PROPERTY_NULL;
1258 /* call CoFileTime to get the current time
1259 newProperty.timeStampS1
1260 newProperty.timeStampD1
1261 newProperty.timeStampS2
1262 newProperty.timeStampD2
1265 /* newStorageProperty.propertyUniqueID */
1268 * Obtain a free property in the property chain
1270 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1273 * Save the new property into the new property spot
1275 StorageImpl_WriteProperty(
1276 This->base.ancestorStorage,
1281 * Find a spot in the property chain for our newly created property.
1283 updatePropertyChain(
1289 * Open it to get a pointer to return.
1291 hr = IStorage_OpenStorage(
1293 (const OLECHAR*)pwcsName,
1300 if( (hr != S_OK) || (*ppstg == NULL))
1310 /***************************************************************************
1314 * Get a free property or create a new one.
1316 static ULONG getFreeProperty(
1317 StorageImpl *storage)
1319 ULONG currentPropertyIndex = 0;
1320 ULONG newPropertyIndex = PROPERTY_NULL;
1321 BOOL readSuccessful = TRUE;
1322 StgProperty currentProperty;
1327 * Start by reading the root property
1329 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1330 currentPropertyIndex,
1334 if (currentProperty.sizeOfNameString == 0)
1337 * The property existis and is available, we found it.
1339 newPropertyIndex = currentPropertyIndex;
1345 * We exhausted the property list, we will create more space below
1347 newPropertyIndex = currentPropertyIndex;
1349 currentPropertyIndex++;
1351 } while (newPropertyIndex == PROPERTY_NULL);
1354 * grow the property chain
1356 if (! readSuccessful)
1358 StgProperty emptyProperty;
1359 ULARGE_INTEGER newSize;
1360 ULONG propertyIndex;
1361 ULONG lastProperty = 0;
1362 ULONG blockCount = 0;
1365 * obtain the new count of property blocks
1367 blockCount = BlockChainStream_GetCount(
1368 storage->base.ancestorStorage->rootBlockChain)+1;
1371 * initialize the size used by the property stream
1373 newSize.u.HighPart = 0;
1374 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1377 * add a property block to the property chain
1379 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1382 * memset the empty property in order to initialize the unused newly
1385 memset(&emptyProperty, 0, sizeof(StgProperty));
1390 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1393 propertyIndex = newPropertyIndex;
1394 propertyIndex < lastProperty;
1397 StorageImpl_WriteProperty(
1398 storage->base.ancestorStorage,
1404 return newPropertyIndex;
1407 /****************************************************************************
1411 * Case insensitive comparaison of StgProperty.name by first considering
1414 * Returns <0 when newPrpoerty < currentProperty
1415 * >0 when newPrpoerty > currentProperty
1416 * 0 when newPrpoerty == currentProperty
1418 static LONG propertyNameCmp(
1419 const OLECHAR *newProperty,
1420 const OLECHAR *currentProperty)
1422 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1427 * We compare the string themselves only when they are of the same length
1429 diff = lstrcmpiW( newProperty, currentProperty);
1435 /****************************************************************************
1439 * Properly link this new element in the property chain.
1441 static void updatePropertyChain(
1442 StorageImpl *storage,
1443 ULONG newPropertyIndex,
1444 StgProperty newProperty)
1446 StgProperty currentProperty;
1449 * Read the root property
1451 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1452 storage->base.rootPropertySetIndex,
1455 if (currentProperty.dirProperty != PROPERTY_NULL)
1458 * The root storage contains some element, therefore, start the research
1459 * for the appropriate location.
1462 ULONG current, next, previous, currentPropertyId;
1465 * Keep the StgProperty sequence number of the storage first property
1467 currentPropertyId = currentProperty.dirProperty;
1472 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1473 currentProperty.dirProperty,
1476 previous = currentProperty.previousProperty;
1477 next = currentProperty.nextProperty;
1478 current = currentPropertyId;
1482 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1486 if (previous != PROPERTY_NULL)
1488 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1495 currentProperty.previousProperty = newPropertyIndex;
1496 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1504 if (next != PROPERTY_NULL)
1506 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1513 currentProperty.nextProperty = newPropertyIndex;
1514 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1523 * Trying to insert an item with the same name in the
1524 * subtree structure.
1529 previous = currentProperty.previousProperty;
1530 next = currentProperty.nextProperty;
1536 * The root storage is empty, link the new property to its dir property
1538 currentProperty.dirProperty = newPropertyIndex;
1539 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1540 storage->base.rootPropertySetIndex,
1546 /*************************************************************************
1549 static HRESULT WINAPI StorageImpl_CopyTo(
1551 DWORD ciidExclude, /* [in] */
1552 const IID* rgiidExclude, /* [size_is][unique][in] */
1553 SNB snbExclude, /* [unique][in] */
1554 IStorage* pstgDest) /* [unique][in] */
1556 IEnumSTATSTG *elements = 0;
1557 STATSTG curElement, strStat;
1559 IStorage *pstgTmp, *pstgChild;
1560 IStream *pstrTmp, *pstrChild;
1562 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1563 FIXME("Exclude option not implemented\n");
1565 TRACE("(%p, %d, %p, %p, %p)\n",
1566 iface, ciidExclude, rgiidExclude,
1567 snbExclude, pstgDest);
1570 * Perform a sanity check
1572 if ( pstgDest == 0 )
1573 return STG_E_INVALIDPOINTER;
1576 * Enumerate the elements
1578 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1586 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1587 IStorage_SetClass( pstgDest, &curElement.clsid );
1592 * Obtain the next element
1594 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1596 if ( hr == S_FALSE )
1598 hr = S_OK; /* done, every element has been copied */
1602 if (curElement.type == STGTY_STORAGE)
1605 * open child source storage
1607 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1608 STGM_READ|STGM_SHARE_EXCLUSIVE,
1609 NULL, 0, &pstgChild );
1615 * Check if destination storage is not a child of the source
1616 * storage, which will cause an infinite loop
1618 if (pstgChild == pstgDest)
1620 IEnumSTATSTG_Release(elements);
1622 return STG_E_ACCESSDENIED;
1626 * create a new storage in destination storage
1628 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1629 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1633 * if it already exist, don't create a new one use this one
1635 if (hr == STG_E_FILEALREADYEXISTS)
1637 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1638 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1639 NULL, 0, &pstgTmp );
1647 * do the copy recursively
1649 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1650 snbExclude, pstgTmp );
1652 IStorage_Release( pstgTmp );
1653 IStorage_Release( pstgChild );
1655 else if (curElement.type == STGTY_STREAM)
1658 * create a new stream in destination storage. If the stream already
1659 * exist, it will be deleted and a new one will be created.
1661 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1662 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1669 * open child stream storage
1671 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1672 STGM_READ|STGM_SHARE_EXCLUSIVE,
1679 * Get the size of the source stream
1681 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1684 * Set the size of the destination stream.
1686 IStream_SetSize(pstrTmp, strStat.cbSize);
1691 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1694 IStream_Release( pstrTmp );
1695 IStream_Release( pstrChild );
1699 WARN("unknown element type: %d\n", curElement.type);
1702 } while (hr == S_OK);
1707 IEnumSTATSTG_Release(elements);
1712 /*************************************************************************
1713 * MoveElementTo (IStorage)
1715 static HRESULT WINAPI StorageImpl_MoveElementTo(
1717 const OLECHAR *pwcsName, /* [string][in] */
1718 IStorage *pstgDest, /* [unique][in] */
1719 const OLECHAR *pwcsNewName,/* [string][in] */
1720 DWORD grfFlags) /* [in] */
1722 FIXME("(%p %s %p %s %u): stub\n", iface,
1723 debugstr_w(pwcsName), pstgDest,
1724 debugstr_w(pwcsNewName), grfFlags);
1728 /*************************************************************************
1731 * Ensures that any changes made to a storage object open in transacted mode
1732 * are reflected in the parent storage
1735 * Wine doesn't implement transacted mode, which seems to be a basic
1736 * optimization, so we can ignore this stub for now.
1738 static HRESULT WINAPI StorageImpl_Commit(
1740 DWORD grfCommitFlags)/* [in] */
1742 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1746 /*************************************************************************
1749 * Discard all changes that have been made since the last commit operation
1751 static HRESULT WINAPI StorageImpl_Revert(
1754 FIXME("(%p): stub\n", iface);
1758 /*************************************************************************
1759 * DestroyElement (IStorage)
1761 * Strategy: This implementation is built this way for simplicity not for speed.
1762 * I always delete the topmost element of the enumeration and adjust
1763 * the deleted element pointer all the time. This takes longer to
1764 * do but allow to reinvoke DestroyElement whenever we encounter a
1765 * storage object. The optimisation resides in the usage of another
1766 * enumeration strategy that would give all the leaves of a storage
1767 * first. (postfix order)
1769 static HRESULT WINAPI StorageImpl_DestroyElement(
1771 const OLECHAR *pwcsName)/* [string][in] */
1773 StorageImpl* const This=(StorageImpl*)iface;
1775 IEnumSTATSTGImpl* propertyEnumeration;
1778 StgProperty propertyToDelete;
1779 StgProperty parentProperty;
1780 ULONG foundPropertyIndexToDelete;
1781 ULONG typeOfRelation;
1782 ULONG parentPropertyId = 0;
1785 iface, debugstr_w(pwcsName));
1788 * Perform a sanity check on the parameters.
1791 return STG_E_INVALIDPOINTER;
1794 * Create a property enumeration to search the property with the given name
1796 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1797 This->base.ancestorStorage,
1798 This->base.rootPropertySetIndex);
1800 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1801 propertyEnumeration,
1805 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1807 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1809 return STG_E_FILENOTFOUND;
1813 * Find the parent property of the property to delete (the one that
1814 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1815 * the parent is This. Otherwise, the parent is one of its sibling...
1819 * First, read This's StgProperty..
1821 res = StorageImpl_ReadProperty(
1822 This->base.ancestorStorage,
1823 This->base.rootPropertySetIndex,
1829 * Second, check to see if by any chance the actual storage (This) is not
1830 * the parent of the property to delete... We never know...
1832 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1835 * Set data as it would have been done in the else part...
1837 typeOfRelation = PROPERTY_RELATION_DIR;
1838 parentPropertyId = This->base.rootPropertySetIndex;
1843 * Create a property enumeration to search the parent properties, and
1844 * delete it once done.
1846 IEnumSTATSTGImpl* propertyEnumeration2;
1848 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1849 This->base.ancestorStorage,
1850 This->base.rootPropertySetIndex);
1852 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1853 propertyEnumeration2,
1854 foundPropertyIndexToDelete,
1858 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1861 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1863 hr = deleteStorageProperty(
1865 foundPropertyIndexToDelete,
1868 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1870 hr = deleteStreamProperty(
1872 foundPropertyIndexToDelete,
1880 * Adjust the property chain
1882 hr = adjustPropertyChain(
1893 /************************************************************************
1894 * StorageImpl_Stat (IStorage)
1896 * This method will retrieve information about this storage object.
1898 * See Windows documentation for more details on IStorage methods.
1900 static HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1901 STATSTG* pstatstg, /* [out] */
1902 DWORD grfStatFlag) /* [in] */
1904 StorageImpl* const This = (StorageImpl*)iface;
1905 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1907 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1909 CoTaskMemFree(pstatstg->pwcsName);
1910 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1911 strcpyW(pstatstg->pwcsName, This->pwcsName);
1917 /******************************************************************************
1918 * Internal stream list handlers
1921 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1923 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1924 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1927 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1929 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1930 list_remove(&(strm->StrmListEntry));
1933 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1935 struct list *cur, *cur2;
1936 StgStreamImpl *strm=NULL;
1938 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1939 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1940 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1941 strm->parentStorage = NULL;
1947 /*********************************************************************
1951 * Perform the deletion of a complete storage node
1954 static HRESULT deleteStorageProperty(
1955 StorageImpl *parentStorage,
1956 ULONG indexOfPropertyToDelete,
1957 StgProperty propertyToDelete)
1959 IEnumSTATSTG *elements = 0;
1960 IStorage *childStorage = 0;
1961 STATSTG currentElement;
1963 HRESULT destroyHr = S_OK;
1966 * Open the storage and enumerate it
1968 hr = StorageBaseImpl_OpenStorage(
1969 (IStorage*)parentStorage,
1970 propertyToDelete.name,
1972 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1983 * Enumerate the elements
1985 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1990 * Obtain the next element
1992 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
1995 destroyHr = StorageImpl_DestroyElement(
1996 (IStorage*)childStorage,
1997 (OLECHAR*)currentElement.pwcsName);
1999 CoTaskMemFree(currentElement.pwcsName);
2003 * We need to Reset the enumeration every time because we delete elements
2004 * and the enumeration could be invalid
2006 IEnumSTATSTG_Reset(elements);
2008 } while ((hr == S_OK) && (destroyHr == S_OK));
2011 * Invalidate the property by zeroing its name member.
2013 propertyToDelete.sizeOfNameString = 0;
2015 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
2016 indexOfPropertyToDelete,
2019 IStorage_Release(childStorage);
2020 IEnumSTATSTG_Release(elements);
2025 /*********************************************************************
2029 * Perform the deletion of a stream node
2032 static HRESULT deleteStreamProperty(
2033 StorageImpl *parentStorage,
2034 ULONG indexOfPropertyToDelete,
2035 StgProperty propertyToDelete)
2039 ULARGE_INTEGER size;
2041 size.u.HighPart = 0;
2044 hr = StorageBaseImpl_OpenStream(
2045 (IStorage*)parentStorage,
2046 (OLECHAR*)propertyToDelete.name,
2048 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2060 hr = IStream_SetSize(pis, size);
2068 * Release the stream object.
2070 IStream_Release(pis);
2073 * Invalidate the property by zeroing its name member.
2075 propertyToDelete.sizeOfNameString = 0;
2078 * Here we should re-read the property so we get the updated pointer
2079 * but since we are here to zap it, I don't do it...
2081 StorageImpl_WriteProperty(
2082 parentStorage->base.ancestorStorage,
2083 indexOfPropertyToDelete,
2089 /*********************************************************************
2093 * Finds a placeholder for the StgProperty within the Storage
2096 static HRESULT findPlaceholder(
2097 StorageImpl *storage,
2098 ULONG propertyIndexToStore,
2099 ULONG storePropertyIndex,
2102 StgProperty storeProperty;
2107 * Read the storage property
2109 res = StorageImpl_ReadProperty(
2110 storage->base.ancestorStorage,
2119 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2121 if (storeProperty.previousProperty != PROPERTY_NULL)
2123 return findPlaceholder(
2125 propertyIndexToStore,
2126 storeProperty.previousProperty,
2131 storeProperty.previousProperty = propertyIndexToStore;
2134 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2136 if (storeProperty.nextProperty != PROPERTY_NULL)
2138 return findPlaceholder(
2140 propertyIndexToStore,
2141 storeProperty.nextProperty,
2146 storeProperty.nextProperty = propertyIndexToStore;
2149 else if (typeOfRelation == PROPERTY_RELATION_DIR)
2151 if (storeProperty.dirProperty != PROPERTY_NULL)
2153 return findPlaceholder(
2155 propertyIndexToStore,
2156 storeProperty.dirProperty,
2161 storeProperty.dirProperty = propertyIndexToStore;
2165 hr = StorageImpl_WriteProperty(
2166 storage->base.ancestorStorage,
2178 /*************************************************************************
2182 * This method takes the previous and the next property link of a property
2183 * to be deleted and find them a place in the Storage.
2185 static HRESULT adjustPropertyChain(
2187 StgProperty propertyToDelete,
2188 StgProperty parentProperty,
2189 ULONG parentPropertyId,
2192 ULONG newLinkProperty = PROPERTY_NULL;
2193 BOOL needToFindAPlaceholder = FALSE;
2194 ULONG storeNode = PROPERTY_NULL;
2195 ULONG toStoreNode = PROPERTY_NULL;
2196 INT relationType = 0;
2200 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2202 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2205 * Set the parent previous to the property to delete previous
2207 newLinkProperty = propertyToDelete.previousProperty;
2209 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2212 * We also need to find a storage for the other link, setup variables
2213 * to do this at the end...
2215 needToFindAPlaceholder = TRUE;
2216 storeNode = propertyToDelete.previousProperty;
2217 toStoreNode = propertyToDelete.nextProperty;
2218 relationType = PROPERTY_RELATION_NEXT;
2221 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2224 * Set the parent previous to the property to delete next
2226 newLinkProperty = propertyToDelete.nextProperty;
2230 * Link it for real...
2232 parentProperty.previousProperty = newLinkProperty;
2235 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2237 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2240 * Set the parent next to the property to delete next previous
2242 newLinkProperty = propertyToDelete.previousProperty;
2244 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2247 * We also need to find a storage for the other link, setup variables
2248 * to do this at the end...
2250 needToFindAPlaceholder = TRUE;
2251 storeNode = propertyToDelete.previousProperty;
2252 toStoreNode = propertyToDelete.nextProperty;
2253 relationType = PROPERTY_RELATION_NEXT;
2256 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2259 * Set the parent next to the property to delete next
2261 newLinkProperty = propertyToDelete.nextProperty;
2265 * Link it for real...
2267 parentProperty.nextProperty = newLinkProperty;
2269 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2271 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2274 * Set the parent dir to the property to delete previous
2276 newLinkProperty = propertyToDelete.previousProperty;
2278 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2281 * We also need to find a storage for the other link, setup variables
2282 * to do this at the end...
2284 needToFindAPlaceholder = TRUE;
2285 storeNode = propertyToDelete.previousProperty;
2286 toStoreNode = propertyToDelete.nextProperty;
2287 relationType = PROPERTY_RELATION_NEXT;
2290 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2293 * Set the parent dir to the property to delete next
2295 newLinkProperty = propertyToDelete.nextProperty;
2299 * Link it for real...
2301 parentProperty.dirProperty = newLinkProperty;
2305 * Write back the parent property
2307 res = StorageImpl_WriteProperty(
2308 This->base.ancestorStorage,
2317 * If a placeholder is required for the other link, then, find one and
2318 * get out of here...
2320 if (needToFindAPlaceholder)
2322 hr = findPlaceholder(
2333 /******************************************************************************
2334 * SetElementTimes (IStorage)
2336 static HRESULT WINAPI StorageImpl_SetElementTimes(
2338 const OLECHAR *pwcsName,/* [string][in] */
2339 const FILETIME *pctime, /* [in] */
2340 const FILETIME *patime, /* [in] */
2341 const FILETIME *pmtime) /* [in] */
2343 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2347 /******************************************************************************
2348 * SetStateBits (IStorage)
2350 static HRESULT WINAPI StorageImpl_SetStateBits(
2352 DWORD grfStateBits,/* [in] */
2353 DWORD grfMask) /* [in] */
2355 FIXME("not implemented!\n");
2360 * Virtual function table for the IStorage32Impl class.
2362 static const IStorageVtbl Storage32Impl_Vtbl =
2364 StorageBaseImpl_QueryInterface,
2365 StorageBaseImpl_AddRef,
2366 StorageBaseImpl_Release,
2367 StorageBaseImpl_CreateStream,
2368 StorageBaseImpl_OpenStream,
2369 StorageImpl_CreateStorage,
2370 StorageBaseImpl_OpenStorage,
2372 StorageImpl_MoveElementTo,
2375 StorageBaseImpl_EnumElements,
2376 StorageImpl_DestroyElement,
2377 StorageBaseImpl_RenameElement,
2378 StorageImpl_SetElementTimes,
2379 StorageBaseImpl_SetClass,
2380 StorageImpl_SetStateBits,
2384 static HRESULT StorageImpl_Construct(
2394 StgProperty currentProperty;
2395 BOOL readSuccessful;
2396 ULONG currentPropertyIndex;
2398 if ( FAILED( validateSTGM(openFlags) ))
2399 return STG_E_INVALIDFLAG;
2401 memset(This, 0, sizeof(StorageImpl));
2404 * Initialize stream list
2407 list_init(&This->base.strmHead);
2410 * Initialize the virtual function table.
2412 This->base.lpVtbl = &Storage32Impl_Vtbl;
2413 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2414 This->base.v_destructor = &StorageImpl_Destroy;
2415 This->base.openFlags = (openFlags & ~STGM_CREATE);
2418 * This is the top-level storage so initialize the ancestor pointer
2421 This->base.ancestorStorage = This;
2424 * Initialize the physical support of the storage.
2426 This->hFile = hFile;
2429 * Store copy of file path.
2432 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2433 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2434 if (!This->pwcsName)
2435 return STG_E_INSUFFICIENTMEMORY;
2436 strcpyW(This->pwcsName, pwcsName);
2440 * Initialize the big block cache.
2442 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2443 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2444 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2450 if (This->bigBlockFile == 0)
2455 ULARGE_INTEGER size;
2456 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2459 * Initialize all header variables:
2460 * - The big block depot consists of one block and it is at block 0
2461 * - The properties start at block 1
2462 * - There is no small block depot
2464 memset( This->bigBlockDepotStart,
2466 sizeof(This->bigBlockDepotStart));
2468 This->bigBlockDepotCount = 1;
2469 This->bigBlockDepotStart[0] = 0;
2470 This->rootStartBlock = 1;
2471 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2472 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2473 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2474 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2475 This->extBigBlockDepotCount = 0;
2477 StorageImpl_SaveFileHeader(This);
2480 * Add one block for the big block depot and one block for the properties
2482 size.u.HighPart = 0;
2483 size.u.LowPart = This->bigBlockSize * 3;
2484 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2487 * Initialize the big block depot
2489 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2490 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2491 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2492 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2497 * Load the header for the file.
2499 hr = StorageImpl_LoadFileHeader(This);
2503 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2510 * There is no block depot cached yet.
2512 This->indexBlockDepotCached = 0xFFFFFFFF;
2515 * Start searching for free blocks with block 0.
2517 This->prevFreeBlock = 0;
2520 * Create the block chain abstractions.
2522 if(!(This->rootBlockChain =
2523 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2524 return STG_E_READFAULT;
2526 if(!(This->smallBlockDepotChain =
2527 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2529 return STG_E_READFAULT;
2532 * Write the root property (memory only)
2536 StgProperty rootProp;
2538 * Initialize the property chain
2540 memset(&rootProp, 0, sizeof(rootProp));
2541 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2542 sizeof(rootProp.name)/sizeof(WCHAR) );
2543 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2544 rootProp.propertyType = PROPTYPE_ROOT;
2545 rootProp.previousProperty = PROPERTY_NULL;
2546 rootProp.nextProperty = PROPERTY_NULL;
2547 rootProp.dirProperty = PROPERTY_NULL;
2548 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2549 rootProp.size.u.HighPart = 0;
2550 rootProp.size.u.LowPart = 0;
2552 StorageImpl_WriteProperty(This, 0, &rootProp);
2556 * Find the ID of the root in the property sets.
2558 currentPropertyIndex = 0;
2562 readSuccessful = StorageImpl_ReadProperty(
2564 currentPropertyIndex,
2569 if ( (currentProperty.sizeOfNameString != 0 ) &&
2570 (currentProperty.propertyType == PROPTYPE_ROOT) )
2572 This->base.rootPropertySetIndex = currentPropertyIndex;
2576 currentPropertyIndex++;
2578 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2580 if (!readSuccessful)
2583 return STG_E_READFAULT;
2587 * Create the block chain abstraction for the small block root chain.
2589 if(!(This->smallBlockRootChain =
2590 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2591 return STG_E_READFAULT;
2596 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2598 StorageImpl *This = (StorageImpl*) iface;
2599 TRACE("(%p)\n", This);
2601 StorageBaseImpl_DeleteAll(&This->base);
2603 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2605 BlockChainStream_Destroy(This->smallBlockRootChain);
2606 BlockChainStream_Destroy(This->rootBlockChain);
2607 BlockChainStream_Destroy(This->smallBlockDepotChain);
2609 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2610 HeapFree(GetProcessHeap(), 0, This);
2613 /******************************************************************************
2614 * Storage32Impl_GetNextFreeBigBlock
2616 * Returns the index of the next free big block.
2617 * If the big block depot is filled, this method will enlarge it.
2620 static ULONG StorageImpl_GetNextFreeBigBlock(
2623 ULONG depotBlockIndexPos;
2624 BYTE depotBuffer[BIG_BLOCK_SIZE];
2626 ULONG depotBlockOffset;
2627 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2628 ULONG nextBlockIndex = BLOCK_SPECIAL;
2630 ULONG freeBlock = BLOCK_UNUSED;
2632 depotIndex = This->prevFreeBlock / blocksPerDepot;
2633 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2636 * Scan the entire big block depot until we find a block marked free
2638 while (nextBlockIndex != BLOCK_UNUSED)
2640 if (depotIndex < COUNT_BBDEPOTINHEADER)
2642 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2645 * Grow the primary depot.
2647 if (depotBlockIndexPos == BLOCK_UNUSED)
2649 depotBlockIndexPos = depotIndex*blocksPerDepot;
2652 * Add a block depot.
2654 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2655 This->bigBlockDepotCount++;
2656 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2659 * Flag it as a block depot.
2661 StorageImpl_SetNextBlockInChain(This,
2665 /* Save new header information.
2667 StorageImpl_SaveFileHeader(This);
2672 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2674 if (depotBlockIndexPos == BLOCK_UNUSED)
2677 * Grow the extended depot.
2679 ULONG extIndex = BLOCK_UNUSED;
2680 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2681 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2683 if (extBlockOffset == 0)
2685 /* We need an extended block.
2687 extIndex = Storage32Impl_AddExtBlockDepot(This);
2688 This->extBigBlockDepotCount++;
2689 depotBlockIndexPos = extIndex + 1;
2692 depotBlockIndexPos = depotIndex * blocksPerDepot;
2695 * Add a block depot and mark it in the extended block.
2697 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2698 This->bigBlockDepotCount++;
2699 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2701 /* Flag the block depot.
2703 StorageImpl_SetNextBlockInChain(This,
2707 /* If necessary, flag the extended depot block.
2709 if (extIndex != BLOCK_UNUSED)
2710 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2712 /* Save header information.
2714 StorageImpl_SaveFileHeader(This);
2718 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2722 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2723 ( nextBlockIndex != BLOCK_UNUSED))
2725 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2727 if (nextBlockIndex == BLOCK_UNUSED)
2729 freeBlock = (depotIndex * blocksPerDepot) +
2730 (depotBlockOffset/sizeof(ULONG));
2733 depotBlockOffset += sizeof(ULONG);
2738 depotBlockOffset = 0;
2742 * make sure that the block physically exists before using it
2744 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2746 This->prevFreeBlock = freeBlock;
2751 /******************************************************************************
2752 * Storage32Impl_AddBlockDepot
2754 * This will create a depot block, essentially it is a block initialized
2757 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2759 BYTE blockBuffer[BIG_BLOCK_SIZE];
2762 * Initialize blocks as free
2764 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2765 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2768 /******************************************************************************
2769 * Storage32Impl_GetExtDepotBlock
2771 * Returns the index of the block that corresponds to the specified depot
2772 * index. This method is only for depot indexes equal or greater than
2773 * COUNT_BBDEPOTINHEADER.
2775 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2777 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2778 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2779 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2780 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2781 ULONG blockIndex = BLOCK_UNUSED;
2782 ULONG extBlockIndex = This->extBigBlockDepotStart;
2784 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2786 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2787 return BLOCK_UNUSED;
2789 while (extBlockCount > 0)
2791 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2795 if (extBlockIndex != BLOCK_UNUSED)
2796 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
2797 extBlockOffset * sizeof(ULONG), &blockIndex);
2802 /******************************************************************************
2803 * Storage32Impl_SetExtDepotBlock
2805 * Associates the specified block index to the specified depot index.
2806 * This method is only for depot indexes equal or greater than
2807 * COUNT_BBDEPOTINHEADER.
2809 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2811 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2812 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2813 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2814 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2815 ULONG extBlockIndex = This->extBigBlockDepotStart;
2817 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2819 while (extBlockCount > 0)
2821 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2825 if (extBlockIndex != BLOCK_UNUSED)
2827 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
2828 extBlockOffset * sizeof(ULONG),
2833 /******************************************************************************
2834 * Storage32Impl_AddExtBlockDepot
2836 * Creates an extended depot block.
2838 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2840 ULONG numExtBlocks = This->extBigBlockDepotCount;
2841 ULONG nextExtBlock = This->extBigBlockDepotStart;
2842 BYTE depotBuffer[BIG_BLOCK_SIZE];
2843 ULONG index = BLOCK_UNUSED;
2844 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2845 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2846 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2848 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2849 blocksPerDepotBlock;
2851 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2854 * The first extended block.
2856 This->extBigBlockDepotStart = index;
2862 * Follow the chain to the last one.
2864 for (i = 0; i < (numExtBlocks - 1); i++)
2866 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2870 * Add the new extended block to the chain.
2872 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
2877 * Initialize this block.
2879 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2880 StorageImpl_WriteBigBlock(This, index, depotBuffer);
2885 /******************************************************************************
2886 * Storage32Impl_FreeBigBlock
2888 * This method will flag the specified block as free in the big block depot.
2890 static void StorageImpl_FreeBigBlock(
2894 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2896 if (blockIndex < This->prevFreeBlock)
2897 This->prevFreeBlock = blockIndex;
2900 /************************************************************************
2901 * Storage32Impl_GetNextBlockInChain
2903 * This method will retrieve the block index of the next big block in
2906 * Params: This - Pointer to the Storage object.
2907 * blockIndex - Index of the block to retrieve the chain
2909 * nextBlockIndex - receives the return value.
2911 * Returns: This method returns the index of the next block in the chain.
2912 * It will return the constants:
2913 * BLOCK_SPECIAL - If the block given was not part of a
2915 * BLOCK_END_OF_CHAIN - If the block given was the last in
2917 * BLOCK_UNUSED - If the block given was not past of a chain
2919 * BLOCK_EXTBBDEPOT - This block is part of the extended
2922 * See Windows documentation for more details on IStorage methods.
2924 static HRESULT StorageImpl_GetNextBlockInChain(
2927 ULONG* nextBlockIndex)
2929 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2930 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2931 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2932 BYTE depotBuffer[BIG_BLOCK_SIZE];
2934 ULONG depotBlockIndexPos;
2937 *nextBlockIndex = BLOCK_SPECIAL;
2939 if(depotBlockCount >= This->bigBlockDepotCount)
2941 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
2942 This->bigBlockDepotCount);
2943 return STG_E_READFAULT;
2947 * Cache the currently accessed depot block.
2949 if (depotBlockCount != This->indexBlockDepotCached)
2951 This->indexBlockDepotCached = depotBlockCount;
2953 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2955 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2960 * We have to look in the extended depot.
2962 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2965 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2968 return STG_E_READFAULT;
2970 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2972 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2973 This->blockDepotCached[index] = *nextBlockIndex;
2977 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2982 /******************************************************************************
2983 * Storage32Impl_GetNextExtendedBlock
2985 * Given an extended block this method will return the next extended block.
2988 * The last ULONG of an extended block is the block index of the next
2989 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2993 * - The index of the next extended block
2994 * - BLOCK_UNUSED: there is no next extended block.
2995 * - Any other return values denotes failure.
2997 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2999 ULONG nextBlockIndex = BLOCK_SPECIAL;
3000 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3002 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3005 return nextBlockIndex;
3008 /******************************************************************************
3009 * Storage32Impl_SetNextBlockInChain
3011 * This method will write the index of the specified block's next block
3012 * in the big block depot.
3014 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3017 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3018 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3019 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3022 static void StorageImpl_SetNextBlockInChain(
3027 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3028 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3029 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3030 ULONG depotBlockIndexPos;
3032 assert(depotBlockCount < This->bigBlockDepotCount);
3033 assert(blockIndex != nextBlock);
3035 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3037 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3042 * We have to look in the extended depot.
3044 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3047 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3050 * Update the cached block depot, if necessary.
3052 if (depotBlockCount == This->indexBlockDepotCached)
3054 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3058 /******************************************************************************
3059 * Storage32Impl_LoadFileHeader
3061 * This method will read in the file header, i.e. big block index -1.
3063 static HRESULT StorageImpl_LoadFileHeader(
3066 HRESULT hr = STG_E_FILENOTFOUND;
3067 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3073 * Get a pointer to the big block of data containing the header.
3075 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3078 * Extract the information from the header.
3083 * Check for the "magic number" signature and return an error if it is not
3086 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3088 return STG_E_OLDFORMAT;
3091 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3093 return STG_E_INVALIDHEADER;
3096 StorageUtl_ReadWord(
3098 OFFSET_BIGBLOCKSIZEBITS,
3099 &This->bigBlockSizeBits);
3101 StorageUtl_ReadWord(
3103 OFFSET_SMALLBLOCKSIZEBITS,
3104 &This->smallBlockSizeBits);
3106 StorageUtl_ReadDWord(
3108 OFFSET_BBDEPOTCOUNT,
3109 &This->bigBlockDepotCount);
3111 StorageUtl_ReadDWord(
3113 OFFSET_ROOTSTARTBLOCK,
3114 &This->rootStartBlock);
3116 StorageUtl_ReadDWord(
3118 OFFSET_SBDEPOTSTART,
3119 &This->smallBlockDepotStart);
3121 StorageUtl_ReadDWord(
3123 OFFSET_EXTBBDEPOTSTART,
3124 &This->extBigBlockDepotStart);
3126 StorageUtl_ReadDWord(
3128 OFFSET_EXTBBDEPOTCOUNT,
3129 &This->extBigBlockDepotCount);
3131 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3133 StorageUtl_ReadDWord(
3135 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3136 &(This->bigBlockDepotStart[index]));
3140 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3144 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3145 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3149 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
3150 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
3154 * Right now, the code is making some assumptions about the size of the
3155 * blocks, just make sure they are what we're expecting.
3157 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3158 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3160 WARN("Broken OLE storage file\n");
3161 hr = STG_E_INVALIDHEADER;
3170 /******************************************************************************
3171 * Storage32Impl_SaveFileHeader
3173 * This method will save to the file the header, i.e. big block -1.
3175 static void StorageImpl_SaveFileHeader(
3178 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3183 * Get a pointer to the big block of data containing the header.
3185 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3188 * If the block read failed, the file is probably new.
3193 * Initialize for all unknown fields.
3195 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3198 * Initialize the magic number.
3200 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3203 * And a bunch of things we don't know what they mean
3205 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3206 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3207 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3208 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3212 * Write the information to the header.
3214 StorageUtl_WriteWord(
3216 OFFSET_BIGBLOCKSIZEBITS,
3217 This->bigBlockSizeBits);
3219 StorageUtl_WriteWord(
3221 OFFSET_SMALLBLOCKSIZEBITS,
3222 This->smallBlockSizeBits);
3224 StorageUtl_WriteDWord(
3226 OFFSET_BBDEPOTCOUNT,
3227 This->bigBlockDepotCount);
3229 StorageUtl_WriteDWord(
3231 OFFSET_ROOTSTARTBLOCK,
3232 This->rootStartBlock);
3234 StorageUtl_WriteDWord(
3236 OFFSET_SBDEPOTSTART,
3237 This->smallBlockDepotStart);
3239 StorageUtl_WriteDWord(
3241 OFFSET_SBDEPOTCOUNT,
3242 This->smallBlockDepotChain ?
3243 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3245 StorageUtl_WriteDWord(
3247 OFFSET_EXTBBDEPOTSTART,
3248 This->extBigBlockDepotStart);
3250 StorageUtl_WriteDWord(
3252 OFFSET_EXTBBDEPOTCOUNT,
3253 This->extBigBlockDepotCount);
3255 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3257 StorageUtl_WriteDWord(
3259 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3260 (This->bigBlockDepotStart[index]));
3264 * Write the big block back to the file.
3266 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3269 /******************************************************************************
3270 * Storage32Impl_ReadProperty
3272 * This method will read the specified property from the property chain.
3274 BOOL StorageImpl_ReadProperty(
3277 StgProperty* buffer)
3279 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3280 ULARGE_INTEGER offsetInPropSet;
3284 offsetInPropSet.u.HighPart = 0;
3285 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3287 readRes = BlockChainStream_ReadAt(
3288 This->rootBlockChain,
3294 if (SUCCEEDED(readRes))
3296 /* replace the name of root entry (often "Root Entry") by the file name */
3297 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3298 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3300 memset(buffer->name, 0, sizeof(buffer->name));
3304 PROPERTY_NAME_BUFFER_LEN );
3305 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3307 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3309 StorageUtl_ReadWord(
3311 OFFSET_PS_NAMELENGTH,
3312 &buffer->sizeOfNameString);
3314 StorageUtl_ReadDWord(
3316 OFFSET_PS_PREVIOUSPROP,
3317 &buffer->previousProperty);
3319 StorageUtl_ReadDWord(
3322 &buffer->nextProperty);
3324 StorageUtl_ReadDWord(
3327 &buffer->dirProperty);
3329 StorageUtl_ReadGUID(
3332 &buffer->propertyUniqueID);
3334 StorageUtl_ReadDWord(
3337 &buffer->timeStampS1);
3339 StorageUtl_ReadDWord(
3342 &buffer->timeStampD1);
3344 StorageUtl_ReadDWord(
3347 &buffer->timeStampS2);
3349 StorageUtl_ReadDWord(
3352 &buffer->timeStampD2);
3354 StorageUtl_ReadDWord(
3356 OFFSET_PS_STARTBLOCK,
3357 &buffer->startingBlock);
3359 StorageUtl_ReadDWord(
3362 &buffer->size.u.LowPart);
3364 buffer->size.u.HighPart = 0;
3367 return SUCCEEDED(readRes) ? TRUE : FALSE;
3370 /*********************************************************************
3371 * Write the specified property into the property chain
3373 BOOL StorageImpl_WriteProperty(
3376 StgProperty* buffer)
3378 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3379 ULARGE_INTEGER offsetInPropSet;
3383 offsetInPropSet.u.HighPart = 0;
3384 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3386 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3389 currentProperty + OFFSET_PS_NAME,
3391 PROPERTY_NAME_BUFFER_LEN );
3393 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3395 StorageUtl_WriteWord(
3397 OFFSET_PS_NAMELENGTH,
3398 buffer->sizeOfNameString);
3400 StorageUtl_WriteDWord(
3402 OFFSET_PS_PREVIOUSPROP,
3403 buffer->previousProperty);
3405 StorageUtl_WriteDWord(
3408 buffer->nextProperty);
3410 StorageUtl_WriteDWord(
3413 buffer->dirProperty);
3415 StorageUtl_WriteGUID(
3418 &buffer->propertyUniqueID);
3420 StorageUtl_WriteDWord(
3423 buffer->timeStampS1);
3425 StorageUtl_WriteDWord(
3428 buffer->timeStampD1);
3430 StorageUtl_WriteDWord(
3433 buffer->timeStampS2);
3435 StorageUtl_WriteDWord(
3438 buffer->timeStampD2);
3440 StorageUtl_WriteDWord(
3442 OFFSET_PS_STARTBLOCK,
3443 buffer->startingBlock);
3445 StorageUtl_WriteDWord(
3448 buffer->size.u.LowPart);
3450 writeRes = BlockChainStream_WriteAt(This->rootBlockChain,
3455 return SUCCEEDED(writeRes) ? TRUE : FALSE;
3458 static BOOL StorageImpl_ReadBigBlock(
3463 ULARGE_INTEGER ulOffset;
3466 ulOffset.u.HighPart = 0;
3467 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3469 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3470 return (read == This->bigBlockSize);
3473 static BOOL StorageImpl_ReadDWordFromBigBlock(
3479 ULARGE_INTEGER ulOffset;
3483 ulOffset.u.HighPart = 0;
3484 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3485 ulOffset.u.LowPart += offset;
3487 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3488 *value = le32toh(tmp);
3489 return (read == sizeof(DWORD));
3492 static BOOL StorageImpl_WriteBigBlock(
3497 ULARGE_INTEGER ulOffset;
3500 ulOffset.u.HighPart = 0;
3501 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3503 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3504 return (wrote == This->bigBlockSize);
3507 static BOOL StorageImpl_WriteDWordToBigBlock(
3513 ULARGE_INTEGER ulOffset;
3516 ulOffset.u.HighPart = 0;
3517 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3518 ulOffset.u.LowPart += offset;
3520 value = htole32(value);
3521 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3522 return (wrote == sizeof(DWORD));
3525 /******************************************************************************
3526 * Storage32Impl_SmallBlocksToBigBlocks
3528 * This method will convert a small block chain to a big block chain.
3529 * The small block chain will be destroyed.
3531 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3533 SmallBlockChainStream** ppsbChain)
3535 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3536 ULARGE_INTEGER size, offset;
3537 ULONG cbRead, cbWritten;
3538 ULARGE_INTEGER cbTotalRead;
3539 ULONG propertyIndex;
3540 HRESULT resWrite = S_OK;
3542 StgProperty chainProperty;
3544 BlockChainStream *bbTempChain = NULL;
3545 BlockChainStream *bigBlockChain = NULL;
3548 * Create a temporary big block chain that doesn't have
3549 * an associated property. This temporary chain will be
3550 * used to copy data from small blocks to big blocks.
3552 bbTempChain = BlockChainStream_Construct(This,
3555 if(!bbTempChain) return NULL;
3557 * Grow the big block chain.
3559 size = SmallBlockChainStream_GetSize(*ppsbChain);
3560 BlockChainStream_SetSize(bbTempChain, size);
3563 * Copy the contents of the small block chain to the big block chain
3564 * by small block size increments.
3566 offset.u.LowPart = 0;
3567 offset.u.HighPart = 0;
3568 cbTotalRead.QuadPart = 0;
3570 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3573 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3575 This->smallBlockSize,
3578 if (FAILED(resRead))
3583 cbTotalRead.QuadPart += cbRead;
3585 resWrite = BlockChainStream_WriteAt(bbTempChain,
3591 if (FAILED(resWrite))
3594 offset.u.LowPart += This->smallBlockSize;
3596 } while (cbTotalRead.QuadPart < size.QuadPart);
3597 HeapFree(GetProcessHeap(),0,buffer);
3599 if (FAILED(resRead) || FAILED(resWrite))
3601 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3602 BlockChainStream_Destroy(bbTempChain);
3607 * Destroy the small block chain.
3609 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3610 size.u.HighPart = 0;
3612 SmallBlockChainStream_SetSize(*ppsbChain, size);
3613 SmallBlockChainStream_Destroy(*ppsbChain);
3617 * Change the property information. This chain is now a big block chain
3618 * and it doesn't reside in the small blocks chain anymore.
3620 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3622 chainProperty.startingBlock = bbHeadOfChain;
3624 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3627 * Destroy the temporary propertyless big block chain.
3628 * Create a new big block chain associated with this property.
3630 BlockChainStream_Destroy(bbTempChain);
3631 bigBlockChain = BlockChainStream_Construct(This,
3635 return bigBlockChain;
3638 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3640 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3642 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3643 HeapFree(GetProcessHeap(), 0, This);
3646 /******************************************************************************
3648 ** Storage32InternalImpl_Commit
3650 ** The non-root storages cannot be opened in transacted mode thus this function
3653 static HRESULT WINAPI StorageInternalImpl_Commit(
3655 DWORD grfCommitFlags) /* [in] */
3660 /******************************************************************************
3662 ** Storage32InternalImpl_Revert
3664 ** The non-root storages cannot be opened in transacted mode thus this function
3667 static HRESULT WINAPI StorageInternalImpl_Revert(
3673 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3675 IStorage_Release((IStorage*)This->parentStorage);
3676 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3677 HeapFree(GetProcessHeap(), 0, This);
3680 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3681 IEnumSTATSTG* iface,
3685 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3688 * Perform a sanity check on the parameters.
3691 return E_INVALIDARG;
3694 * Initialize the return parameter.
3699 * Compare the riid with the interface IDs implemented by this object.
3701 if (IsEqualGUID(&IID_IUnknown, riid) ||
3702 IsEqualGUID(&IID_IEnumSTATSTG, riid))
3704 *ppvObject = (IEnumSTATSTG*)This;
3705 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3709 return E_NOINTERFACE;
3712 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3713 IEnumSTATSTG* iface)
3715 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3716 return InterlockedIncrement(&This->ref);
3719 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3720 IEnumSTATSTG* iface)
3722 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3726 newRef = InterlockedDecrement(&This->ref);
3729 * If the reference count goes down to 0, perform suicide.
3733 IEnumSTATSTGImpl_Destroy(This);
3739 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3740 IEnumSTATSTG* iface,
3743 ULONG* pceltFetched)
3745 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3747 StgProperty currentProperty;
3748 STATSTG* currentReturnStruct = rgelt;
3749 ULONG objectFetched = 0;
3750 ULONG currentSearchNode;
3753 * Perform a sanity check on the parameters.
3755 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3756 return E_INVALIDARG;
3759 * To avoid the special case, get another pointer to a ULONG value if
3760 * the caller didn't supply one.
3762 if (pceltFetched==0)
3763 pceltFetched = &objectFetched;
3766 * Start the iteration, we will iterate until we hit the end of the
3767 * linked list or until we hit the number of items to iterate through
3772 * Start with the node at the top of the stack.
3774 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3776 while ( ( *pceltFetched < celt) &&
3777 ( currentSearchNode!=PROPERTY_NULL) )
3780 * Remove the top node from the stack
3782 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3785 * Read the property from the storage.
3787 StorageImpl_ReadProperty(This->parentStorage,
3792 * Copy the information to the return buffer.
3794 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3799 * Step to the next item in the iteration
3802 currentReturnStruct++;
3805 * Push the next search node in the search stack.
3807 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3810 * continue the iteration.
3812 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3815 if (*pceltFetched == celt)
3822 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3823 IEnumSTATSTG* iface,
3826 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3828 StgProperty currentProperty;
3829 ULONG objectFetched = 0;
3830 ULONG currentSearchNode;
3833 * Start with the node at the top of the stack.
3835 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3837 while ( (objectFetched < celt) &&
3838 (currentSearchNode!=PROPERTY_NULL) )
3841 * Remove the top node from the stack
3843 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3846 * Read the property from the storage.
3848 StorageImpl_ReadProperty(This->parentStorage,
3853 * Step to the next item in the iteration
3858 * Push the next search node in the search stack.
3860 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3863 * continue the iteration.
3865 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3868 if (objectFetched == celt)
3874 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3875 IEnumSTATSTG* iface)
3877 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3879 StgProperty rootProperty;
3880 BOOL readSuccessful;
3883 * Re-initialize the search stack to an empty stack
3885 This->stackSize = 0;
3888 * Read the root property from the storage.
3890 readSuccessful = StorageImpl_ReadProperty(
3891 This->parentStorage,
3892 This->firstPropertyNode,
3897 assert(rootProperty.sizeOfNameString!=0);
3900 * Push the search node in the search stack.
3902 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3908 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3909 IEnumSTATSTG* iface,
3910 IEnumSTATSTG** ppenum)
3912 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3914 IEnumSTATSTGImpl* newClone;
3917 * Perform a sanity check on the parameters.
3920 return E_INVALIDARG;
3922 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3923 This->firstPropertyNode);
3927 * The new clone enumeration must point to the same current node as
3930 newClone->stackSize = This->stackSize ;
3931 newClone->stackMaxSize = This->stackMaxSize ;
3932 newClone->stackToVisit =
3933 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3936 newClone->stackToVisit,
3938 sizeof(ULONG) * newClone->stackSize);
3940 *ppenum = (IEnumSTATSTG*)newClone;
3943 * Don't forget to nail down a reference to the clone before
3946 IEnumSTATSTGImpl_AddRef(*ppenum);
3951 static INT IEnumSTATSTGImpl_FindParentProperty(
3952 IEnumSTATSTGImpl *This,
3953 ULONG childProperty,
3954 StgProperty *currentProperty,
3957 ULONG currentSearchNode;
3961 * To avoid the special case, get another pointer to a ULONG value if
3962 * the caller didn't supply one.
3965 thisNodeId = &foundNode;
3968 * Start with the node at the top of the stack.
3970 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3973 while (currentSearchNode!=PROPERTY_NULL)
3976 * Store the current node in the returned parameters
3978 *thisNodeId = currentSearchNode;
3981 * Remove the top node from the stack
3983 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3986 * Read the property from the storage.
3988 StorageImpl_ReadProperty(
3989 This->parentStorage,
3993 if (currentProperty->previousProperty == childProperty)
3994 return PROPERTY_RELATION_PREVIOUS;
3996 else if (currentProperty->nextProperty == childProperty)
3997 return PROPERTY_RELATION_NEXT;
3999 else if (currentProperty->dirProperty == childProperty)
4000 return PROPERTY_RELATION_DIR;
4003 * Push the next search node in the search stack.
4005 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4008 * continue the iteration.
4010 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4013 return PROPERTY_NULL;
4016 static ULONG IEnumSTATSTGImpl_FindProperty(
4017 IEnumSTATSTGImpl* This,
4018 const OLECHAR* lpszPropName,
4019 StgProperty* currentProperty)
4021 ULONG currentSearchNode;
4024 * Start with the node at the top of the stack.
4026 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4028 while (currentSearchNode!=PROPERTY_NULL)
4031 * Remove the top node from the stack
4033 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4036 * Read the property from the storage.
4038 StorageImpl_ReadProperty(This->parentStorage,
4042 if ( propertyNameCmp(
4043 (const OLECHAR*)currentProperty->name,
4044 (const OLECHAR*)lpszPropName) == 0)
4045 return currentSearchNode;
4048 * Push the next search node in the search stack.
4050 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4053 * continue the iteration.
4055 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4058 return PROPERTY_NULL;
4061 static void IEnumSTATSTGImpl_PushSearchNode(
4062 IEnumSTATSTGImpl* This,
4065 StgProperty rootProperty;
4066 BOOL readSuccessful;
4069 * First, make sure we're not trying to push an unexisting node.
4071 if (nodeToPush==PROPERTY_NULL)
4075 * First push the node to the stack
4077 if (This->stackSize == This->stackMaxSize)
4079 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4081 This->stackToVisit = HeapReAlloc(
4085 sizeof(ULONG) * This->stackMaxSize);
4088 This->stackToVisit[This->stackSize] = nodeToPush;
4092 * Read the root property from the storage.
4094 readSuccessful = StorageImpl_ReadProperty(
4095 This->parentStorage,
4101 assert(rootProperty.sizeOfNameString!=0);
4104 * Push the previous search node in the search stack.
4106 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4110 static ULONG IEnumSTATSTGImpl_PopSearchNode(
4111 IEnumSTATSTGImpl* This,
4116 if (This->stackSize == 0)
4117 return PROPERTY_NULL;
4119 topNode = This->stackToVisit[This->stackSize-1];
4128 * Virtual function table for the IEnumSTATSTGImpl class.
4130 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4132 IEnumSTATSTGImpl_QueryInterface,
4133 IEnumSTATSTGImpl_AddRef,
4134 IEnumSTATSTGImpl_Release,
4135 IEnumSTATSTGImpl_Next,
4136 IEnumSTATSTGImpl_Skip,
4137 IEnumSTATSTGImpl_Reset,
4138 IEnumSTATSTGImpl_Clone
4141 /******************************************************************************
4142 ** IEnumSTATSTGImpl implementation
4145 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4146 StorageImpl* parentStorage,
4147 ULONG firstPropertyNode)
4149 IEnumSTATSTGImpl* newEnumeration;
4151 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4153 if (newEnumeration!=0)
4156 * Set-up the virtual function table and reference count.
4158 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4159 newEnumeration->ref = 0;
4162 * We want to nail-down the reference to the storage in case the
4163 * enumeration out-lives the storage in the client application.
4165 newEnumeration->parentStorage = parentStorage;
4166 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4168 newEnumeration->firstPropertyNode = firstPropertyNode;
4171 * Initialize the search stack
4173 newEnumeration->stackSize = 0;
4174 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4175 newEnumeration->stackToVisit =
4176 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4179 * Make sure the current node of the iterator is the first one.
4181 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4184 return newEnumeration;
4188 * Virtual function table for the Storage32InternalImpl class.
4190 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4192 StorageBaseImpl_QueryInterface,
4193 StorageBaseImpl_AddRef,
4194 StorageBaseImpl_Release,
4195 StorageBaseImpl_CreateStream,
4196 StorageBaseImpl_OpenStream,
4197 StorageImpl_CreateStorage,
4198 StorageBaseImpl_OpenStorage,
4200 StorageImpl_MoveElementTo,
4201 StorageInternalImpl_Commit,
4202 StorageInternalImpl_Revert,
4203 StorageBaseImpl_EnumElements,
4204 StorageImpl_DestroyElement,
4205 StorageBaseImpl_RenameElement,
4206 StorageImpl_SetElementTimes,
4207 StorageBaseImpl_SetClass,
4208 StorageImpl_SetStateBits,
4209 StorageBaseImpl_Stat
4212 /******************************************************************************
4213 ** Storage32InternalImpl implementation
4216 static StorageInternalImpl* StorageInternalImpl_Construct(
4217 StorageImpl* ancestorStorage,
4219 ULONG rootPropertyIndex)
4221 StorageInternalImpl* newStorage;
4224 * Allocate space for the new storage object
4226 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
4231 * Initialize the stream list
4233 list_init(&newStorage->base.strmHead);
4236 * Initialize the virtual function table.
4238 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4239 newStorage->base.v_destructor = &StorageInternalImpl_Destroy;
4240 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4243 * Keep the ancestor storage pointer and nail a reference to it.
4245 newStorage->base.ancestorStorage = ancestorStorage;
4246 StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
4249 * Keep the index of the root property set for this storage,
4251 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4259 /******************************************************************************
4260 ** StorageUtl implementation
4263 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4267 memcpy(&tmp, buffer+offset, sizeof(WORD));
4268 *value = le16toh(tmp);
4271 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4273 value = htole16(value);
4274 memcpy(buffer+offset, &value, sizeof(WORD));
4277 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4281 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4282 *value = le32toh(tmp);
4285 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4287 value = htole32(value);
4288 memcpy(buffer+offset, &value, sizeof(DWORD));
4291 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4292 ULARGE_INTEGER* value)
4294 #ifdef WORDS_BIGENDIAN
4297 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4298 value->u.LowPart = htole32(tmp.u.HighPart);
4299 value->u.HighPart = htole32(tmp.u.LowPart);
4301 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4305 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4306 const ULARGE_INTEGER *value)
4308 #ifdef WORDS_BIGENDIAN
4311 tmp.u.LowPart = htole32(value->u.HighPart);
4312 tmp.u.HighPart = htole32(value->u.LowPart);
4313 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4315 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4319 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4321 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4322 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4323 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4325 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4328 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4330 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4331 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4332 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4334 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4337 void StorageUtl_CopyPropertyToSTATSTG(
4338 STATSTG* destination,
4339 StgProperty* source,
4343 * The copy of the string occurs only when the flag is not set
4345 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4346 (source->name == NULL) ||
4347 (source->name[0] == 0) )
4349 destination->pwcsName = 0;
4353 destination->pwcsName =
4354 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4356 strcpyW((LPWSTR)destination->pwcsName, source->name);
4359 switch (source->propertyType)
4361 case PROPTYPE_STORAGE:
4363 destination->type = STGTY_STORAGE;
4365 case PROPTYPE_STREAM:
4366 destination->type = STGTY_STREAM;
4369 destination->type = STGTY_STREAM;
4373 destination->cbSize = source->size;
4375 currentReturnStruct->mtime = {0}; TODO
4376 currentReturnStruct->ctime = {0};
4377 currentReturnStruct->atime = {0};
4379 destination->grfMode = 0;
4380 destination->grfLocksSupported = 0;
4381 destination->clsid = source->propertyUniqueID;
4382 destination->grfStateBits = 0;
4383 destination->reserved = 0;
4386 /******************************************************************************
4387 ** BlockChainStream implementation
4390 BlockChainStream* BlockChainStream_Construct(
4391 StorageImpl* parentStorage,
4392 ULONG* headOfStreamPlaceHolder,
4393 ULONG propertyIndex)
4395 BlockChainStream* newStream;
4398 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4400 newStream->parentStorage = parentStorage;
4401 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4402 newStream->ownerPropertyIndex = propertyIndex;
4403 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4404 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4405 newStream->numBlocks = 0;
4407 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4409 while (blockIndex != BLOCK_END_OF_CHAIN)
4411 newStream->numBlocks++;
4412 newStream->tailIndex = blockIndex;
4414 if(FAILED(StorageImpl_GetNextBlockInChain(
4419 HeapFree(GetProcessHeap(), 0, newStream);
4427 void BlockChainStream_Destroy(BlockChainStream* This)
4429 HeapFree(GetProcessHeap(), 0, This);
4432 /******************************************************************************
4433 * BlockChainStream_GetHeadOfChain
4435 * Returns the head of this stream chain.
4436 * Some special chains don't have properties, their heads are kept in
4437 * This->headOfStreamPlaceHolder.
4440 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4442 StgProperty chainProperty;
4443 BOOL readSuccessful;
4445 if (This->headOfStreamPlaceHolder != 0)
4446 return *(This->headOfStreamPlaceHolder);
4448 if (This->ownerPropertyIndex != PROPERTY_NULL)
4450 readSuccessful = StorageImpl_ReadProperty(
4451 This->parentStorage,
4452 This->ownerPropertyIndex,
4457 return chainProperty.startingBlock;
4461 return BLOCK_END_OF_CHAIN;
4464 /******************************************************************************
4465 * BlockChainStream_GetCount
4467 * Returns the number of blocks that comprises this chain.
4468 * This is not the size of the stream as the last block may not be full!
4471 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
4476 blockIndex = BlockChainStream_GetHeadOfChain(This);
4478 while (blockIndex != BLOCK_END_OF_CHAIN)
4482 if(FAILED(StorageImpl_GetNextBlockInChain(
4483 This->parentStorage,
4492 /******************************************************************************
4493 * BlockChainStream_ReadAt
4495 * Reads a specified number of bytes from this chain at the specified offset.
4496 * bytesRead may be NULL.
4497 * Failure will be returned if the specified number of bytes has not been read.
4499 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
4500 ULARGE_INTEGER offset,
4505 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4506 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4507 ULONG bytesToReadInBuffer;
4511 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
4514 * Find the first block in the stream that contains part of the buffer.
4516 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4517 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4518 (blockNoInSequence < This->lastBlockNoInSequence) )
4520 blockIndex = BlockChainStream_GetHeadOfChain(This);
4521 This->lastBlockNoInSequence = blockNoInSequence;
4525 ULONG temp = blockNoInSequence;
4527 blockIndex = This->lastBlockNoInSequenceIndex;
4528 blockNoInSequence -= This->lastBlockNoInSequence;
4529 This->lastBlockNoInSequence = temp;
4532 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4534 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4535 return STG_E_DOCFILECORRUPT;
4536 blockNoInSequence--;
4539 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4540 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
4542 This->lastBlockNoInSequenceIndex = blockIndex;
4545 * Start reading the buffer.
4548 bufferWalker = buffer;
4550 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4552 ULARGE_INTEGER ulOffset;
4555 * Calculate how many bytes we can copy from this big block.
4557 bytesToReadInBuffer =
4558 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4560 TRACE("block %i\n",blockIndex);
4561 ulOffset.u.HighPart = 0;
4562 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4565 StorageImpl_ReadAt(This->parentStorage,
4568 bytesToReadInBuffer,
4571 * Step to the next big block.
4573 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4574 return STG_E_DOCFILECORRUPT;
4576 bufferWalker += bytesReadAt;
4577 size -= bytesReadAt;
4578 *bytesRead += bytesReadAt;
4579 offsetInBlock = 0; /* There is no offset on the next block */
4581 if (bytesToReadInBuffer != bytesReadAt)
4585 return (size == 0) ? S_OK : STG_E_READFAULT;
4588 /******************************************************************************
4589 * BlockChainStream_WriteAt
4591 * Writes the specified number of bytes to this chain at the specified offset.
4592 * bytesWritten may be NULL.
4593 * Will fail if not all specified number of bytes have been written.
4595 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
4596 ULARGE_INTEGER offset,
4599 ULONG* bytesWritten)
4601 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4602 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4605 const BYTE* bufferWalker;
4608 * Find the first block in the stream that contains part of the buffer.
4610 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4611 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4612 (blockNoInSequence < This->lastBlockNoInSequence) )
4614 blockIndex = BlockChainStream_GetHeadOfChain(This);
4615 This->lastBlockNoInSequence = blockNoInSequence;
4619 ULONG temp = blockNoInSequence;
4621 blockIndex = This->lastBlockNoInSequenceIndex;
4622 blockNoInSequence -= This->lastBlockNoInSequence;
4623 This->lastBlockNoInSequence = temp;
4626 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4628 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4630 return STG_E_DOCFILECORRUPT;
4631 blockNoInSequence--;
4634 This->lastBlockNoInSequenceIndex = blockIndex;
4636 /* BlockChainStream_SetSize should have already been called to ensure we have
4637 * enough blocks in the chain to write into */
4638 if (blockIndex == BLOCK_END_OF_CHAIN)
4640 ERR("not enough blocks in chain to write data\n");
4641 return STG_E_DOCFILECORRUPT;
4645 * Here, I'm casting away the constness on the buffer variable
4646 * This is OK since we don't intend to modify that buffer.
4649 bufferWalker = (const BYTE*)buffer;
4651 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4653 ULARGE_INTEGER ulOffset;
4654 DWORD bytesWrittenAt;
4656 * Calculate how many bytes we can copy from this big block.
4659 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4661 TRACE("block %i\n",blockIndex);
4662 ulOffset.u.HighPart = 0;
4663 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4666 StorageImpl_WriteAt(This->parentStorage,
4668 (BYTE*)bufferWalker,
4673 * Step to the next big block.
4675 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4677 return STG_E_DOCFILECORRUPT;
4679 bufferWalker += bytesWrittenAt;
4680 size -= bytesWrittenAt;
4681 *bytesWritten += bytesWrittenAt;
4682 offsetInBlock = 0; /* There is no offset on the next block */
4684 if (bytesWrittenAt != bytesToWrite)
4688 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
4691 /******************************************************************************
4692 * BlockChainStream_Shrink
4694 * Shrinks this chain in the big block depot.
4696 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
4697 ULARGE_INTEGER newSize)
4699 ULONG blockIndex, extraBlock;
4704 * Reset the last accessed block cache.
4706 This->lastBlockNoInSequence = 0xFFFFFFFF;
4707 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4710 * Figure out how many blocks are needed to contain the new size
4712 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4714 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4717 blockIndex = BlockChainStream_GetHeadOfChain(This);
4720 * Go to the new end of chain
4722 while (count < numBlocks)
4724 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4730 /* Get the next block before marking the new end */
4731 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4735 /* Mark the new end of chain */
4736 StorageImpl_SetNextBlockInChain(
4737 This->parentStorage,
4739 BLOCK_END_OF_CHAIN);
4741 This->tailIndex = blockIndex;
4742 This->numBlocks = numBlocks;
4745 * Mark the extra blocks as free
4747 while (extraBlock != BLOCK_END_OF_CHAIN)
4749 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4752 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4753 extraBlock = blockIndex;
4759 /******************************************************************************
4760 * BlockChainStream_Enlarge
4762 * Grows this chain in the big block depot.
4764 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4765 ULARGE_INTEGER newSize)
4767 ULONG blockIndex, currentBlock;
4769 ULONG oldNumBlocks = 0;
4771 blockIndex = BlockChainStream_GetHeadOfChain(This);
4774 * Empty chain. Create the head.
4776 if (blockIndex == BLOCK_END_OF_CHAIN)
4778 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4779 StorageImpl_SetNextBlockInChain(This->parentStorage,
4781 BLOCK_END_OF_CHAIN);
4783 if (This->headOfStreamPlaceHolder != 0)
4785 *(This->headOfStreamPlaceHolder) = blockIndex;
4789 StgProperty chainProp;
4790 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4792 StorageImpl_ReadProperty(
4793 This->parentStorage,
4794 This->ownerPropertyIndex,
4797 chainProp.startingBlock = blockIndex;
4799 StorageImpl_WriteProperty(
4800 This->parentStorage,
4801 This->ownerPropertyIndex,
4805 This->tailIndex = blockIndex;
4806 This->numBlocks = 1;
4810 * Figure out how many blocks are needed to contain this stream
4812 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4814 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4818 * Go to the current end of chain
4820 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4822 currentBlock = blockIndex;
4824 while (blockIndex != BLOCK_END_OF_CHAIN)
4827 currentBlock = blockIndex;
4829 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4834 This->tailIndex = currentBlock;
4837 currentBlock = This->tailIndex;
4838 oldNumBlocks = This->numBlocks;
4841 * Add new blocks to the chain
4843 if (oldNumBlocks < newNumBlocks)
4845 while (oldNumBlocks < newNumBlocks)
4847 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4849 StorageImpl_SetNextBlockInChain(
4850 This->parentStorage,
4854 StorageImpl_SetNextBlockInChain(
4855 This->parentStorage,
4857 BLOCK_END_OF_CHAIN);
4859 currentBlock = blockIndex;
4863 This->tailIndex = blockIndex;
4864 This->numBlocks = newNumBlocks;
4870 /******************************************************************************
4871 * BlockChainStream_SetSize
4873 * Sets the size of this stream. The big block depot will be updated.
4874 * The file will grow if we grow the chain.
4876 * TODO: Free the actual blocks in the file when we shrink the chain.
4877 * Currently, the blocks are still in the file. So the file size
4878 * doesn't shrink even if we shrink streams.
4880 BOOL BlockChainStream_SetSize(
4881 BlockChainStream* This,
4882 ULARGE_INTEGER newSize)
4884 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4886 if (newSize.u.LowPart == size.u.LowPart)
4889 if (newSize.u.LowPart < size.u.LowPart)
4891 BlockChainStream_Shrink(This, newSize);
4895 BlockChainStream_Enlarge(This, newSize);
4901 /******************************************************************************
4902 * BlockChainStream_GetSize
4904 * Returns the size of this chain.
4905 * Will return the block count if this chain doesn't have a property.
4907 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4909 StgProperty chainProperty;
4911 if(This->headOfStreamPlaceHolder == NULL)
4914 * This chain is a data stream read the property and return
4915 * the appropriate size
4917 StorageImpl_ReadProperty(
4918 This->parentStorage,
4919 This->ownerPropertyIndex,
4922 return chainProperty.size;
4927 * this chain is a chain that does not have a property, figure out the
4928 * size by making the product number of used blocks times the
4931 ULARGE_INTEGER result;
4932 result.u.HighPart = 0;
4935 BlockChainStream_GetCount(This) *
4936 This->parentStorage->bigBlockSize;
4942 /******************************************************************************
4943 ** SmallBlockChainStream implementation
4946 SmallBlockChainStream* SmallBlockChainStream_Construct(
4947 StorageImpl* parentStorage,
4948 ULONG propertyIndex)
4950 SmallBlockChainStream* newStream;
4952 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4954 newStream->parentStorage = parentStorage;
4955 newStream->ownerPropertyIndex = propertyIndex;
4960 void SmallBlockChainStream_Destroy(
4961 SmallBlockChainStream* This)
4963 HeapFree(GetProcessHeap(), 0, This);
4966 /******************************************************************************
4967 * SmallBlockChainStream_GetHeadOfChain
4969 * Returns the head of this chain of small blocks.
4971 static ULONG SmallBlockChainStream_GetHeadOfChain(
4972 SmallBlockChainStream* This)
4974 StgProperty chainProperty;
4975 BOOL readSuccessful;
4977 if (This->ownerPropertyIndex)
4979 readSuccessful = StorageImpl_ReadProperty(
4980 This->parentStorage,
4981 This->ownerPropertyIndex,
4986 return chainProperty.startingBlock;
4991 return BLOCK_END_OF_CHAIN;
4994 /******************************************************************************
4995 * SmallBlockChainStream_GetNextBlockInChain
4997 * Returns the index of the next small block in this chain.
5000 * - BLOCK_END_OF_CHAIN: end of this chain
5001 * - BLOCK_UNUSED: small block 'blockIndex' is free
5003 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
5004 SmallBlockChainStream* This,
5006 ULONG* nextBlockInChain)
5008 ULARGE_INTEGER offsetOfBlockInDepot;
5013 *nextBlockInChain = BLOCK_END_OF_CHAIN;
5015 offsetOfBlockInDepot.u.HighPart = 0;
5016 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5019 * Read those bytes in the buffer from the small block file.
5021 res = BlockChainStream_ReadAt(
5022 This->parentStorage->smallBlockDepotChain,
5023 offsetOfBlockInDepot,
5030 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
5037 /******************************************************************************
5038 * SmallBlockChainStream_SetNextBlockInChain
5040 * Writes the index of the next block of the specified block in the small
5042 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5043 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5045 static void SmallBlockChainStream_SetNextBlockInChain(
5046 SmallBlockChainStream* This,
5050 ULARGE_INTEGER offsetOfBlockInDepot;
5054 offsetOfBlockInDepot.u.HighPart = 0;
5055 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5057 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
5060 * Read those bytes in the buffer from the small block file.
5062 BlockChainStream_WriteAt(
5063 This->parentStorage->smallBlockDepotChain,
5064 offsetOfBlockInDepot,
5070 /******************************************************************************
5071 * SmallBlockChainStream_FreeBlock
5073 * Flag small block 'blockIndex' as free in the small block depot.
5075 static void SmallBlockChainStream_FreeBlock(
5076 SmallBlockChainStream* This,
5079 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5082 /******************************************************************************
5083 * SmallBlockChainStream_GetNextFreeBlock
5085 * Returns the index of a free small block. The small block depot will be
5086 * enlarged if necessary. The small block chain will also be enlarged if
5089 static ULONG SmallBlockChainStream_GetNextFreeBlock(
5090 SmallBlockChainStream* This)
5092 ULARGE_INTEGER offsetOfBlockInDepot;
5095 ULONG blockIndex = 0;
5096 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5098 ULONG smallBlocksPerBigBlock;
5100 offsetOfBlockInDepot.u.HighPart = 0;
5103 * Scan the small block depot for a free block
5105 while (nextBlockIndex != BLOCK_UNUSED)
5107 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5109 res = BlockChainStream_ReadAt(
5110 This->parentStorage->smallBlockDepotChain,
5111 offsetOfBlockInDepot,
5117 * If we run out of space for the small block depot, enlarge it
5121 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5123 if (nextBlockIndex != BLOCK_UNUSED)
5129 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5131 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5132 ULONG nextBlock, newsbdIndex;
5133 BYTE smallBlockDepot[BIG_BLOCK_SIZE];
5135 nextBlock = sbdIndex;
5136 while (nextBlock != BLOCK_END_OF_CHAIN)
5138 sbdIndex = nextBlock;
5139 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5142 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5143 if (sbdIndex != BLOCK_END_OF_CHAIN)
5144 StorageImpl_SetNextBlockInChain(
5145 This->parentStorage,
5149 StorageImpl_SetNextBlockInChain(
5150 This->parentStorage,
5152 BLOCK_END_OF_CHAIN);
5155 * Initialize all the small blocks to free
5157 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5158 StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
5163 * We have just created the small block depot.
5165 StgProperty rootProp;
5169 * Save it in the header
5171 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5172 StorageImpl_SaveFileHeader(This->parentStorage);
5175 * And allocate the first big block that will contain small blocks
5178 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5180 StorageImpl_SetNextBlockInChain(
5181 This->parentStorage,
5183 BLOCK_END_OF_CHAIN);
5185 StorageImpl_ReadProperty(
5186 This->parentStorage,
5187 This->parentStorage->base.rootPropertySetIndex,
5190 rootProp.startingBlock = sbStartIndex;
5191 rootProp.size.u.HighPart = 0;
5192 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
5194 StorageImpl_WriteProperty(
5195 This->parentStorage,
5196 This->parentStorage->base.rootPropertySetIndex,
5202 smallBlocksPerBigBlock =
5203 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5206 * Verify if we have to allocate big blocks to contain small blocks
5208 if (blockIndex % smallBlocksPerBigBlock == 0)
5210 StgProperty rootProp;
5211 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5213 StorageImpl_ReadProperty(
5214 This->parentStorage,
5215 This->parentStorage->base.rootPropertySetIndex,
5218 if (rootProp.size.u.LowPart <
5219 (blocksRequired * This->parentStorage->bigBlockSize))
5221 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5223 BlockChainStream_SetSize(
5224 This->parentStorage->smallBlockRootChain,
5227 StorageImpl_WriteProperty(
5228 This->parentStorage,
5229 This->parentStorage->base.rootPropertySetIndex,
5237 /******************************************************************************
5238 * SmallBlockChainStream_ReadAt
5240 * Reads a specified number of bytes from this chain at the specified offset.
5241 * bytesRead may be NULL.
5242 * Failure will be returned if the specified number of bytes has not been read.
5244 HRESULT SmallBlockChainStream_ReadAt(
5245 SmallBlockChainStream* This,
5246 ULARGE_INTEGER offset,
5252 ULARGE_INTEGER offsetInBigBlockFile;
5253 ULONG blockNoInSequence =
5254 offset.u.LowPart / This->parentStorage->smallBlockSize;
5256 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5257 ULONG bytesToReadInBuffer;
5259 ULONG bytesReadFromBigBlockFile;
5263 * This should never happen on a small block file.
5265 assert(offset.u.HighPart==0);
5268 * Find the first block in the stream that contains part of the buffer.
5270 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5272 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5274 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5277 blockNoInSequence--;
5281 * Start reading the buffer.
5284 bufferWalker = buffer;
5286 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5289 * Calculate how many bytes we can copy from this small block.
5291 bytesToReadInBuffer =
5292 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5295 * Calculate the offset of the small block in the small block file.
5297 offsetInBigBlockFile.u.HighPart = 0;
5298 offsetInBigBlockFile.u.LowPart =
5299 blockIndex * This->parentStorage->smallBlockSize;
5301 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5304 * Read those bytes in the buffer from the small block file.
5305 * The small block has already been identified so it shouldn't fail
5306 * unless the file is corrupt.
5308 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5309 offsetInBigBlockFile,
5310 bytesToReadInBuffer,
5312 &bytesReadFromBigBlockFile);
5318 * Step to the next big block.
5320 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5322 return STG_E_DOCFILECORRUPT;
5324 bufferWalker += bytesReadFromBigBlockFile;
5325 size -= bytesReadFromBigBlockFile;
5326 *bytesRead += bytesReadFromBigBlockFile;
5327 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5330 return (size == 0) ? S_OK : STG_E_READFAULT;
5333 /******************************************************************************
5334 * SmallBlockChainStream_WriteAt
5336 * Writes the specified number of bytes to this chain at the specified offset.
5337 * bytesWritten may be NULL.
5338 * Will fail if not all specified number of bytes have been written.
5340 HRESULT SmallBlockChainStream_WriteAt(
5341 SmallBlockChainStream* This,
5342 ULARGE_INTEGER offset,
5345 ULONG* bytesWritten)
5347 ULARGE_INTEGER offsetInBigBlockFile;
5348 ULONG blockNoInSequence =
5349 offset.u.LowPart / This->parentStorage->smallBlockSize;
5351 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5352 ULONG bytesToWriteInBuffer;
5354 ULONG bytesWrittenToBigBlockFile;
5355 const BYTE* bufferWalker;
5359 * This should never happen on a small block file.
5361 assert(offset.u.HighPart==0);
5364 * Find the first block in the stream that contains part of the buffer.
5366 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5368 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5370 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5371 return STG_E_DOCFILECORRUPT;
5372 blockNoInSequence--;
5376 * Start writing the buffer.
5378 * Here, I'm casting away the constness on the buffer variable
5379 * This is OK since we don't intend to modify that buffer.
5382 bufferWalker = (const BYTE*)buffer;
5383 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5386 * Calculate how many bytes we can copy to this small block.
5388 bytesToWriteInBuffer =
5389 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5392 * Calculate the offset of the small block in the small block file.
5394 offsetInBigBlockFile.u.HighPart = 0;
5395 offsetInBigBlockFile.u.LowPart =
5396 blockIndex * This->parentStorage->smallBlockSize;
5398 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5401 * Write those bytes in the buffer to the small block file.
5403 res = BlockChainStream_WriteAt(
5404 This->parentStorage->smallBlockRootChain,
5405 offsetInBigBlockFile,
5406 bytesToWriteInBuffer,
5408 &bytesWrittenToBigBlockFile);
5413 * Step to the next big block.
5415 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5418 bufferWalker += bytesWrittenToBigBlockFile;
5419 size -= bytesWrittenToBigBlockFile;
5420 *bytesWritten += bytesWrittenToBigBlockFile;
5421 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
5424 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5427 /******************************************************************************
5428 * SmallBlockChainStream_Shrink
5430 * Shrinks this chain in the small block depot.
5432 static BOOL SmallBlockChainStream_Shrink(
5433 SmallBlockChainStream* This,
5434 ULARGE_INTEGER newSize)
5436 ULONG blockIndex, extraBlock;
5440 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5442 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5445 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5448 * Go to the new end of chain
5450 while (count < numBlocks)
5452 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5459 * If the count is 0, we have a special case, the head of the chain was
5464 StgProperty chainProp;
5466 StorageImpl_ReadProperty(This->parentStorage,
5467 This->ownerPropertyIndex,
5470 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5472 StorageImpl_WriteProperty(This->parentStorage,
5473 This->ownerPropertyIndex,
5477 * We start freeing the chain at the head block.
5479 extraBlock = blockIndex;
5483 /* Get the next block before marking the new end */
5484 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5488 /* Mark the new end of chain */
5489 SmallBlockChainStream_SetNextBlockInChain(
5492 BLOCK_END_OF_CHAIN);
5496 * Mark the extra blocks as free
5498 while (extraBlock != BLOCK_END_OF_CHAIN)
5500 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5503 SmallBlockChainStream_FreeBlock(This, extraBlock);
5504 extraBlock = blockIndex;
5510 /******************************************************************************
5511 * SmallBlockChainStream_Enlarge
5513 * Grows this chain in the small block depot.
5515 static BOOL SmallBlockChainStream_Enlarge(
5516 SmallBlockChainStream* This,
5517 ULARGE_INTEGER newSize)
5519 ULONG blockIndex, currentBlock;
5521 ULONG oldNumBlocks = 0;
5523 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5528 if (blockIndex == BLOCK_END_OF_CHAIN)
5531 StgProperty chainProp;
5533 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5536 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5538 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5541 blockIndex = chainProp.startingBlock;
5542 SmallBlockChainStream_SetNextBlockInChain(
5545 BLOCK_END_OF_CHAIN);
5548 currentBlock = blockIndex;
5551 * Figure out how many blocks are needed to contain this stream
5553 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5555 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5559 * Go to the current end of chain
5561 while (blockIndex != BLOCK_END_OF_CHAIN)
5564 currentBlock = blockIndex;
5565 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5570 * Add new blocks to the chain
5572 while (oldNumBlocks < newNumBlocks)
5574 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5575 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5577 SmallBlockChainStream_SetNextBlockInChain(
5580 BLOCK_END_OF_CHAIN);
5582 currentBlock = blockIndex;
5589 /******************************************************************************
5590 * SmallBlockChainStream_SetSize
5592 * Sets the size of this stream.
5593 * The file will grow if we grow the chain.
5595 * TODO: Free the actual blocks in the file when we shrink the chain.
5596 * Currently, the blocks are still in the file. So the file size
5597 * doesn't shrink even if we shrink streams.
5599 BOOL SmallBlockChainStream_SetSize(
5600 SmallBlockChainStream* This,
5601 ULARGE_INTEGER newSize)
5603 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5605 if (newSize.u.LowPart == size.u.LowPart)
5608 if (newSize.u.LowPart < size.u.LowPart)
5610 SmallBlockChainStream_Shrink(This, newSize);
5614 SmallBlockChainStream_Enlarge(This, newSize);
5620 /******************************************************************************
5621 * SmallBlockChainStream_GetSize
5623 * Returns the size of this chain.
5625 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5627 StgProperty chainProperty;
5629 StorageImpl_ReadProperty(
5630 This->parentStorage,
5631 This->ownerPropertyIndex,
5634 return chainProperty.size;
5637 /******************************************************************************
5638 * StgCreateDocfile [OLE32.@]
5639 * Creates a new compound file storage object
5642 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5643 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5644 * reserved [ ?] unused?, usually 0
5645 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5648 * S_OK if the file was successfully created
5649 * some STG_E_ value if error
5651 * if pwcsName is NULL, create file with new unique name
5652 * the function can returns
5653 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5656 HRESULT WINAPI StgCreateDocfile(
5660 IStorage **ppstgOpen)
5662 StorageImpl* newStorage = 0;
5663 HANDLE hFile = INVALID_HANDLE_VALUE;
5664 HRESULT hr = STG_E_INVALIDFLAG;
5668 DWORD fileAttributes;
5669 WCHAR tempFileName[MAX_PATH];
5671 TRACE("(%s, %x, %d, %p)\n",
5672 debugstr_w(pwcsName), grfMode,
5673 reserved, ppstgOpen);
5676 * Validate the parameters
5679 return STG_E_INVALIDPOINTER;
5681 return STG_E_INVALIDPARAMETER;
5683 /* if no share mode given then DENY_NONE is the default */
5684 if (STGM_SHARE_MODE(grfMode) == 0)
5685 grfMode |= STGM_SHARE_DENY_NONE;
5688 * Validate the STGM flags
5690 if ( FAILED( validateSTGM(grfMode) ))
5693 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5694 switch(STGM_ACCESS_MODE(grfMode))
5697 case STGM_READWRITE:
5703 /* in direct mode, can only use SHARE_EXCLUSIVE */
5704 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5707 /* but in transacted mode, any share mode is valid */
5710 * Generate a unique name.
5714 WCHAR tempPath[MAX_PATH];
5715 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5717 memset(tempPath, 0, sizeof(tempPath));
5718 memset(tempFileName, 0, sizeof(tempFileName));
5720 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5723 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5724 pwcsName = tempFileName;
5727 hr = STG_E_INSUFFICIENTMEMORY;
5731 creationMode = TRUNCATE_EXISTING;
5735 creationMode = GetCreationModeFromSTGM(grfMode);
5739 * Interpret the STGM value grfMode
5741 shareMode = GetShareModeFromSTGM(grfMode);
5742 accessMode = GetAccessModeFromSTGM(grfMode);
5744 if (grfMode & STGM_DELETEONRELEASE)
5745 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5747 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5749 if (grfMode & STGM_TRANSACTED)
5750 FIXME("Transacted mode not implemented.\n");
5753 * Initialize the "out" parameter.
5757 hFile = CreateFileW(pwcsName,
5765 if (hFile == INVALID_HANDLE_VALUE)
5767 if(GetLastError() == ERROR_FILE_EXISTS)
5768 hr = STG_E_FILEALREADYEXISTS;
5775 * Allocate and initialize the new IStorage32object.
5777 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5779 if (newStorage == 0)
5781 hr = STG_E_INSUFFICIENTMEMORY;
5785 hr = StorageImpl_Construct(
5796 HeapFree(GetProcessHeap(), 0, newStorage);
5801 * Get an "out" pointer for the caller.
5803 hr = StorageBaseImpl_QueryInterface(
5804 (IStorage*)newStorage,
5805 (REFIID)&IID_IStorage,
5808 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
5813 /******************************************************************************
5814 * StgCreateStorageEx [OLE32.@]
5816 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5818 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5819 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5821 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5823 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5824 return STG_E_INVALIDPARAMETER;
5827 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5829 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5830 return STG_E_INVALIDPARAMETER;
5833 if (stgfmt == STGFMT_FILE)
5835 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5836 return STG_E_INVALIDPARAMETER;
5839 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5841 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5842 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5845 ERR("Invalid stgfmt argument\n");
5846 return STG_E_INVALIDPARAMETER;
5849 /******************************************************************************
5850 * StgCreatePropSetStg [OLE32.@]
5852 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5853 IPropertySetStorage **ppPropSetStg)
5857 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
5859 hr = STG_E_INVALIDPARAMETER;
5861 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5862 (void**)ppPropSetStg);
5866 /******************************************************************************
5867 * StgOpenStorageEx [OLE32.@]
5869 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5871 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5872 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5874 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5876 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5877 return STG_E_INVALIDPARAMETER;
5883 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5884 return STG_E_INVALIDPARAMETER;
5886 case STGFMT_STORAGE:
5889 case STGFMT_DOCFILE:
5890 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
5892 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5893 return STG_E_INVALIDPARAMETER;
5895 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5899 WARN("STGFMT_ANY assuming storage\n");
5903 return STG_E_INVALIDPARAMETER;
5906 return StgOpenStorage(pwcsName, NULL, grfMode, (SNB)NULL, 0, (IStorage **)ppObjectOpen);
5910 /******************************************************************************
5911 * StgOpenStorage [OLE32.@]
5913 HRESULT WINAPI StgOpenStorage(
5914 const OLECHAR *pwcsName,
5915 IStorage *pstgPriority,
5919 IStorage **ppstgOpen)
5921 StorageImpl* newStorage = 0;
5926 WCHAR fullname[MAX_PATH];
5928 TRACE("(%s, %p, %x, %p, %d, %p)\n",
5929 debugstr_w(pwcsName), pstgPriority, grfMode,
5930 snbExclude, reserved, ppstgOpen);
5933 * Perform sanity checks
5937 hr = STG_E_INVALIDNAME;
5943 hr = STG_E_INVALIDPOINTER;
5949 hr = STG_E_INVALIDPARAMETER;
5953 if (grfMode & STGM_PRIORITY)
5955 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
5956 return STG_E_INVALIDFLAG;
5957 if (grfMode & STGM_DELETEONRELEASE)
5958 return STG_E_INVALIDFUNCTION;
5959 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
5960 return STG_E_INVALIDFLAG;
5961 grfMode &= ~0xf0; /* remove the existing sharing mode */
5962 grfMode |= STGM_SHARE_DENY_NONE;
5964 /* STGM_PRIORITY stops other IStorage objects on the same file from
5965 * committing until the STGM_PRIORITY IStorage is closed. it also
5966 * stops non-transacted mode StgOpenStorage calls with write access from
5967 * succeeding. obviously, both of these cannot be achieved through just
5968 * file share flags */
5969 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5973 * Validate the sharing mode
5975 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
5976 switch(STGM_SHARE_MODE(grfMode))
5978 case STGM_SHARE_EXCLUSIVE:
5979 case STGM_SHARE_DENY_WRITE:
5982 hr = STG_E_INVALIDFLAG;
5987 * Validate the STGM flags
5989 if ( FAILED( validateSTGM(grfMode) ) ||
5990 (grfMode&STGM_CREATE))
5992 hr = STG_E_INVALIDFLAG;
5996 /* shared reading requires transacted mode */
5997 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5998 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
5999 !(grfMode&STGM_TRANSACTED) )
6001 hr = STG_E_INVALIDFLAG;
6006 * Interpret the STGM value grfMode
6008 shareMode = GetShareModeFromSTGM(grfMode);
6009 accessMode = GetAccessModeFromSTGM(grfMode);
6012 * Initialize the "out" parameter.
6016 hFile = CreateFileW( pwcsName,
6021 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
6024 if (hFile==INVALID_HANDLE_VALUE)
6026 DWORD last_error = GetLastError();
6032 case ERROR_FILE_NOT_FOUND:
6033 hr = STG_E_FILENOTFOUND;
6036 case ERROR_PATH_NOT_FOUND:
6037 hr = STG_E_PATHNOTFOUND;
6040 case ERROR_ACCESS_DENIED:
6041 case ERROR_WRITE_PROTECT:
6042 hr = STG_E_ACCESSDENIED;
6045 case ERROR_SHARING_VIOLATION:
6046 hr = STG_E_SHAREVIOLATION;
6057 * Refuse to open the file if it's too small to be a structured storage file
6058 * FIXME: verify the file when reading instead of here
6060 if (GetFileSize(hFile, NULL) < 0x100)
6063 hr = STG_E_FILEALREADYEXISTS;
6068 * Allocate and initialize the new IStorage32object.
6070 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6072 if (newStorage == 0)
6074 hr = STG_E_INSUFFICIENTMEMORY;
6078 /* Initialize the storage */
6079 hr = StorageImpl_Construct(
6090 HeapFree(GetProcessHeap(), 0, newStorage);
6092 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6094 if(hr == STG_E_INVALIDHEADER)
6095 hr = STG_E_FILEALREADYEXISTS;
6099 /* prepare the file name string given in lieu of the root property name */
6100 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
6101 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
6102 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
6105 * Get an "out" pointer for the caller.
6107 hr = StorageBaseImpl_QueryInterface(
6108 (IStorage*)newStorage,
6109 (REFIID)&IID_IStorage,
6113 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6117 /******************************************************************************
6118 * StgCreateDocfileOnILockBytes [OLE32.@]
6120 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6124 IStorage** ppstgOpen)
6126 StorageImpl* newStorage = 0;
6130 * Validate the parameters
6132 if ((ppstgOpen == 0) || (plkbyt == 0))
6133 return STG_E_INVALIDPOINTER;
6136 * Allocate and initialize the new IStorage object.
6138 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6140 if (newStorage == 0)
6141 return STG_E_INSUFFICIENTMEMORY;
6143 hr = StorageImpl_Construct(
6154 HeapFree(GetProcessHeap(), 0, newStorage);
6159 * Get an "out" pointer for the caller.
6161 hr = StorageBaseImpl_QueryInterface(
6162 (IStorage*)newStorage,
6163 (REFIID)&IID_IStorage,
6169 /******************************************************************************
6170 * StgOpenStorageOnILockBytes [OLE32.@]
6172 HRESULT WINAPI StgOpenStorageOnILockBytes(
6174 IStorage *pstgPriority,
6178 IStorage **ppstgOpen)
6180 StorageImpl* newStorage = 0;
6184 * Perform a sanity check
6186 if ((plkbyt == 0) || (ppstgOpen == 0))
6187 return STG_E_INVALIDPOINTER;
6190 * Validate the STGM flags
6192 if ( FAILED( validateSTGM(grfMode) ))
6193 return STG_E_INVALIDFLAG;
6196 * Initialize the "out" parameter.
6201 * Allocate and initialize the new IStorage object.
6203 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6205 if (newStorage == 0)
6206 return STG_E_INSUFFICIENTMEMORY;
6208 hr = StorageImpl_Construct(
6219 HeapFree(GetProcessHeap(), 0, newStorage);
6224 * Get an "out" pointer for the caller.
6226 hr = StorageBaseImpl_QueryInterface(
6227 (IStorage*)newStorage,
6228 (REFIID)&IID_IStorage,
6234 /******************************************************************************
6235 * StgSetTimes [ole32.@]
6236 * StgSetTimes [OLE32.@]
6240 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6241 FILETIME const *patime, FILETIME const *pmtime)
6243 IStorage *stg = NULL;
6246 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6248 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6252 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6253 IStorage_Release(stg);
6259 /******************************************************************************
6260 * StgIsStorageILockBytes [OLE32.@]
6262 * Determines if the ILockBytes contains a storage object.
6264 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6267 ULARGE_INTEGER offset;
6269 offset.u.HighPart = 0;
6270 offset.u.LowPart = 0;
6272 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6274 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6280 /******************************************************************************
6281 * WriteClassStg [OLE32.@]
6283 * This method will store the specified CLSID in the specified storage object
6285 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6290 return E_INVALIDARG;
6292 hRes = IStorage_SetClass(pStg, rclsid);
6297 /***********************************************************************
6298 * ReadClassStg (OLE32.@)
6300 * This method reads the CLSID previously written to a storage object with
6301 * the WriteClassStg.
6304 * pstg [I] IStorage pointer
6305 * pclsid [O] Pointer to where the CLSID is written
6309 * Failure: HRESULT code.
6311 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6316 TRACE("(%p, %p)\n", pstg, pclsid);
6318 if(!pstg || !pclsid)
6319 return E_INVALIDARG;
6322 * read a STATSTG structure (contains the clsid) from the storage
6324 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
6327 *pclsid=pstatstg.clsid;
6332 /***********************************************************************
6333 * OleLoadFromStream (OLE32.@)
6335 * This function loads an object from stream
6337 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6341 LPPERSISTSTREAM xstm;
6343 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6345 res=ReadClassStm(pStm,&clsid);
6346 if (!SUCCEEDED(res))
6348 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6349 if (!SUCCEEDED(res))
6351 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6352 if (!SUCCEEDED(res)) {
6353 IUnknown_Release((IUnknown*)*ppvObj);
6356 res=IPersistStream_Load(xstm,pStm);
6357 IPersistStream_Release(xstm);
6358 /* FIXME: all refcounts ok at this point? I think they should be:
6361 * xstm : 0 (released)
6366 /***********************************************************************
6367 * OleSaveToStream (OLE32.@)
6369 * This function saves an object with the IPersistStream interface on it
6370 * to the specified stream.
6372 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6378 TRACE("(%p,%p)\n",pPStm,pStm);
6380 res=IPersistStream_GetClassID(pPStm,&clsid);
6382 if (SUCCEEDED(res)){
6384 res=WriteClassStm(pStm,&clsid);
6388 res=IPersistStream_Save(pPStm,pStm,TRUE);
6391 TRACE("Finished Save\n");
6395 /****************************************************************************
6396 * This method validate a STGM parameter that can contain the values below
6398 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6399 * The stgm values contained in 0xffff0000 are bitmasks.
6401 * STGM_DIRECT 0x00000000
6402 * STGM_TRANSACTED 0x00010000
6403 * STGM_SIMPLE 0x08000000
6405 * STGM_READ 0x00000000
6406 * STGM_WRITE 0x00000001
6407 * STGM_READWRITE 0x00000002
6409 * STGM_SHARE_DENY_NONE 0x00000040
6410 * STGM_SHARE_DENY_READ 0x00000030
6411 * STGM_SHARE_DENY_WRITE 0x00000020
6412 * STGM_SHARE_EXCLUSIVE 0x00000010
6414 * STGM_PRIORITY 0x00040000
6415 * STGM_DELETEONRELEASE 0x04000000
6417 * STGM_CREATE 0x00001000
6418 * STGM_CONVERT 0x00020000
6419 * STGM_FAILIFTHERE 0x00000000
6421 * STGM_NOSCRATCH 0x00100000
6422 * STGM_NOSNAPSHOT 0x00200000
6424 static HRESULT validateSTGM(DWORD stgm)
6426 DWORD access = STGM_ACCESS_MODE(stgm);
6427 DWORD share = STGM_SHARE_MODE(stgm);
6428 DWORD create = STGM_CREATE_MODE(stgm);
6430 if (stgm&~STGM_KNOWN_FLAGS)
6432 ERR("unknown flags %08x\n", stgm);
6440 case STGM_READWRITE:
6448 case STGM_SHARE_DENY_NONE:
6449 case STGM_SHARE_DENY_READ:
6450 case STGM_SHARE_DENY_WRITE:
6451 case STGM_SHARE_EXCLUSIVE:
6460 case STGM_FAILIFTHERE:
6467 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6469 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6473 * STGM_CREATE | STGM_CONVERT
6474 * if both are false, STGM_FAILIFTHERE is set to TRUE
6476 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6480 * STGM_NOSCRATCH requires STGM_TRANSACTED
6482 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6486 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6487 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6489 if ( (stgm & STGM_NOSNAPSHOT) &&
6490 (!(stgm & STGM_TRANSACTED) ||
6491 share == STGM_SHARE_EXCLUSIVE ||
6492 share == STGM_SHARE_DENY_WRITE) )
6498 /****************************************************************************
6499 * GetShareModeFromSTGM
6501 * This method will return a share mode flag from a STGM value.
6502 * The STGM value is assumed valid.
6504 static DWORD GetShareModeFromSTGM(DWORD stgm)
6506 switch (STGM_SHARE_MODE(stgm))
6508 case STGM_SHARE_DENY_NONE:
6509 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6510 case STGM_SHARE_DENY_READ:
6511 return FILE_SHARE_WRITE;
6512 case STGM_SHARE_DENY_WRITE:
6513 return FILE_SHARE_READ;
6514 case STGM_SHARE_EXCLUSIVE:
6517 ERR("Invalid share mode!\n");
6522 /****************************************************************************
6523 * GetAccessModeFromSTGM
6525 * This method will return an access mode flag from a STGM value.
6526 * The STGM value is assumed valid.
6528 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6530 switch (STGM_ACCESS_MODE(stgm))
6533 return GENERIC_READ;
6535 case STGM_READWRITE:
6536 return GENERIC_READ | GENERIC_WRITE;
6538 ERR("Invalid access mode!\n");
6543 /****************************************************************************
6544 * GetCreationModeFromSTGM
6546 * This method will return a creation mode flag from a STGM value.
6547 * The STGM value is assumed valid.
6549 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6551 switch(STGM_CREATE_MODE(stgm))
6554 return CREATE_ALWAYS;
6556 FIXME("STGM_CONVERT not implemented!\n");
6558 case STGM_FAILIFTHERE:
6561 ERR("Invalid create mode!\n");
6567 /*************************************************************************
6568 * OLECONVERT_LoadOLE10 [Internal]
6570 * Loads the OLE10 STREAM to memory
6573 * pOleStream [I] The OLESTREAM
6574 * pData [I] Data Structure for the OLESTREAM Data
6578 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6579 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6582 * This function is used by OleConvertOLESTREAMToIStorage only.
6584 * Memory allocated for pData must be freed by the caller
6586 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6589 HRESULT hRes = S_OK;
6593 pData->pData = NULL;
6594 pData->pstrOleObjFileName = (CHAR *) NULL;
6596 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6599 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6600 if(dwSize != sizeof(pData->dwOleID))
6602 hRes = CONVERT10_E_OLESTREAM_GET;
6604 else if(pData->dwOleID != OLESTREAM_ID)
6606 hRes = CONVERT10_E_OLESTREAM_FMT;
6617 /* Get the TypeID...more info needed for this field */
6618 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6619 if(dwSize != sizeof(pData->dwTypeID))
6621 hRes = CONVERT10_E_OLESTREAM_GET;
6626 if(pData->dwTypeID != 0)
6628 /* Get the length of the OleTypeName */
6629 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6630 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6632 hRes = CONVERT10_E_OLESTREAM_GET;
6637 if(pData->dwOleTypeNameLength > 0)
6639 /* Get the OleTypeName */
6640 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6641 if(dwSize != pData->dwOleTypeNameLength)
6643 hRes = CONVERT10_E_OLESTREAM_GET;
6649 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6650 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6652 hRes = CONVERT10_E_OLESTREAM_GET;
6656 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6657 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6658 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6659 if(pData->pstrOleObjFileName)
6661 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6662 if(dwSize != pData->dwOleObjFileNameLength)
6664 hRes = CONVERT10_E_OLESTREAM_GET;
6668 hRes = CONVERT10_E_OLESTREAM_GET;
6673 /* Get the Width of the Metafile */
6674 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6675 if(dwSize != sizeof(pData->dwMetaFileWidth))
6677 hRes = CONVERT10_E_OLESTREAM_GET;
6681 /* Get the Height of the Metafile */
6682 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6683 if(dwSize != sizeof(pData->dwMetaFileHeight))
6685 hRes = CONVERT10_E_OLESTREAM_GET;
6691 /* Get the Length of the Data */
6692 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6693 if(dwSize != sizeof(pData->dwDataLength))
6695 hRes = CONVERT10_E_OLESTREAM_GET;
6699 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6701 if(!bStrem1) /* if it is a second OLE stream data */
6703 pData->dwDataLength -= 8;
6704 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6705 if(dwSize != sizeof(pData->strUnknown))
6707 hRes = CONVERT10_E_OLESTREAM_GET;
6713 if(pData->dwDataLength > 0)
6715 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6717 /* Get Data (ex. IStorage, Metafile, or BMP) */
6720 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6721 if(dwSize != pData->dwDataLength)
6723 hRes = CONVERT10_E_OLESTREAM_GET;
6728 hRes = CONVERT10_E_OLESTREAM_GET;
6737 /*************************************************************************
6738 * OLECONVERT_SaveOLE10 [Internal]
6740 * Saves the OLE10 STREAM From memory
6743 * pData [I] Data Structure for the OLESTREAM Data
6744 * pOleStream [I] The OLESTREAM to save
6748 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6751 * This function is used by OleConvertIStorageToOLESTREAM only.
6754 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6757 HRESULT hRes = S_OK;
6761 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6762 if(dwSize != sizeof(pData->dwOleID))
6764 hRes = CONVERT10_E_OLESTREAM_PUT;
6769 /* Set the TypeID */
6770 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6771 if(dwSize != sizeof(pData->dwTypeID))
6773 hRes = CONVERT10_E_OLESTREAM_PUT;
6777 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6779 /* Set the Length of the OleTypeName */
6780 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6781 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6783 hRes = CONVERT10_E_OLESTREAM_PUT;
6788 if(pData->dwOleTypeNameLength > 0)
6790 /* Set the OleTypeName */
6791 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6792 if(dwSize != pData->dwOleTypeNameLength)
6794 hRes = CONVERT10_E_OLESTREAM_PUT;
6801 /* Set the width of the Metafile */
6802 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6803 if(dwSize != sizeof(pData->dwMetaFileWidth))
6805 hRes = CONVERT10_E_OLESTREAM_PUT;
6811 /* Set the height of the Metafile */
6812 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6813 if(dwSize != sizeof(pData->dwMetaFileHeight))
6815 hRes = CONVERT10_E_OLESTREAM_PUT;
6821 /* Set the length of the Data */
6822 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6823 if(dwSize != sizeof(pData->dwDataLength))
6825 hRes = CONVERT10_E_OLESTREAM_PUT;
6831 if(pData->dwDataLength > 0)
6833 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6834 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6835 if(dwSize != pData->dwDataLength)
6837 hRes = CONVERT10_E_OLESTREAM_PUT;
6845 /*************************************************************************
6846 * OLECONVERT_GetOLE20FromOLE10[Internal]
6848 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6849 * opens it, and copies the content to the dest IStorage for
6850 * OleConvertOLESTREAMToIStorage
6854 * pDestStorage [I] The IStorage to copy the data to
6855 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6856 * nBufferLength [I] The size of the buffer
6865 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6869 IStorage *pTempStorage;
6870 DWORD dwNumOfBytesWritten;
6871 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6872 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6874 /* Create a temp File */
6875 GetTempPathW(MAX_PATH, wstrTempDir);
6876 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6877 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6879 if(hFile != INVALID_HANDLE_VALUE)
6881 /* Write IStorage Data to File */
6882 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6885 /* Open and copy temp storage to the Dest Storage */
6886 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6889 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6890 StorageBaseImpl_Release(pTempStorage);
6892 DeleteFileW(wstrTempFile);
6897 /*************************************************************************
6898 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6900 * Saves the OLE10 STREAM From memory
6903 * pStorage [I] The Src IStorage to copy
6904 * pData [I] The Dest Memory to write to.
6907 * The size in bytes allocated for pData
6910 * Memory allocated for pData must be freed by the caller
6912 * Used by OleConvertIStorageToOLESTREAM only.
6915 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6919 DWORD nDataLength = 0;
6920 IStorage *pTempStorage;
6921 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6922 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6926 /* Create temp Storage */
6927 GetTempPathW(MAX_PATH, wstrTempDir);
6928 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6929 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6933 /* Copy Src Storage to the Temp Storage */
6934 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6935 StorageBaseImpl_Release(pTempStorage);
6937 /* Open Temp Storage as a file and copy to memory */
6938 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6939 if(hFile != INVALID_HANDLE_VALUE)
6941 nDataLength = GetFileSize(hFile, NULL);
6942 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6943 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6946 DeleteFileW(wstrTempFile);
6951 /*************************************************************************
6952 * OLECONVERT_CreateOleStream [Internal]
6954 * Creates the "\001OLE" stream in the IStorage if necessary.
6957 * pStorage [I] Dest storage to create the stream in
6963 * This function is used by OleConvertOLESTREAMToIStorage only.
6965 * This stream is still unknown, MS Word seems to have extra data
6966 * but since the data is stored in the OLESTREAM there should be
6967 * no need to recreate the stream. If the stream is manually
6968 * deleted it will create it with this default data.
6971 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6975 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6976 BYTE pOleStreamHeader [] =
6978 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6979 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6980 0x00, 0x00, 0x00, 0x00
6983 /* Create stream if not present */
6984 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6985 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6989 /* Write default Data */
6990 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6991 IStream_Release(pStream);
6995 /* write a string to a stream, preceded by its length */
6996 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
7003 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
7004 r = IStream_Write( stm, &len, sizeof(len), NULL);
7009 str = CoTaskMemAlloc( len );
7010 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
7011 r = IStream_Write( stm, str, len, NULL);
7012 CoTaskMemFree( str );
7016 /* read a string preceded by its length from a stream */
7017 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
7020 DWORD len, count = 0;
7024 r = IStream_Read( stm, &len, sizeof(len), &count );
7027 if( count != sizeof(len) )
7028 return E_OUTOFMEMORY;
7030 TRACE("%d bytes\n",len);
7032 str = CoTaskMemAlloc( len );
7034 return E_OUTOFMEMORY;
7036 r = IStream_Read( stm, str, len, &count );
7041 CoTaskMemFree( str );
7042 return E_OUTOFMEMORY;
7045 TRACE("Read string %s\n",debugstr_an(str,len));
7047 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7048 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7050 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7051 CoTaskMemFree( str );
7059 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7060 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7064 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7066 static const BYTE unknown1[12] =
7067 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7068 0xFF, 0xFF, 0xFF, 0xFF};
7069 static const BYTE unknown2[16] =
7070 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7071 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7073 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7074 debugstr_w(lpszUserType), debugstr_w(szClipName),
7075 debugstr_w(szProgIDName));
7077 /* Create a CompObj stream if it doesn't exist */
7078 r = IStorage_CreateStream(pstg, szwStreamName,
7079 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7083 /* Write CompObj Structure to stream */
7084 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7086 if( SUCCEEDED( r ) )
7087 r = WriteClassStm( pstm, clsid );
7089 if( SUCCEEDED( r ) )
7090 r = STREAM_WriteString( pstm, lpszUserType );
7091 if( SUCCEEDED( r ) )
7092 r = STREAM_WriteString( pstm, szClipName );
7093 if( SUCCEEDED( r ) )
7094 r = STREAM_WriteString( pstm, szProgIDName );
7095 if( SUCCEEDED( r ) )
7096 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7098 IStream_Release( pstm );
7103 /***********************************************************************
7104 * WriteFmtUserTypeStg (OLE32.@)
7106 HRESULT WINAPI WriteFmtUserTypeStg(
7107 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7110 WCHAR szwClipName[0x40];
7111 CLSID clsid = CLSID_NULL;
7112 LPWSTR wstrProgID = NULL;
7115 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7117 /* get the clipboard format name */
7118 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
7121 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7123 /* FIXME: There's room to save a CLSID and its ProgID, but
7124 the CLSID is not looked up in the registry and in all the
7125 tests I wrote it was CLSID_NULL. Where does it come from?
7128 /* get the real program ID. This may fail, but that's fine */
7129 ProgIDFromCLSID(&clsid, &wstrProgID);
7131 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7133 r = STORAGE_WriteCompObj( pstg, &clsid,
7134 lpszUserType, szwClipName, wstrProgID );
7136 CoTaskMemFree(wstrProgID);
7142 /******************************************************************************
7143 * ReadFmtUserTypeStg [OLE32.@]
7145 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7149 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7150 unsigned char unknown1[12];
7151 unsigned char unknown2[16];
7153 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7156 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7158 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7159 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7162 WARN("Failed to open stream r = %08x\n", r);
7166 /* read the various parts of the structure */
7167 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7168 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7170 r = ReadClassStm( stm, &clsid );
7174 r = STREAM_ReadString( stm, &szCLSIDName );
7178 r = STREAM_ReadString( stm, &szOleTypeName );
7182 r = STREAM_ReadString( stm, &szProgIDName );
7186 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7187 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7190 /* ok, success... now we just need to store what we found */
7192 *pcf = RegisterClipboardFormatW( szOleTypeName );
7193 CoTaskMemFree( szOleTypeName );
7195 if( lplpszUserType )
7196 *lplpszUserType = szCLSIDName;
7197 CoTaskMemFree( szProgIDName );
7200 IStream_Release( stm );
7206 /*************************************************************************
7207 * OLECONVERT_CreateCompObjStream [Internal]
7209 * Creates a "\001CompObj" is the destination IStorage if necessary.
7212 * pStorage [I] The dest IStorage to create the CompObj Stream
7214 * strOleTypeName [I] The ProgID
7218 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7221 * This function is used by OleConvertOLESTREAMToIStorage only.
7223 * The stream data is stored in the OLESTREAM and there should be
7224 * no need to recreate the stream. If the stream is manually
7225 * deleted it will attempt to create it by querying the registry.
7229 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7232 HRESULT hStorageRes, hRes = S_OK;
7233 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7234 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7235 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7237 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7238 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7240 /* Initialize the CompObj structure */
7241 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7242 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
7243 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
7246 /* Create a CompObj stream if it doesn't exist */
7247 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7248 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7249 if(hStorageRes == S_OK)
7251 /* copy the OleTypeName to the compobj struct */
7252 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7253 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7255 /* copy the OleTypeName to the compobj struct */
7256 /* Note: in the test made, these were Identical */
7257 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7258 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7261 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7262 bufferW, OLESTREAM_MAX_STR_LEN );
7263 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7269 /* Get the CLSID Default Name from the Registry */
7270 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7271 if(hErr == ERROR_SUCCESS)
7273 char strTemp[OLESTREAM_MAX_STR_LEN];
7274 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7275 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7276 if(hErr == ERROR_SUCCESS)
7278 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7284 /* Write CompObj Structure to stream */
7285 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7287 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7289 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7290 if(IStorageCompObj.dwCLSIDNameLength > 0)
7292 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7294 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7295 if(IStorageCompObj.dwOleTypeNameLength > 0)
7297 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7299 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7300 if(IStorageCompObj.dwProgIDNameLength > 0)
7302 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7304 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7305 IStream_Release(pStream);
7311 /*************************************************************************
7312 * OLECONVERT_CreateOlePresStream[Internal]
7314 * Creates the "\002OlePres000" Stream with the Metafile data
7317 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7318 * dwExtentX [I] Width of the Metafile
7319 * dwExtentY [I] Height of the Metafile
7320 * pData [I] Metafile data
7321 * dwDataLength [I] Size of the Metafile data
7325 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7328 * This function is used by OleConvertOLESTREAMToIStorage only.
7331 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7335 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7336 BYTE pOlePresStreamHeader [] =
7338 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7339 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7340 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7341 0x00, 0x00, 0x00, 0x00
7344 BYTE pOlePresStreamHeaderEmpty [] =
7346 0x00, 0x00, 0x00, 0x00,
7347 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7348 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7349 0x00, 0x00, 0x00, 0x00
7352 /* Create the OlePres000 Stream */
7353 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7354 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7359 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7361 memset(&OlePres, 0, sizeof(OlePres));
7362 /* Do we have any metafile data to save */
7363 if(dwDataLength > 0)
7365 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7366 nHeaderSize = sizeof(pOlePresStreamHeader);
7370 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7371 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7373 /* Set width and height of the metafile */
7374 OlePres.dwExtentX = dwExtentX;
7375 OlePres.dwExtentY = -dwExtentY;
7377 /* Set Data and Length */
7378 if(dwDataLength > sizeof(METAFILEPICT16))
7380 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7381 OlePres.pData = &(pData[8]);
7383 /* Save OlePres000 Data to Stream */
7384 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7385 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7386 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7387 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7388 if(OlePres.dwSize > 0)
7390 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7392 IStream_Release(pStream);
7396 /*************************************************************************
7397 * OLECONVERT_CreateOle10NativeStream [Internal]
7399 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7402 * pStorage [I] Dest storage to create the stream in
7403 * pData [I] Ole10 Native Data (ex. bmp)
7404 * dwDataLength [I] Size of the Ole10 Native Data
7410 * This function is used by OleConvertOLESTREAMToIStorage only.
7412 * Might need to verify the data and return appropriate error message
7415 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7419 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7421 /* Create the Ole10Native Stream */
7422 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7423 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7427 /* Write info to stream */
7428 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7429 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7430 IStream_Release(pStream);
7435 /*************************************************************************
7436 * OLECONVERT_GetOLE10ProgID [Internal]
7438 * Finds the ProgID (or OleTypeID) from the IStorage
7441 * pStorage [I] The Src IStorage to get the ProgID
7442 * strProgID [I] the ProgID string to get
7443 * dwSize [I] the size of the string
7447 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7450 * This function is used by OleConvertIStorageToOLESTREAM only.
7454 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7458 LARGE_INTEGER iSeekPos;
7459 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7460 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7462 /* Open the CompObj Stream */
7463 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7464 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7468 /*Get the OleType from the CompObj Stream */
7469 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7470 iSeekPos.u.HighPart = 0;
7472 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7473 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7474 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7475 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7476 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7477 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7478 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7480 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7483 IStream_Read(pStream, strProgID, *dwSize, NULL);
7485 IStream_Release(pStream);
7490 LPOLESTR wstrProgID;
7492 /* Get the OleType from the registry */
7493 REFCLSID clsid = &(stat.clsid);
7494 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7495 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7498 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7505 /*************************************************************************
7506 * OLECONVERT_GetOle10PresData [Internal]
7508 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7511 * pStorage [I] Src IStroage
7512 * pOleStream [I] Dest OleStream Mem Struct
7518 * This function is used by OleConvertIStorageToOLESTREAM only.
7520 * Memory allocated for pData must be freed by the caller
7524 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7529 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7531 /* Initialize Default data for OLESTREAM */
7532 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7533 pOleStreamData[0].dwTypeID = 2;
7534 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7535 pOleStreamData[1].dwTypeID = 0;
7536 pOleStreamData[0].dwMetaFileWidth = 0;
7537 pOleStreamData[0].dwMetaFileHeight = 0;
7538 pOleStreamData[0].pData = NULL;
7539 pOleStreamData[1].pData = NULL;
7541 /* Open Ole10Native Stream */
7542 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7543 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7547 /* Read Size and Data */
7548 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7549 if(pOleStreamData->dwDataLength > 0)
7551 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7552 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7554 IStream_Release(pStream);
7560 /*************************************************************************
7561 * OLECONVERT_GetOle20PresData[Internal]
7563 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7566 * pStorage [I] Src IStroage
7567 * pOleStreamData [I] Dest OleStream Mem Struct
7573 * This function is used by OleConvertIStorageToOLESTREAM only.
7575 * Memory allocated for pData must be freed by the caller
7577 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7581 OLECONVERT_ISTORAGE_OLEPRES olePress;
7582 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7584 /* Initialize Default data for OLESTREAM */
7585 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7586 pOleStreamData[0].dwTypeID = 2;
7587 pOleStreamData[0].dwMetaFileWidth = 0;
7588 pOleStreamData[0].dwMetaFileHeight = 0;
7589 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7590 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7591 pOleStreamData[1].dwTypeID = 0;
7592 pOleStreamData[1].dwOleTypeNameLength = 0;
7593 pOleStreamData[1].strOleTypeName[0] = 0;
7594 pOleStreamData[1].dwMetaFileWidth = 0;
7595 pOleStreamData[1].dwMetaFileHeight = 0;
7596 pOleStreamData[1].pData = NULL;
7597 pOleStreamData[1].dwDataLength = 0;
7600 /* Open OlePress000 stream */
7601 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7602 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7605 LARGE_INTEGER iSeekPos;
7606 METAFILEPICT16 MetaFilePict;
7607 static const char strMetafilePictName[] = "METAFILEPICT";
7609 /* Set the TypeID for a Metafile */
7610 pOleStreamData[1].dwTypeID = 5;
7612 /* Set the OleTypeName to Metafile */
7613 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7614 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7616 iSeekPos.u.HighPart = 0;
7617 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7619 /* Get Presentation Data */
7620 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7621 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7622 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7623 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7625 /*Set width and Height */
7626 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7627 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7628 if(olePress.dwSize > 0)
7631 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7633 /* Set MetaFilePict struct */
7634 MetaFilePict.mm = 8;
7635 MetaFilePict.xExt = olePress.dwExtentX;
7636 MetaFilePict.yExt = olePress.dwExtentY;
7637 MetaFilePict.hMF = 0;
7639 /* Get Metafile Data */
7640 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7641 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7642 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7644 IStream_Release(pStream);
7648 /*************************************************************************
7649 * OleConvertOLESTREAMToIStorage [OLE32.@]
7654 * DVTARGETDEVICE paramenter is not handled
7655 * Still unsure of some mem fields for OLE 10 Stream
7656 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7657 * and "\001OLE" streams
7660 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7661 LPOLESTREAM pOleStream,
7663 const DVTARGETDEVICE* ptd)
7667 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7669 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
7671 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7675 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7678 if(pstg == NULL || pOleStream == NULL)
7680 hRes = E_INVALIDARG;
7685 /* Load the OLESTREAM to Memory */
7686 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7691 /* Load the OLESTREAM to Memory (part 2)*/
7692 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7698 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7700 /* Do we have the IStorage Data in the OLESTREAM */
7701 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7703 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7704 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7708 /* It must be an original OLE 1.0 source */
7709 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7714 /* It must be an original OLE 1.0 source */
7715 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7718 /* Create CompObj Stream if necessary */
7719 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7722 /*Create the Ole Stream if necessary */
7723 OLECONVERT_CreateOleStream(pstg);
7728 /* Free allocated memory */
7729 for(i=0; i < 2; i++)
7731 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7732 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7733 pOleStreamData[i].pstrOleObjFileName = NULL;
7738 /*************************************************************************
7739 * OleConvertIStorageToOLESTREAM [OLE32.@]
7746 * Still unsure of some mem fields for OLE 10 Stream
7747 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7748 * and "\001OLE" streams.
7751 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7753 LPOLESTREAM pOleStream)
7756 HRESULT hRes = S_OK;
7758 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7759 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7761 TRACE("%p %p\n", pstg, pOleStream);
7763 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7765 if(pstg == NULL || pOleStream == NULL)
7767 hRes = E_INVALIDARG;
7771 /* Get the ProgID */
7772 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7773 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7777 /* Was it originally Ole10 */
7778 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7781 IStream_Release(pStream);
7782 /* Get Presentation Data for Ole10Native */
7783 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7787 /* Get Presentation Data (OLE20) */
7788 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7791 /* Save OLESTREAM */
7792 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7795 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7800 /* Free allocated memory */
7801 for(i=0; i < 2; i++)
7803 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7809 /***********************************************************************
7810 * GetConvertStg (OLE32.@)
7812 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7813 FIXME("unimplemented stub!\n");
7817 /******************************************************************************
7818 * StgIsStorageFile [OLE32.@]
7819 * Verify if the file contains a storage object
7825 * S_OK if file has magic bytes as a storage object
7826 * S_FALSE if file is not storage
7829 StgIsStorageFile(LPCOLESTR fn)
7835 TRACE("%s\n", debugstr_w(fn));
7836 hf = CreateFileW(fn, GENERIC_READ,
7837 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7838 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7840 if (hf == INVALID_HANDLE_VALUE)
7841 return STG_E_FILENOTFOUND;
7843 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7845 WARN(" unable to read file\n");
7852 if (bytes_read != 8) {
7853 WARN(" too short\n");
7857 if (!memcmp(magic,STORAGE_magic,8)) {
7862 WARN(" -> Invalid header.\n");
7866 /***********************************************************************
7867 * WriteClassStm (OLE32.@)
7869 * Writes a CLSID to a stream.
7872 * pStm [I] Stream to write to.
7873 * rclsid [I] CLSID to write.
7877 * Failure: HRESULT code.
7879 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
7881 TRACE("(%p,%p)\n",pStm,rclsid);
7883 if (!pStm || !rclsid)
7884 return E_INVALIDARG;
7886 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
7889 /***********************************************************************
7890 * ReadClassStm (OLE32.@)
7892 * Reads a CLSID from a stream.
7895 * pStm [I] Stream to read from.
7896 * rclsid [O] CLSID to read.
7900 * Failure: HRESULT code.
7902 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
7907 TRACE("(%p,%p)\n",pStm,pclsid);
7909 if (!pStm || !pclsid)
7910 return E_INVALIDARG;
7912 /* clear the output args */
7913 memcpy(pclsid, &CLSID_NULL, sizeof(*pclsid));
7915 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
7920 if (nbByte != sizeof(CLSID))
7921 return STG_E_READFAULT;