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, const 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;
756 pstatstg->grfStateBits = This->stateBits;
767 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);
769 TRACE("<-- %08x\n", res);
773 /************************************************************************
774 * Storage32BaseImpl_RenameElement (IStorage)
776 * This method will rename the specified element.
778 * See Windows documentation for more details on IStorage methods.
780 * Implementation notes: The method used to rename consists of creating a clone
781 * of the deleted StgProperty object setting it with the new name and to
782 * perform a DestroyElement of the old StgProperty.
784 static HRESULT WINAPI StorageBaseImpl_RenameElement(
786 const OLECHAR* pwcsOldName, /* [in] */
787 const OLECHAR* pwcsNewName) /* [in] */
789 StorageBaseImpl *This = (StorageBaseImpl *)iface;
790 IEnumSTATSTGImpl* propertyEnumeration;
791 StgProperty currentProperty;
792 ULONG foundPropertyIndex;
794 TRACE("(%p, %s, %s)\n",
795 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
798 * Create a property enumeration to search the properties
800 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
801 This->rootPropertySetIndex);
804 * Search the enumeration for the new property name
806 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
810 if (foundPropertyIndex != PROPERTY_NULL)
813 * There is already a property with the new name
815 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
816 return STG_E_FILEALREADYEXISTS;
819 IEnumSTATSTG_Reset((IEnumSTATSTG*)propertyEnumeration);
822 * Search the enumeration for the old property name
824 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
829 * Delete the property enumeration since we don't need it anymore
831 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
833 if (foundPropertyIndex != PROPERTY_NULL)
835 StgProperty renamedProperty;
836 ULONG renamedPropertyIndex;
839 * Setup a new property for the renamed property
841 renamedProperty.sizeOfNameString =
842 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
844 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
845 return STG_E_INVALIDNAME;
847 strcpyW(renamedProperty.name, pwcsNewName);
849 renamedProperty.propertyType = currentProperty.propertyType;
850 renamedProperty.startingBlock = currentProperty.startingBlock;
851 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
852 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
854 renamedProperty.previousProperty = PROPERTY_NULL;
855 renamedProperty.nextProperty = PROPERTY_NULL;
858 * Bring the dirProperty link in case it is a storage and in which
859 * case the renamed storage elements don't require to be reorganized.
861 renamedProperty.dirProperty = currentProperty.dirProperty;
863 /* call CoFileTime to get the current time
864 renamedProperty.timeStampS1
865 renamedProperty.timeStampD1
866 renamedProperty.timeStampS2
867 renamedProperty.timeStampD2
868 renamedProperty.propertyUniqueID
872 * Obtain a free property in the property chain
874 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
877 * Save the new property into the new property spot
879 StorageImpl_WriteProperty(
880 This->ancestorStorage,
881 renamedPropertyIndex,
885 * Find a spot in the property chain for our newly created property.
889 renamedPropertyIndex,
893 * At this point the renamed property has been inserted in the tree,
894 * now, before Destroying the old property we must zero its dirProperty
895 * otherwise the DestroyProperty below will zap it all and we do not want
897 * Also, we fake that the old property is a storage so the DestroyProperty
898 * will not do a SetSize(0) on the stream data.
900 * This means that we need to tweak the StgProperty if it is a stream or a
903 StorageImpl_ReadProperty(This->ancestorStorage,
907 currentProperty.dirProperty = PROPERTY_NULL;
908 currentProperty.propertyType = PROPTYPE_STORAGE;
909 StorageImpl_WriteProperty(
910 This->ancestorStorage,
915 * Invoke Destroy to get rid of the ole property and automatically redo
916 * the linking of its previous and next members...
918 IStorage_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
924 * There is no property with the old name
926 return STG_E_FILENOTFOUND;
932 /************************************************************************
933 * Storage32BaseImpl_CreateStream (IStorage)
935 * This method will create a stream object within this storage
937 * See Windows documentation for more details on IStorage methods.
939 static HRESULT WINAPI StorageBaseImpl_CreateStream(
941 const OLECHAR* pwcsName, /* [string][in] */
942 DWORD grfMode, /* [in] */
943 DWORD reserved1, /* [in] */
944 DWORD reserved2, /* [in] */
945 IStream** ppstm) /* [out] */
947 StorageBaseImpl *This = (StorageBaseImpl *)iface;
948 IEnumSTATSTGImpl* propertyEnumeration;
949 StgStreamImpl* newStream;
950 StgProperty currentProperty, newStreamProperty;
951 ULONG foundPropertyIndex, newPropertyIndex;
953 TRACE("(%p, %s, %x, %d, %d, %p)\n",
954 iface, debugstr_w(pwcsName), grfMode,
955 reserved1, reserved2, ppstm);
958 * Validate parameters
961 return STG_E_INVALIDPOINTER;
964 return STG_E_INVALIDNAME;
966 if (reserved1 || reserved2)
967 return STG_E_INVALIDPARAMETER;
970 * Validate the STGM flags
972 if ( FAILED( validateSTGM(grfMode) ))
973 return STG_E_INVALIDFLAG;
975 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
976 return STG_E_INVALIDFLAG;
981 if ((grfMode & STGM_DELETEONRELEASE) ||
982 (grfMode & STGM_TRANSACTED))
983 return STG_E_INVALIDFUNCTION;
986 * Check that we're compatible with the parent's storage mode
987 * if not in transacted mode
989 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
990 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
991 return STG_E_ACCESSDENIED;
995 * Initialize the out parameter
1000 * Create a property enumeration to search the properties
1002 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
1003 This->rootPropertySetIndex);
1005 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1009 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1011 if (foundPropertyIndex != PROPERTY_NULL)
1014 * An element with this name already exists
1016 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1018 StgStreamImpl *strm;
1020 LIST_FOR_EACH_ENTRY(strm, &This->strmHead, StgStreamImpl, StrmListEntry)
1022 if (strm->ownerProperty == foundPropertyIndex)
1024 TRACE("Stream deleted %p\n", strm);
1025 strm->parentStorage = NULL;
1026 list_remove(&strm->StrmListEntry);
1029 IStorage_DestroyElement(iface, pwcsName);
1032 return STG_E_FILEALREADYEXISTS;
1034 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1036 WARN("read-only storage\n");
1037 return STG_E_ACCESSDENIED;
1041 * memset the empty property
1043 memset(&newStreamProperty, 0, sizeof(StgProperty));
1045 newStreamProperty.sizeOfNameString =
1046 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
1048 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1049 return STG_E_INVALIDNAME;
1051 strcpyW(newStreamProperty.name, pwcsName);
1053 newStreamProperty.propertyType = PROPTYPE_STREAM;
1054 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
1055 newStreamProperty.size.u.LowPart = 0;
1056 newStreamProperty.size.u.HighPart = 0;
1058 newStreamProperty.previousProperty = PROPERTY_NULL;
1059 newStreamProperty.nextProperty = PROPERTY_NULL;
1060 newStreamProperty.dirProperty = PROPERTY_NULL;
1062 /* call CoFileTime to get the current time
1063 newStreamProperty.timeStampS1
1064 newStreamProperty.timeStampD1
1065 newStreamProperty.timeStampS2
1066 newStreamProperty.timeStampD2
1069 /* newStreamProperty.propertyUniqueID */
1072 * Get a free property or create a new one
1074 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1077 * Save the new property into the new property spot
1079 StorageImpl_WriteProperty(
1080 This->ancestorStorage,
1082 &newStreamProperty);
1085 * Find a spot in the property chain for our newly created property.
1087 updatePropertyChain(
1093 * Open the stream to return it.
1095 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
1099 *ppstm = (IStream*)newStream;
1102 * Since we are returning a pointer to the interface, we have to nail down
1105 IStream_AddRef(*ppstm);
1109 return STG_E_INSUFFICIENTMEMORY;
1115 /************************************************************************
1116 * Storage32BaseImpl_SetClass (IStorage)
1118 * This method will write the specified CLSID in the property of this
1121 * See Windows documentation for more details on IStorage methods.
1123 static HRESULT WINAPI StorageBaseImpl_SetClass(
1125 REFCLSID clsid) /* [in] */
1127 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1128 HRESULT hRes = E_FAIL;
1129 StgProperty curProperty;
1132 TRACE("(%p, %p)\n", iface, clsid);
1134 success = StorageImpl_ReadProperty(This->ancestorStorage,
1135 This->rootPropertySetIndex,
1139 curProperty.propertyUniqueID = *clsid;
1141 success = StorageImpl_WriteProperty(This->ancestorStorage,
1142 This->rootPropertySetIndex,
1151 /************************************************************************
1152 ** Storage32Impl implementation
1155 /************************************************************************
1156 * Storage32Impl_CreateStorage (IStorage)
1158 * This method will create the storage object within the provided storage.
1160 * See Windows documentation for more details on IStorage methods.
1162 static HRESULT WINAPI StorageImpl_CreateStorage(
1164 const OLECHAR *pwcsName, /* [string][in] */
1165 DWORD grfMode, /* [in] */
1166 DWORD reserved1, /* [in] */
1167 DWORD reserved2, /* [in] */
1168 IStorage **ppstg) /* [out] */
1170 StorageImpl* const This=(StorageImpl*)iface;
1172 IEnumSTATSTGImpl *propertyEnumeration;
1173 StgProperty currentProperty;
1174 StgProperty newProperty;
1175 ULONG foundPropertyIndex;
1176 ULONG newPropertyIndex;
1179 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1180 iface, debugstr_w(pwcsName), grfMode,
1181 reserved1, reserved2, ppstg);
1184 * Validate parameters
1187 return STG_E_INVALIDPOINTER;
1190 return STG_E_INVALIDNAME;
1193 * Initialize the out parameter
1198 * Validate the STGM flags
1200 if ( FAILED( validateSTGM(grfMode) ) ||
1201 (grfMode & STGM_DELETEONRELEASE) )
1203 WARN("bad grfMode: 0x%x\n", grfMode);
1204 return STG_E_INVALIDFLAG;
1208 * Check that we're compatible with the parent's storage mode
1210 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->base.openFlags ) )
1212 WARN("access denied\n");
1213 return STG_E_ACCESSDENIED;
1217 * Create a property enumeration and search the properties
1219 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1220 This->base.rootPropertySetIndex);
1222 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1225 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1227 if (foundPropertyIndex != PROPERTY_NULL)
1230 * An element with this name already exists
1232 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1233 IStorage_DestroyElement(iface, pwcsName);
1236 WARN("file already exists\n");
1237 return STG_E_FILEALREADYEXISTS;
1240 else if (STGM_ACCESS_MODE(This->base.openFlags) == STGM_READ)
1242 WARN("read-only storage\n");
1243 return STG_E_ACCESSDENIED;
1247 * memset the empty property
1249 memset(&newProperty, 0, sizeof(StgProperty));
1251 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1253 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1255 FIXME("name too long\n");
1256 return STG_E_INVALIDNAME;
1259 strcpyW(newProperty.name, pwcsName);
1261 newProperty.propertyType = PROPTYPE_STORAGE;
1262 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1263 newProperty.size.u.LowPart = 0;
1264 newProperty.size.u.HighPart = 0;
1266 newProperty.previousProperty = PROPERTY_NULL;
1267 newProperty.nextProperty = PROPERTY_NULL;
1268 newProperty.dirProperty = PROPERTY_NULL;
1270 /* call CoFileTime to get the current time
1271 newProperty.timeStampS1
1272 newProperty.timeStampD1
1273 newProperty.timeStampS2
1274 newProperty.timeStampD2
1277 /* newStorageProperty.propertyUniqueID */
1280 * Obtain a free property in the property chain
1282 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1285 * Save the new property into the new property spot
1287 StorageImpl_WriteProperty(
1288 This->base.ancestorStorage,
1293 * Find a spot in the property chain for our newly created property.
1295 updatePropertyChain(
1301 * Open it to get a pointer to return.
1303 hr = IStorage_OpenStorage(
1305 (const OLECHAR*)pwcsName,
1312 if( (hr != S_OK) || (*ppstg == NULL))
1322 /***************************************************************************
1326 * Get a free property or create a new one.
1328 static ULONG getFreeProperty(
1329 StorageImpl *storage)
1331 ULONG currentPropertyIndex = 0;
1332 ULONG newPropertyIndex = PROPERTY_NULL;
1333 BOOL readSuccessful = TRUE;
1334 StgProperty currentProperty;
1339 * Start by reading the root property
1341 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1342 currentPropertyIndex,
1346 if (currentProperty.sizeOfNameString == 0)
1349 * The property existis and is available, we found it.
1351 newPropertyIndex = currentPropertyIndex;
1357 * We exhausted the property list, we will create more space below
1359 newPropertyIndex = currentPropertyIndex;
1361 currentPropertyIndex++;
1363 } while (newPropertyIndex == PROPERTY_NULL);
1366 * grow the property chain
1368 if (! readSuccessful)
1370 StgProperty emptyProperty;
1371 ULARGE_INTEGER newSize;
1372 ULONG propertyIndex;
1373 ULONG lastProperty = 0;
1374 ULONG blockCount = 0;
1377 * obtain the new count of property blocks
1379 blockCount = BlockChainStream_GetCount(
1380 storage->base.ancestorStorage->rootBlockChain)+1;
1383 * initialize the size used by the property stream
1385 newSize.u.HighPart = 0;
1386 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1389 * add a property block to the property chain
1391 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1394 * memset the empty property in order to initialize the unused newly
1397 memset(&emptyProperty, 0, sizeof(StgProperty));
1402 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1405 propertyIndex = newPropertyIndex;
1406 propertyIndex < lastProperty;
1409 StorageImpl_WriteProperty(
1410 storage->base.ancestorStorage,
1416 return newPropertyIndex;
1419 /****************************************************************************
1423 * Case insensitive comparaison of StgProperty.name by first considering
1426 * Returns <0 when newPrpoerty < currentProperty
1427 * >0 when newPrpoerty > currentProperty
1428 * 0 when newPrpoerty == currentProperty
1430 static LONG propertyNameCmp(
1431 const OLECHAR *newProperty,
1432 const OLECHAR *currentProperty)
1434 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1439 * We compare the string themselves only when they are of the same length
1441 diff = lstrcmpiW( newProperty, currentProperty);
1447 /****************************************************************************
1451 * Properly link this new element in the property chain.
1453 static void updatePropertyChain(
1454 StorageImpl *storage,
1455 ULONG newPropertyIndex,
1456 StgProperty newProperty)
1458 StgProperty currentProperty;
1461 * Read the root property
1463 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1464 storage->base.rootPropertySetIndex,
1467 if (currentProperty.dirProperty != PROPERTY_NULL)
1470 * The root storage contains some element, therefore, start the research
1471 * for the appropriate location.
1474 ULONG current, next, previous, currentPropertyId;
1477 * Keep the StgProperty sequence number of the storage first property
1479 currentPropertyId = currentProperty.dirProperty;
1484 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1485 currentProperty.dirProperty,
1488 previous = currentProperty.previousProperty;
1489 next = currentProperty.nextProperty;
1490 current = currentPropertyId;
1494 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1498 if (previous != PROPERTY_NULL)
1500 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1507 currentProperty.previousProperty = newPropertyIndex;
1508 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1516 if (next != PROPERTY_NULL)
1518 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1525 currentProperty.nextProperty = newPropertyIndex;
1526 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1535 * Trying to insert an item with the same name in the
1536 * subtree structure.
1541 previous = currentProperty.previousProperty;
1542 next = currentProperty.nextProperty;
1548 * The root storage is empty, link the new property to its dir property
1550 currentProperty.dirProperty = newPropertyIndex;
1551 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1552 storage->base.rootPropertySetIndex,
1558 /*************************************************************************
1561 static HRESULT WINAPI StorageImpl_CopyTo(
1563 DWORD ciidExclude, /* [in] */
1564 const IID* rgiidExclude, /* [size_is][unique][in] */
1565 SNB snbExclude, /* [unique][in] */
1566 IStorage* pstgDest) /* [unique][in] */
1568 IEnumSTATSTG *elements = 0;
1569 STATSTG curElement, strStat;
1571 IStorage *pstgTmp, *pstgChild;
1572 IStream *pstrTmp, *pstrChild;
1574 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1575 FIXME("Exclude option not implemented\n");
1577 TRACE("(%p, %d, %p, %p, %p)\n",
1578 iface, ciidExclude, rgiidExclude,
1579 snbExclude, pstgDest);
1582 * Perform a sanity check
1584 if ( pstgDest == 0 )
1585 return STG_E_INVALIDPOINTER;
1588 * Enumerate the elements
1590 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1598 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1599 IStorage_SetClass( pstgDest, &curElement.clsid );
1604 * Obtain the next element
1606 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1608 if ( hr == S_FALSE )
1610 hr = S_OK; /* done, every element has been copied */
1614 if (curElement.type == STGTY_STORAGE)
1617 * open child source storage
1619 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1620 STGM_READ|STGM_SHARE_EXCLUSIVE,
1621 NULL, 0, &pstgChild );
1627 * Check if destination storage is not a child of the source
1628 * storage, which will cause an infinite loop
1630 if (pstgChild == pstgDest)
1632 IEnumSTATSTG_Release(elements);
1634 return STG_E_ACCESSDENIED;
1638 * create a new storage in destination storage
1640 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1641 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1645 * if it already exist, don't create a new one use this one
1647 if (hr == STG_E_FILEALREADYEXISTS)
1649 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1650 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1651 NULL, 0, &pstgTmp );
1659 * do the copy recursively
1661 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1662 snbExclude, pstgTmp );
1664 IStorage_Release( pstgTmp );
1665 IStorage_Release( pstgChild );
1667 else if (curElement.type == STGTY_STREAM)
1670 * create a new stream in destination storage. If the stream already
1671 * exist, it will be deleted and a new one will be created.
1673 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1674 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1681 * open child stream storage
1683 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1684 STGM_READ|STGM_SHARE_EXCLUSIVE,
1691 * Get the size of the source stream
1693 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1696 * Set the size of the destination stream.
1698 IStream_SetSize(pstrTmp, strStat.cbSize);
1703 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1706 IStream_Release( pstrTmp );
1707 IStream_Release( pstrChild );
1711 WARN("unknown element type: %d\n", curElement.type);
1714 } while (hr == S_OK);
1719 IEnumSTATSTG_Release(elements);
1724 /*************************************************************************
1725 * MoveElementTo (IStorage)
1727 static HRESULT WINAPI StorageImpl_MoveElementTo(
1729 const OLECHAR *pwcsName, /* [string][in] */
1730 IStorage *pstgDest, /* [unique][in] */
1731 const OLECHAR *pwcsNewName,/* [string][in] */
1732 DWORD grfFlags) /* [in] */
1734 FIXME("(%p %s %p %s %u): stub\n", iface,
1735 debugstr_w(pwcsName), pstgDest,
1736 debugstr_w(pwcsNewName), grfFlags);
1740 /*************************************************************************
1743 * Ensures that any changes made to a storage object open in transacted mode
1744 * are reflected in the parent storage
1747 * Wine doesn't implement transacted mode, which seems to be a basic
1748 * optimization, so we can ignore this stub for now.
1750 static HRESULT WINAPI StorageImpl_Commit(
1752 DWORD grfCommitFlags)/* [in] */
1754 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1758 /*************************************************************************
1761 * Discard all changes that have been made since the last commit operation
1763 static HRESULT WINAPI StorageImpl_Revert(
1766 FIXME("(%p): stub\n", iface);
1770 /*************************************************************************
1771 * DestroyElement (IStorage)
1773 * Strategy: This implementation is built this way for simplicity not for speed.
1774 * I always delete the topmost element of the enumeration and adjust
1775 * the deleted element pointer all the time. This takes longer to
1776 * do but allow to reinvoke DestroyElement whenever we encounter a
1777 * storage object. The optimisation resides in the usage of another
1778 * enumeration strategy that would give all the leaves of a storage
1779 * first. (postfix order)
1781 static HRESULT WINAPI StorageImpl_DestroyElement(
1783 const OLECHAR *pwcsName)/* [string][in] */
1785 StorageImpl* const This=(StorageImpl*)iface;
1787 IEnumSTATSTGImpl* propertyEnumeration;
1790 StgProperty propertyToDelete;
1791 StgProperty parentProperty;
1792 ULONG foundPropertyIndexToDelete;
1793 ULONG typeOfRelation;
1794 ULONG parentPropertyId = 0;
1797 iface, debugstr_w(pwcsName));
1800 * Perform a sanity check on the parameters.
1803 return STG_E_INVALIDPOINTER;
1806 * Create a property enumeration to search the property with the given name
1808 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1809 This->base.ancestorStorage,
1810 This->base.rootPropertySetIndex);
1812 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1813 propertyEnumeration,
1817 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1819 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1821 return STG_E_FILENOTFOUND;
1825 * Find the parent property of the property to delete (the one that
1826 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1827 * the parent is This. Otherwise, the parent is one of its sibling...
1831 * First, read This's StgProperty..
1833 res = StorageImpl_ReadProperty(
1834 This->base.ancestorStorage,
1835 This->base.rootPropertySetIndex,
1841 * Second, check to see if by any chance the actual storage (This) is not
1842 * the parent of the property to delete... We never know...
1844 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1847 * Set data as it would have been done in the else part...
1849 typeOfRelation = PROPERTY_RELATION_DIR;
1850 parentPropertyId = This->base.rootPropertySetIndex;
1855 * Create a property enumeration to search the parent properties, and
1856 * delete it once done.
1858 IEnumSTATSTGImpl* propertyEnumeration2;
1860 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1861 This->base.ancestorStorage,
1862 This->base.rootPropertySetIndex);
1864 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1865 propertyEnumeration2,
1866 foundPropertyIndexToDelete,
1870 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1873 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1875 hr = deleteStorageProperty(
1877 foundPropertyIndexToDelete,
1880 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1882 hr = deleteStreamProperty(
1884 foundPropertyIndexToDelete,
1892 * Adjust the property chain
1894 hr = adjustPropertyChain(
1905 /************************************************************************
1906 * StorageImpl_Stat (IStorage)
1908 * This method will retrieve information about this storage object.
1910 * See Windows documentation for more details on IStorage methods.
1912 static HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1913 STATSTG* pstatstg, /* [out] */
1914 DWORD grfStatFlag) /* [in] */
1916 StorageImpl* const This = (StorageImpl*)iface;
1917 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1919 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1921 CoTaskMemFree(pstatstg->pwcsName);
1922 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1923 strcpyW(pstatstg->pwcsName, This->pwcsName);
1929 /******************************************************************************
1930 * Internal stream list handlers
1933 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1935 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1936 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1939 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1941 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1942 list_remove(&(strm->StrmListEntry));
1945 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1947 struct list *cur, *cur2;
1948 StgStreamImpl *strm=NULL;
1950 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1951 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1952 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1953 strm->parentStorage = NULL;
1959 /*********************************************************************
1963 * Perform the deletion of a complete storage node
1966 static HRESULT deleteStorageProperty(
1967 StorageImpl *parentStorage,
1968 ULONG indexOfPropertyToDelete,
1969 StgProperty propertyToDelete)
1971 IEnumSTATSTG *elements = 0;
1972 IStorage *childStorage = 0;
1973 STATSTG currentElement;
1975 HRESULT destroyHr = S_OK;
1978 * Open the storage and enumerate it
1980 hr = StorageBaseImpl_OpenStorage(
1981 (IStorage*)parentStorage,
1982 propertyToDelete.name,
1984 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1995 * Enumerate the elements
1997 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2002 * Obtain the next element
2004 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
2007 destroyHr = StorageImpl_DestroyElement(
2008 (IStorage*)childStorage,
2009 (OLECHAR*)currentElement.pwcsName);
2011 CoTaskMemFree(currentElement.pwcsName);
2015 * We need to Reset the enumeration every time because we delete elements
2016 * and the enumeration could be invalid
2018 IEnumSTATSTG_Reset(elements);
2020 } while ((hr == S_OK) && (destroyHr == S_OK));
2023 * Invalidate the property by zeroing its name member.
2025 propertyToDelete.sizeOfNameString = 0;
2027 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
2028 indexOfPropertyToDelete,
2031 IStorage_Release(childStorage);
2032 IEnumSTATSTG_Release(elements);
2037 /*********************************************************************
2041 * Perform the deletion of a stream node
2044 static HRESULT deleteStreamProperty(
2045 StorageImpl *parentStorage,
2046 ULONG indexOfPropertyToDelete,
2047 StgProperty propertyToDelete)
2051 ULARGE_INTEGER size;
2053 size.u.HighPart = 0;
2056 hr = StorageBaseImpl_OpenStream(
2057 (IStorage*)parentStorage,
2058 (OLECHAR*)propertyToDelete.name,
2060 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2072 hr = IStream_SetSize(pis, size);
2080 * Release the stream object.
2082 IStream_Release(pis);
2085 * Invalidate the property by zeroing its name member.
2087 propertyToDelete.sizeOfNameString = 0;
2090 * Here we should re-read the property so we get the updated pointer
2091 * but since we are here to zap it, I don't do it...
2093 StorageImpl_WriteProperty(
2094 parentStorage->base.ancestorStorage,
2095 indexOfPropertyToDelete,
2101 /*********************************************************************
2105 * Finds a placeholder for the StgProperty within the Storage
2108 static HRESULT findPlaceholder(
2109 StorageImpl *storage,
2110 ULONG propertyIndexToStore,
2111 ULONG storePropertyIndex,
2114 StgProperty storeProperty;
2119 * Read the storage property
2121 res = StorageImpl_ReadProperty(
2122 storage->base.ancestorStorage,
2131 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2133 if (storeProperty.previousProperty != PROPERTY_NULL)
2135 return findPlaceholder(
2137 propertyIndexToStore,
2138 storeProperty.previousProperty,
2143 storeProperty.previousProperty = propertyIndexToStore;
2146 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2148 if (storeProperty.nextProperty != PROPERTY_NULL)
2150 return findPlaceholder(
2152 propertyIndexToStore,
2153 storeProperty.nextProperty,
2158 storeProperty.nextProperty = propertyIndexToStore;
2161 else if (typeOfRelation == PROPERTY_RELATION_DIR)
2163 if (storeProperty.dirProperty != PROPERTY_NULL)
2165 return findPlaceholder(
2167 propertyIndexToStore,
2168 storeProperty.dirProperty,
2173 storeProperty.dirProperty = propertyIndexToStore;
2177 hr = StorageImpl_WriteProperty(
2178 storage->base.ancestorStorage,
2190 /*************************************************************************
2194 * This method takes the previous and the next property link of a property
2195 * to be deleted and find them a place in the Storage.
2197 static HRESULT adjustPropertyChain(
2199 StgProperty propertyToDelete,
2200 StgProperty parentProperty,
2201 ULONG parentPropertyId,
2204 ULONG newLinkProperty = PROPERTY_NULL;
2205 BOOL needToFindAPlaceholder = FALSE;
2206 ULONG storeNode = PROPERTY_NULL;
2207 ULONG toStoreNode = PROPERTY_NULL;
2208 INT relationType = 0;
2212 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2214 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2217 * Set the parent previous to the property to delete previous
2219 newLinkProperty = propertyToDelete.previousProperty;
2221 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2224 * We also need to find a storage for the other link, setup variables
2225 * to do this at the end...
2227 needToFindAPlaceholder = TRUE;
2228 storeNode = propertyToDelete.previousProperty;
2229 toStoreNode = propertyToDelete.nextProperty;
2230 relationType = PROPERTY_RELATION_NEXT;
2233 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2236 * Set the parent previous to the property to delete next
2238 newLinkProperty = propertyToDelete.nextProperty;
2242 * Link it for real...
2244 parentProperty.previousProperty = newLinkProperty;
2247 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2249 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2252 * Set the parent next to the property to delete next previous
2254 newLinkProperty = propertyToDelete.previousProperty;
2256 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2259 * We also need to find a storage for the other link, setup variables
2260 * to do this at the end...
2262 needToFindAPlaceholder = TRUE;
2263 storeNode = propertyToDelete.previousProperty;
2264 toStoreNode = propertyToDelete.nextProperty;
2265 relationType = PROPERTY_RELATION_NEXT;
2268 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2271 * Set the parent next to the property to delete next
2273 newLinkProperty = propertyToDelete.nextProperty;
2277 * Link it for real...
2279 parentProperty.nextProperty = newLinkProperty;
2281 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2283 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2286 * Set the parent dir to the property to delete previous
2288 newLinkProperty = propertyToDelete.previousProperty;
2290 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2293 * We also need to find a storage for the other link, setup variables
2294 * to do this at the end...
2296 needToFindAPlaceholder = TRUE;
2297 storeNode = propertyToDelete.previousProperty;
2298 toStoreNode = propertyToDelete.nextProperty;
2299 relationType = PROPERTY_RELATION_NEXT;
2302 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2305 * Set the parent dir to the property to delete next
2307 newLinkProperty = propertyToDelete.nextProperty;
2311 * Link it for real...
2313 parentProperty.dirProperty = newLinkProperty;
2317 * Write back the parent property
2319 res = StorageImpl_WriteProperty(
2320 This->base.ancestorStorage,
2329 * If a placeholder is required for the other link, then, find one and
2330 * get out of here...
2332 if (needToFindAPlaceholder)
2334 hr = findPlaceholder(
2345 /******************************************************************************
2346 * SetElementTimes (IStorage)
2348 static HRESULT WINAPI StorageImpl_SetElementTimes(
2350 const OLECHAR *pwcsName,/* [string][in] */
2351 const FILETIME *pctime, /* [in] */
2352 const FILETIME *patime, /* [in] */
2353 const FILETIME *pmtime) /* [in] */
2355 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2359 /******************************************************************************
2360 * SetStateBits (IStorage)
2362 static HRESULT WINAPI StorageImpl_SetStateBits(
2364 DWORD grfStateBits,/* [in] */
2365 DWORD grfMask) /* [in] */
2367 StorageImpl* const This = (StorageImpl*)iface;
2368 This->base.stateBits = (This->base.stateBits & ~grfMask) | (grfStateBits & grfMask);
2373 * Virtual function table for the IStorage32Impl class.
2375 static const IStorageVtbl Storage32Impl_Vtbl =
2377 StorageBaseImpl_QueryInterface,
2378 StorageBaseImpl_AddRef,
2379 StorageBaseImpl_Release,
2380 StorageBaseImpl_CreateStream,
2381 StorageBaseImpl_OpenStream,
2382 StorageImpl_CreateStorage,
2383 StorageBaseImpl_OpenStorage,
2385 StorageImpl_MoveElementTo,
2388 StorageBaseImpl_EnumElements,
2389 StorageImpl_DestroyElement,
2390 StorageBaseImpl_RenameElement,
2391 StorageImpl_SetElementTimes,
2392 StorageBaseImpl_SetClass,
2393 StorageImpl_SetStateBits,
2397 static HRESULT StorageImpl_Construct(
2407 StgProperty currentProperty;
2408 BOOL readSuccessful;
2409 ULONG currentPropertyIndex;
2411 if ( FAILED( validateSTGM(openFlags) ))
2412 return STG_E_INVALIDFLAG;
2414 memset(This, 0, sizeof(StorageImpl));
2417 * Initialize stream list
2420 list_init(&This->base.strmHead);
2423 * Initialize the virtual function table.
2425 This->base.lpVtbl = &Storage32Impl_Vtbl;
2426 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2427 This->base.v_destructor = &StorageImpl_Destroy;
2428 This->base.openFlags = (openFlags & ~STGM_CREATE);
2431 * This is the top-level storage so initialize the ancestor pointer
2434 This->base.ancestorStorage = This;
2437 * Initialize the physical support of the storage.
2439 This->hFile = hFile;
2442 * Store copy of file path.
2445 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2446 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2447 if (!This->pwcsName)
2448 return STG_E_INSUFFICIENTMEMORY;
2449 strcpyW(This->pwcsName, pwcsName);
2453 * Initialize the big block cache.
2455 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2456 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2457 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2463 if (This->bigBlockFile == 0)
2468 ULARGE_INTEGER size;
2469 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2472 * Initialize all header variables:
2473 * - The big block depot consists of one block and it is at block 0
2474 * - The properties start at block 1
2475 * - There is no small block depot
2477 memset( This->bigBlockDepotStart,
2479 sizeof(This->bigBlockDepotStart));
2481 This->bigBlockDepotCount = 1;
2482 This->bigBlockDepotStart[0] = 0;
2483 This->rootStartBlock = 1;
2484 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2485 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2486 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2487 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2488 This->extBigBlockDepotCount = 0;
2490 StorageImpl_SaveFileHeader(This);
2493 * Add one block for the big block depot and one block for the properties
2495 size.u.HighPart = 0;
2496 size.u.LowPart = This->bigBlockSize * 3;
2497 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2500 * Initialize the big block depot
2502 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2503 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2504 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2505 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2510 * Load the header for the file.
2512 hr = StorageImpl_LoadFileHeader(This);
2516 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2523 * There is no block depot cached yet.
2525 This->indexBlockDepotCached = 0xFFFFFFFF;
2528 * Start searching for free blocks with block 0.
2530 This->prevFreeBlock = 0;
2533 * Create the block chain abstractions.
2535 if(!(This->rootBlockChain =
2536 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2537 return STG_E_READFAULT;
2539 if(!(This->smallBlockDepotChain =
2540 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2542 return STG_E_READFAULT;
2545 * Write the root property (memory only)
2549 StgProperty rootProp;
2551 * Initialize the property chain
2553 memset(&rootProp, 0, sizeof(rootProp));
2554 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2555 sizeof(rootProp.name)/sizeof(WCHAR) );
2556 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2557 rootProp.propertyType = PROPTYPE_ROOT;
2558 rootProp.previousProperty = PROPERTY_NULL;
2559 rootProp.nextProperty = PROPERTY_NULL;
2560 rootProp.dirProperty = PROPERTY_NULL;
2561 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2562 rootProp.size.u.HighPart = 0;
2563 rootProp.size.u.LowPart = 0;
2565 StorageImpl_WriteProperty(This, 0, &rootProp);
2569 * Find the ID of the root in the property sets.
2571 currentPropertyIndex = 0;
2575 readSuccessful = StorageImpl_ReadProperty(
2577 currentPropertyIndex,
2582 if ( (currentProperty.sizeOfNameString != 0 ) &&
2583 (currentProperty.propertyType == PROPTYPE_ROOT) )
2585 This->base.rootPropertySetIndex = currentPropertyIndex;
2589 currentPropertyIndex++;
2591 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2593 if (!readSuccessful)
2596 return STG_E_READFAULT;
2600 * Create the block chain abstraction for the small block root chain.
2602 if(!(This->smallBlockRootChain =
2603 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2604 return STG_E_READFAULT;
2609 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2611 StorageImpl *This = (StorageImpl*) iface;
2612 TRACE("(%p)\n", This);
2614 StorageBaseImpl_DeleteAll(&This->base);
2616 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2618 BlockChainStream_Destroy(This->smallBlockRootChain);
2619 BlockChainStream_Destroy(This->rootBlockChain);
2620 BlockChainStream_Destroy(This->smallBlockDepotChain);
2622 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2623 HeapFree(GetProcessHeap(), 0, This);
2626 /******************************************************************************
2627 * Storage32Impl_GetNextFreeBigBlock
2629 * Returns the index of the next free big block.
2630 * If the big block depot is filled, this method will enlarge it.
2633 static ULONG StorageImpl_GetNextFreeBigBlock(
2636 ULONG depotBlockIndexPos;
2637 BYTE depotBuffer[BIG_BLOCK_SIZE];
2639 ULONG depotBlockOffset;
2640 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2641 ULONG nextBlockIndex = BLOCK_SPECIAL;
2643 ULONG freeBlock = BLOCK_UNUSED;
2645 depotIndex = This->prevFreeBlock / blocksPerDepot;
2646 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2649 * Scan the entire big block depot until we find a block marked free
2651 while (nextBlockIndex != BLOCK_UNUSED)
2653 if (depotIndex < COUNT_BBDEPOTINHEADER)
2655 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2658 * Grow the primary depot.
2660 if (depotBlockIndexPos == BLOCK_UNUSED)
2662 depotBlockIndexPos = depotIndex*blocksPerDepot;
2665 * Add a block depot.
2667 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2668 This->bigBlockDepotCount++;
2669 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2672 * Flag it as a block depot.
2674 StorageImpl_SetNextBlockInChain(This,
2678 /* Save new header information.
2680 StorageImpl_SaveFileHeader(This);
2685 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2687 if (depotBlockIndexPos == BLOCK_UNUSED)
2690 * Grow the extended depot.
2692 ULONG extIndex = BLOCK_UNUSED;
2693 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2694 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2696 if (extBlockOffset == 0)
2698 /* We need an extended block.
2700 extIndex = Storage32Impl_AddExtBlockDepot(This);
2701 This->extBigBlockDepotCount++;
2702 depotBlockIndexPos = extIndex + 1;
2705 depotBlockIndexPos = depotIndex * blocksPerDepot;
2708 * Add a block depot and mark it in the extended block.
2710 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2711 This->bigBlockDepotCount++;
2712 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2714 /* Flag the block depot.
2716 StorageImpl_SetNextBlockInChain(This,
2720 /* If necessary, flag the extended depot block.
2722 if (extIndex != BLOCK_UNUSED)
2723 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2725 /* Save header information.
2727 StorageImpl_SaveFileHeader(This);
2731 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2735 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2736 ( nextBlockIndex != BLOCK_UNUSED))
2738 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2740 if (nextBlockIndex == BLOCK_UNUSED)
2742 freeBlock = (depotIndex * blocksPerDepot) +
2743 (depotBlockOffset/sizeof(ULONG));
2746 depotBlockOffset += sizeof(ULONG);
2751 depotBlockOffset = 0;
2755 * make sure that the block physically exists before using it
2757 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2759 This->prevFreeBlock = freeBlock;
2764 /******************************************************************************
2765 * Storage32Impl_AddBlockDepot
2767 * This will create a depot block, essentially it is a block initialized
2770 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2772 BYTE blockBuffer[BIG_BLOCK_SIZE];
2775 * Initialize blocks as free
2777 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2778 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2781 /******************************************************************************
2782 * Storage32Impl_GetExtDepotBlock
2784 * Returns the index of the block that corresponds to the specified depot
2785 * index. This method is only for depot indexes equal or greater than
2786 * COUNT_BBDEPOTINHEADER.
2788 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2790 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2791 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2792 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2793 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2794 ULONG blockIndex = BLOCK_UNUSED;
2795 ULONG extBlockIndex = This->extBigBlockDepotStart;
2797 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2799 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2800 return BLOCK_UNUSED;
2802 while (extBlockCount > 0)
2804 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2808 if (extBlockIndex != BLOCK_UNUSED)
2809 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
2810 extBlockOffset * sizeof(ULONG), &blockIndex);
2815 /******************************************************************************
2816 * Storage32Impl_SetExtDepotBlock
2818 * Associates the specified block index to the specified depot index.
2819 * This method is only for depot indexes equal or greater than
2820 * COUNT_BBDEPOTINHEADER.
2822 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2824 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2825 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2826 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2827 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2828 ULONG extBlockIndex = This->extBigBlockDepotStart;
2830 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2832 while (extBlockCount > 0)
2834 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2838 if (extBlockIndex != BLOCK_UNUSED)
2840 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
2841 extBlockOffset * sizeof(ULONG),
2846 /******************************************************************************
2847 * Storage32Impl_AddExtBlockDepot
2849 * Creates an extended depot block.
2851 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2853 ULONG numExtBlocks = This->extBigBlockDepotCount;
2854 ULONG nextExtBlock = This->extBigBlockDepotStart;
2855 BYTE depotBuffer[BIG_BLOCK_SIZE];
2856 ULONG index = BLOCK_UNUSED;
2857 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2858 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2859 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2861 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2862 blocksPerDepotBlock;
2864 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2867 * The first extended block.
2869 This->extBigBlockDepotStart = index;
2875 * Follow the chain to the last one.
2877 for (i = 0; i < (numExtBlocks - 1); i++)
2879 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2883 * Add the new extended block to the chain.
2885 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
2890 * Initialize this block.
2892 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2893 StorageImpl_WriteBigBlock(This, index, depotBuffer);
2898 /******************************************************************************
2899 * Storage32Impl_FreeBigBlock
2901 * This method will flag the specified block as free in the big block depot.
2903 static void StorageImpl_FreeBigBlock(
2907 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2909 if (blockIndex < This->prevFreeBlock)
2910 This->prevFreeBlock = blockIndex;
2913 /************************************************************************
2914 * Storage32Impl_GetNextBlockInChain
2916 * This method will retrieve the block index of the next big block in
2919 * Params: This - Pointer to the Storage object.
2920 * blockIndex - Index of the block to retrieve the chain
2922 * nextBlockIndex - receives the return value.
2924 * Returns: This method returns the index of the next block in the chain.
2925 * It will return the constants:
2926 * BLOCK_SPECIAL - If the block given was not part of a
2928 * BLOCK_END_OF_CHAIN - If the block given was the last in
2930 * BLOCK_UNUSED - If the block given was not past of a chain
2932 * BLOCK_EXTBBDEPOT - This block is part of the extended
2935 * See Windows documentation for more details on IStorage methods.
2937 static HRESULT StorageImpl_GetNextBlockInChain(
2940 ULONG* nextBlockIndex)
2942 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2943 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2944 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2945 BYTE depotBuffer[BIG_BLOCK_SIZE];
2947 ULONG depotBlockIndexPos;
2950 *nextBlockIndex = BLOCK_SPECIAL;
2952 if(depotBlockCount >= This->bigBlockDepotCount)
2954 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
2955 This->bigBlockDepotCount);
2956 return STG_E_READFAULT;
2960 * Cache the currently accessed depot block.
2962 if (depotBlockCount != This->indexBlockDepotCached)
2964 This->indexBlockDepotCached = depotBlockCount;
2966 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2968 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2973 * We have to look in the extended depot.
2975 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2978 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2981 return STG_E_READFAULT;
2983 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2985 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2986 This->blockDepotCached[index] = *nextBlockIndex;
2990 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2995 /******************************************************************************
2996 * Storage32Impl_GetNextExtendedBlock
2998 * Given an extended block this method will return the next extended block.
3001 * The last ULONG of an extended block is the block index of the next
3002 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3006 * - The index of the next extended block
3007 * - BLOCK_UNUSED: there is no next extended block.
3008 * - Any other return values denotes failure.
3010 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3012 ULONG nextBlockIndex = BLOCK_SPECIAL;
3013 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3015 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3018 return nextBlockIndex;
3021 /******************************************************************************
3022 * Storage32Impl_SetNextBlockInChain
3024 * This method will write the index of the specified block's next block
3025 * in the big block depot.
3027 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3030 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3031 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3032 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3035 static void StorageImpl_SetNextBlockInChain(
3040 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3041 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3042 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3043 ULONG depotBlockIndexPos;
3045 assert(depotBlockCount < This->bigBlockDepotCount);
3046 assert(blockIndex != nextBlock);
3048 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3050 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3055 * We have to look in the extended depot.
3057 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3060 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3063 * Update the cached block depot, if necessary.
3065 if (depotBlockCount == This->indexBlockDepotCached)
3067 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3071 /******************************************************************************
3072 * Storage32Impl_LoadFileHeader
3074 * This method will read in the file header, i.e. big block index -1.
3076 static HRESULT StorageImpl_LoadFileHeader(
3079 HRESULT hr = STG_E_FILENOTFOUND;
3080 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3086 * Get a pointer to the big block of data containing the header.
3088 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3091 * Extract the information from the header.
3096 * Check for the "magic number" signature and return an error if it is not
3099 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3101 return STG_E_OLDFORMAT;
3104 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3106 return STG_E_INVALIDHEADER;
3109 StorageUtl_ReadWord(
3111 OFFSET_BIGBLOCKSIZEBITS,
3112 &This->bigBlockSizeBits);
3114 StorageUtl_ReadWord(
3116 OFFSET_SMALLBLOCKSIZEBITS,
3117 &This->smallBlockSizeBits);
3119 StorageUtl_ReadDWord(
3121 OFFSET_BBDEPOTCOUNT,
3122 &This->bigBlockDepotCount);
3124 StorageUtl_ReadDWord(
3126 OFFSET_ROOTSTARTBLOCK,
3127 &This->rootStartBlock);
3129 StorageUtl_ReadDWord(
3131 OFFSET_SBDEPOTSTART,
3132 &This->smallBlockDepotStart);
3134 StorageUtl_ReadDWord(
3136 OFFSET_EXTBBDEPOTSTART,
3137 &This->extBigBlockDepotStart);
3139 StorageUtl_ReadDWord(
3141 OFFSET_EXTBBDEPOTCOUNT,
3142 &This->extBigBlockDepotCount);
3144 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3146 StorageUtl_ReadDWord(
3148 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3149 &(This->bigBlockDepotStart[index]));
3153 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3157 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3158 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3162 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
3163 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
3167 * Right now, the code is making some assumptions about the size of the
3168 * blocks, just make sure they are what we're expecting.
3170 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3171 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3173 WARN("Broken OLE storage file\n");
3174 hr = STG_E_INVALIDHEADER;
3183 /******************************************************************************
3184 * Storage32Impl_SaveFileHeader
3186 * This method will save to the file the header, i.e. big block -1.
3188 static void StorageImpl_SaveFileHeader(
3191 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3196 * Get a pointer to the big block of data containing the header.
3198 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3201 * If the block read failed, the file is probably new.
3206 * Initialize for all unknown fields.
3208 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3211 * Initialize the magic number.
3213 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3216 * And a bunch of things we don't know what they mean
3218 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3219 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3220 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3221 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3225 * Write the information to the header.
3227 StorageUtl_WriteWord(
3229 OFFSET_BIGBLOCKSIZEBITS,
3230 This->bigBlockSizeBits);
3232 StorageUtl_WriteWord(
3234 OFFSET_SMALLBLOCKSIZEBITS,
3235 This->smallBlockSizeBits);
3237 StorageUtl_WriteDWord(
3239 OFFSET_BBDEPOTCOUNT,
3240 This->bigBlockDepotCount);
3242 StorageUtl_WriteDWord(
3244 OFFSET_ROOTSTARTBLOCK,
3245 This->rootStartBlock);
3247 StorageUtl_WriteDWord(
3249 OFFSET_SBDEPOTSTART,
3250 This->smallBlockDepotStart);
3252 StorageUtl_WriteDWord(
3254 OFFSET_SBDEPOTCOUNT,
3255 This->smallBlockDepotChain ?
3256 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3258 StorageUtl_WriteDWord(
3260 OFFSET_EXTBBDEPOTSTART,
3261 This->extBigBlockDepotStart);
3263 StorageUtl_WriteDWord(
3265 OFFSET_EXTBBDEPOTCOUNT,
3266 This->extBigBlockDepotCount);
3268 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3270 StorageUtl_WriteDWord(
3272 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3273 (This->bigBlockDepotStart[index]));
3277 * Write the big block back to the file.
3279 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3282 /******************************************************************************
3283 * Storage32Impl_ReadProperty
3285 * This method will read the specified property from the property chain.
3287 BOOL StorageImpl_ReadProperty(
3290 StgProperty* buffer)
3292 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3293 ULARGE_INTEGER offsetInPropSet;
3297 offsetInPropSet.u.HighPart = 0;
3298 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3300 readRes = BlockChainStream_ReadAt(
3301 This->rootBlockChain,
3307 if (SUCCEEDED(readRes))
3309 /* replace the name of root entry (often "Root Entry") by the file name */
3310 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3311 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3313 memset(buffer->name, 0, sizeof(buffer->name));
3317 PROPERTY_NAME_BUFFER_LEN );
3318 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3320 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3322 StorageUtl_ReadWord(
3324 OFFSET_PS_NAMELENGTH,
3325 &buffer->sizeOfNameString);
3327 StorageUtl_ReadDWord(
3329 OFFSET_PS_PREVIOUSPROP,
3330 &buffer->previousProperty);
3332 StorageUtl_ReadDWord(
3335 &buffer->nextProperty);
3337 StorageUtl_ReadDWord(
3340 &buffer->dirProperty);
3342 StorageUtl_ReadGUID(
3345 &buffer->propertyUniqueID);
3347 StorageUtl_ReadDWord(
3350 &buffer->timeStampS1);
3352 StorageUtl_ReadDWord(
3355 &buffer->timeStampD1);
3357 StorageUtl_ReadDWord(
3360 &buffer->timeStampS2);
3362 StorageUtl_ReadDWord(
3365 &buffer->timeStampD2);
3367 StorageUtl_ReadDWord(
3369 OFFSET_PS_STARTBLOCK,
3370 &buffer->startingBlock);
3372 StorageUtl_ReadDWord(
3375 &buffer->size.u.LowPart);
3377 buffer->size.u.HighPart = 0;
3380 return SUCCEEDED(readRes) ? TRUE : FALSE;
3383 /*********************************************************************
3384 * Write the specified property into the property chain
3386 BOOL StorageImpl_WriteProperty(
3389 const StgProperty* buffer)
3391 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3392 ULARGE_INTEGER offsetInPropSet;
3396 offsetInPropSet.u.HighPart = 0;
3397 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3399 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3402 currentProperty + OFFSET_PS_NAME,
3404 PROPERTY_NAME_BUFFER_LEN );
3406 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3408 StorageUtl_WriteWord(
3410 OFFSET_PS_NAMELENGTH,
3411 buffer->sizeOfNameString);
3413 StorageUtl_WriteDWord(
3415 OFFSET_PS_PREVIOUSPROP,
3416 buffer->previousProperty);
3418 StorageUtl_WriteDWord(
3421 buffer->nextProperty);
3423 StorageUtl_WriteDWord(
3426 buffer->dirProperty);
3428 StorageUtl_WriteGUID(
3431 &buffer->propertyUniqueID);
3433 StorageUtl_WriteDWord(
3436 buffer->timeStampS1);
3438 StorageUtl_WriteDWord(
3441 buffer->timeStampD1);
3443 StorageUtl_WriteDWord(
3446 buffer->timeStampS2);
3448 StorageUtl_WriteDWord(
3451 buffer->timeStampD2);
3453 StorageUtl_WriteDWord(
3455 OFFSET_PS_STARTBLOCK,
3456 buffer->startingBlock);
3458 StorageUtl_WriteDWord(
3461 buffer->size.u.LowPart);
3463 writeRes = BlockChainStream_WriteAt(This->rootBlockChain,
3468 return SUCCEEDED(writeRes) ? TRUE : FALSE;
3471 static BOOL StorageImpl_ReadBigBlock(
3476 ULARGE_INTEGER ulOffset;
3479 ulOffset.u.HighPart = 0;
3480 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3482 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3483 return (read == This->bigBlockSize);
3486 static BOOL StorageImpl_ReadDWordFromBigBlock(
3492 ULARGE_INTEGER ulOffset;
3496 ulOffset.u.HighPart = 0;
3497 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3498 ulOffset.u.LowPart += offset;
3500 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3501 *value = le32toh(tmp);
3502 return (read == sizeof(DWORD));
3505 static BOOL StorageImpl_WriteBigBlock(
3510 ULARGE_INTEGER ulOffset;
3513 ulOffset.u.HighPart = 0;
3514 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3516 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3517 return (wrote == This->bigBlockSize);
3520 static BOOL StorageImpl_WriteDWordToBigBlock(
3526 ULARGE_INTEGER ulOffset;
3529 ulOffset.u.HighPart = 0;
3530 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3531 ulOffset.u.LowPart += offset;
3533 value = htole32(value);
3534 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3535 return (wrote == sizeof(DWORD));
3538 /******************************************************************************
3539 * Storage32Impl_SmallBlocksToBigBlocks
3541 * This method will convert a small block chain to a big block chain.
3542 * The small block chain will be destroyed.
3544 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3546 SmallBlockChainStream** ppsbChain)
3548 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3549 ULARGE_INTEGER size, offset;
3550 ULONG cbRead, cbWritten;
3551 ULARGE_INTEGER cbTotalRead;
3552 ULONG propertyIndex;
3553 HRESULT resWrite = S_OK;
3555 StgProperty chainProperty;
3557 BlockChainStream *bbTempChain = NULL;
3558 BlockChainStream *bigBlockChain = NULL;
3561 * Create a temporary big block chain that doesn't have
3562 * an associated property. This temporary chain will be
3563 * used to copy data from small blocks to big blocks.
3565 bbTempChain = BlockChainStream_Construct(This,
3568 if(!bbTempChain) return NULL;
3570 * Grow the big block chain.
3572 size = SmallBlockChainStream_GetSize(*ppsbChain);
3573 BlockChainStream_SetSize(bbTempChain, size);
3576 * Copy the contents of the small block chain to the big block chain
3577 * by small block size increments.
3579 offset.u.LowPart = 0;
3580 offset.u.HighPart = 0;
3581 cbTotalRead.QuadPart = 0;
3583 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3586 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3588 This->smallBlockSize,
3591 if (FAILED(resRead))
3596 cbTotalRead.QuadPart += cbRead;
3598 resWrite = BlockChainStream_WriteAt(bbTempChain,
3604 if (FAILED(resWrite))
3607 offset.u.LowPart += This->smallBlockSize;
3609 } while (cbTotalRead.QuadPart < size.QuadPart);
3610 HeapFree(GetProcessHeap(),0,buffer);
3612 if (FAILED(resRead) || FAILED(resWrite))
3614 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3615 BlockChainStream_Destroy(bbTempChain);
3620 * Destroy the small block chain.
3622 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3623 size.u.HighPart = 0;
3625 SmallBlockChainStream_SetSize(*ppsbChain, size);
3626 SmallBlockChainStream_Destroy(*ppsbChain);
3630 * Change the property information. This chain is now a big block chain
3631 * and it doesn't reside in the small blocks chain anymore.
3633 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3635 chainProperty.startingBlock = bbHeadOfChain;
3637 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3640 * Destroy the temporary propertyless big block chain.
3641 * Create a new big block chain associated with this property.
3643 BlockChainStream_Destroy(bbTempChain);
3644 bigBlockChain = BlockChainStream_Construct(This,
3648 return bigBlockChain;
3651 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3653 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3655 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3656 HeapFree(GetProcessHeap(), 0, This);
3659 /******************************************************************************
3661 ** Storage32InternalImpl_Commit
3663 ** The non-root storages cannot be opened in transacted mode thus this function
3666 static HRESULT WINAPI StorageInternalImpl_Commit(
3668 DWORD grfCommitFlags) /* [in] */
3673 /******************************************************************************
3675 ** Storage32InternalImpl_Revert
3677 ** The non-root storages cannot be opened in transacted mode thus this function
3680 static HRESULT WINAPI StorageInternalImpl_Revert(
3686 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3688 IStorage_Release((IStorage*)This->parentStorage);
3689 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3690 HeapFree(GetProcessHeap(), 0, This);
3693 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3694 IEnumSTATSTG* iface,
3698 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3701 * Perform a sanity check on the parameters.
3704 return E_INVALIDARG;
3707 * Initialize the return parameter.
3712 * Compare the riid with the interface IDs implemented by this object.
3714 if (IsEqualGUID(&IID_IUnknown, riid) ||
3715 IsEqualGUID(&IID_IEnumSTATSTG, riid))
3717 *ppvObject = (IEnumSTATSTG*)This;
3718 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3722 return E_NOINTERFACE;
3725 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3726 IEnumSTATSTG* iface)
3728 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3729 return InterlockedIncrement(&This->ref);
3732 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3733 IEnumSTATSTG* iface)
3735 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3739 newRef = InterlockedDecrement(&This->ref);
3742 * If the reference count goes down to 0, perform suicide.
3746 IEnumSTATSTGImpl_Destroy(This);
3752 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3753 IEnumSTATSTG* iface,
3756 ULONG* pceltFetched)
3758 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3760 StgProperty currentProperty;
3761 STATSTG* currentReturnStruct = rgelt;
3762 ULONG objectFetched = 0;
3763 ULONG currentSearchNode;
3766 * Perform a sanity check on the parameters.
3768 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3769 return E_INVALIDARG;
3772 * To avoid the special case, get another pointer to a ULONG value if
3773 * the caller didn't supply one.
3775 if (pceltFetched==0)
3776 pceltFetched = &objectFetched;
3779 * Start the iteration, we will iterate until we hit the end of the
3780 * linked list or until we hit the number of items to iterate through
3785 * Start with the node at the top of the stack.
3787 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3789 while ( ( *pceltFetched < celt) &&
3790 ( currentSearchNode!=PROPERTY_NULL) )
3793 * Remove the top node from the stack
3795 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3798 * Read the property from the storage.
3800 StorageImpl_ReadProperty(This->parentStorage,
3805 * Copy the information to the return buffer.
3807 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3812 * Step to the next item in the iteration
3815 currentReturnStruct++;
3818 * Push the next search node in the search stack.
3820 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3823 * continue the iteration.
3825 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3828 if (*pceltFetched == celt)
3835 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3836 IEnumSTATSTG* iface,
3839 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3841 StgProperty currentProperty;
3842 ULONG objectFetched = 0;
3843 ULONG currentSearchNode;
3846 * Start with the node at the top of the stack.
3848 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3850 while ( (objectFetched < celt) &&
3851 (currentSearchNode!=PROPERTY_NULL) )
3854 * Remove the top node from the stack
3856 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3859 * Read the property from the storage.
3861 StorageImpl_ReadProperty(This->parentStorage,
3866 * Step to the next item in the iteration
3871 * Push the next search node in the search stack.
3873 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3876 * continue the iteration.
3878 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3881 if (objectFetched == celt)
3887 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3888 IEnumSTATSTG* iface)
3890 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3892 StgProperty rootProperty;
3893 BOOL readSuccessful;
3896 * Re-initialize the search stack to an empty stack
3898 This->stackSize = 0;
3901 * Read the root property from the storage.
3903 readSuccessful = StorageImpl_ReadProperty(
3904 This->parentStorage,
3905 This->firstPropertyNode,
3910 assert(rootProperty.sizeOfNameString!=0);
3913 * Push the search node in the search stack.
3915 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3921 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3922 IEnumSTATSTG* iface,
3923 IEnumSTATSTG** ppenum)
3925 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3927 IEnumSTATSTGImpl* newClone;
3930 * Perform a sanity check on the parameters.
3933 return E_INVALIDARG;
3935 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3936 This->firstPropertyNode);
3940 * The new clone enumeration must point to the same current node as
3943 newClone->stackSize = This->stackSize ;
3944 newClone->stackMaxSize = This->stackMaxSize ;
3945 newClone->stackToVisit =
3946 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3949 newClone->stackToVisit,
3951 sizeof(ULONG) * newClone->stackSize);
3953 *ppenum = (IEnumSTATSTG*)newClone;
3956 * Don't forget to nail down a reference to the clone before
3959 IEnumSTATSTGImpl_AddRef(*ppenum);
3964 static INT IEnumSTATSTGImpl_FindParentProperty(
3965 IEnumSTATSTGImpl *This,
3966 ULONG childProperty,
3967 StgProperty *currentProperty,
3970 ULONG currentSearchNode;
3974 * To avoid the special case, get another pointer to a ULONG value if
3975 * the caller didn't supply one.
3978 thisNodeId = &foundNode;
3981 * Start with the node at the top of the stack.
3983 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3986 while (currentSearchNode!=PROPERTY_NULL)
3989 * Store the current node in the returned parameters
3991 *thisNodeId = currentSearchNode;
3994 * Remove the top node from the stack
3996 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3999 * Read the property from the storage.
4001 StorageImpl_ReadProperty(
4002 This->parentStorage,
4006 if (currentProperty->previousProperty == childProperty)
4007 return PROPERTY_RELATION_PREVIOUS;
4009 else if (currentProperty->nextProperty == childProperty)
4010 return PROPERTY_RELATION_NEXT;
4012 else if (currentProperty->dirProperty == childProperty)
4013 return PROPERTY_RELATION_DIR;
4016 * Push the next search node in the search stack.
4018 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4021 * continue the iteration.
4023 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4026 return PROPERTY_NULL;
4029 static ULONG IEnumSTATSTGImpl_FindProperty(
4030 IEnumSTATSTGImpl* This,
4031 const OLECHAR* lpszPropName,
4032 StgProperty* currentProperty)
4034 ULONG currentSearchNode;
4037 * Start with the node at the top of the stack.
4039 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4041 while (currentSearchNode!=PROPERTY_NULL)
4044 * Remove the top node from the stack
4046 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4049 * Read the property from the storage.
4051 StorageImpl_ReadProperty(This->parentStorage,
4055 if ( propertyNameCmp(
4056 (const OLECHAR*)currentProperty->name,
4057 (const OLECHAR*)lpszPropName) == 0)
4058 return currentSearchNode;
4061 * Push the next search node in the search stack.
4063 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4066 * continue the iteration.
4068 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4071 return PROPERTY_NULL;
4074 static void IEnumSTATSTGImpl_PushSearchNode(
4075 IEnumSTATSTGImpl* This,
4078 StgProperty rootProperty;
4079 BOOL readSuccessful;
4082 * First, make sure we're not trying to push an unexisting node.
4084 if (nodeToPush==PROPERTY_NULL)
4088 * First push the node to the stack
4090 if (This->stackSize == This->stackMaxSize)
4092 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4094 This->stackToVisit = HeapReAlloc(
4098 sizeof(ULONG) * This->stackMaxSize);
4101 This->stackToVisit[This->stackSize] = nodeToPush;
4105 * Read the root property from the storage.
4107 readSuccessful = StorageImpl_ReadProperty(
4108 This->parentStorage,
4114 assert(rootProperty.sizeOfNameString!=0);
4117 * Push the previous search node in the search stack.
4119 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4123 static ULONG IEnumSTATSTGImpl_PopSearchNode(
4124 IEnumSTATSTGImpl* This,
4129 if (This->stackSize == 0)
4130 return PROPERTY_NULL;
4132 topNode = This->stackToVisit[This->stackSize-1];
4141 * Virtual function table for the IEnumSTATSTGImpl class.
4143 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4145 IEnumSTATSTGImpl_QueryInterface,
4146 IEnumSTATSTGImpl_AddRef,
4147 IEnumSTATSTGImpl_Release,
4148 IEnumSTATSTGImpl_Next,
4149 IEnumSTATSTGImpl_Skip,
4150 IEnumSTATSTGImpl_Reset,
4151 IEnumSTATSTGImpl_Clone
4154 /******************************************************************************
4155 ** IEnumSTATSTGImpl implementation
4158 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4159 StorageImpl* parentStorage,
4160 ULONG firstPropertyNode)
4162 IEnumSTATSTGImpl* newEnumeration;
4164 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4166 if (newEnumeration!=0)
4169 * Set-up the virtual function table and reference count.
4171 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4172 newEnumeration->ref = 0;
4175 * We want to nail-down the reference to the storage in case the
4176 * enumeration out-lives the storage in the client application.
4178 newEnumeration->parentStorage = parentStorage;
4179 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4181 newEnumeration->firstPropertyNode = firstPropertyNode;
4184 * Initialize the search stack
4186 newEnumeration->stackSize = 0;
4187 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4188 newEnumeration->stackToVisit =
4189 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4192 * Make sure the current node of the iterator is the first one.
4194 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4197 return newEnumeration;
4201 * Virtual function table for the Storage32InternalImpl class.
4203 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4205 StorageBaseImpl_QueryInterface,
4206 StorageBaseImpl_AddRef,
4207 StorageBaseImpl_Release,
4208 StorageBaseImpl_CreateStream,
4209 StorageBaseImpl_OpenStream,
4210 StorageImpl_CreateStorage,
4211 StorageBaseImpl_OpenStorage,
4213 StorageImpl_MoveElementTo,
4214 StorageInternalImpl_Commit,
4215 StorageInternalImpl_Revert,
4216 StorageBaseImpl_EnumElements,
4217 StorageImpl_DestroyElement,
4218 StorageBaseImpl_RenameElement,
4219 StorageImpl_SetElementTimes,
4220 StorageBaseImpl_SetClass,
4221 StorageImpl_SetStateBits,
4222 StorageBaseImpl_Stat
4225 /******************************************************************************
4226 ** Storage32InternalImpl implementation
4229 static StorageInternalImpl* StorageInternalImpl_Construct(
4230 StorageImpl* ancestorStorage,
4232 ULONG rootPropertyIndex)
4234 StorageInternalImpl* newStorage;
4237 * Allocate space for the new storage object
4239 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
4244 * Initialize the stream list
4246 list_init(&newStorage->base.strmHead);
4249 * Initialize the virtual function table.
4251 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4252 newStorage->base.v_destructor = &StorageInternalImpl_Destroy;
4253 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4256 * Keep the ancestor storage pointer and nail a reference to it.
4258 newStorage->base.ancestorStorage = ancestorStorage;
4259 StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
4262 * Keep the index of the root property set for this storage,
4264 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4272 /******************************************************************************
4273 ** StorageUtl implementation
4276 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4280 memcpy(&tmp, buffer+offset, sizeof(WORD));
4281 *value = le16toh(tmp);
4284 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4286 value = htole16(value);
4287 memcpy(buffer+offset, &value, sizeof(WORD));
4290 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4294 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4295 *value = le32toh(tmp);
4298 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4300 value = htole32(value);
4301 memcpy(buffer+offset, &value, sizeof(DWORD));
4304 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4305 ULARGE_INTEGER* value)
4307 #ifdef WORDS_BIGENDIAN
4310 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4311 value->u.LowPart = htole32(tmp.u.HighPart);
4312 value->u.HighPart = htole32(tmp.u.LowPart);
4314 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4318 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4319 const ULARGE_INTEGER *value)
4321 #ifdef WORDS_BIGENDIAN
4324 tmp.u.LowPart = htole32(value->u.HighPart);
4325 tmp.u.HighPart = htole32(value->u.LowPart);
4326 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4328 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4332 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4334 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4335 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4336 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4338 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4341 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4343 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4344 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4345 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4347 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4350 void StorageUtl_CopyPropertyToSTATSTG(
4351 STATSTG* destination,
4352 const StgProperty* source,
4356 * The copy of the string occurs only when the flag is not set
4358 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4359 (source->name == NULL) ||
4360 (source->name[0] == 0) )
4362 destination->pwcsName = 0;
4366 destination->pwcsName =
4367 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4369 strcpyW((LPWSTR)destination->pwcsName, source->name);
4372 switch (source->propertyType)
4374 case PROPTYPE_STORAGE:
4376 destination->type = STGTY_STORAGE;
4378 case PROPTYPE_STREAM:
4379 destination->type = STGTY_STREAM;
4382 destination->type = STGTY_STREAM;
4386 destination->cbSize = source->size;
4388 currentReturnStruct->mtime = {0}; TODO
4389 currentReturnStruct->ctime = {0};
4390 currentReturnStruct->atime = {0};
4392 destination->grfMode = 0;
4393 destination->grfLocksSupported = 0;
4394 destination->clsid = source->propertyUniqueID;
4395 destination->grfStateBits = 0;
4396 destination->reserved = 0;
4399 /******************************************************************************
4400 ** BlockChainStream implementation
4403 BlockChainStream* BlockChainStream_Construct(
4404 StorageImpl* parentStorage,
4405 ULONG* headOfStreamPlaceHolder,
4406 ULONG propertyIndex)
4408 BlockChainStream* newStream;
4411 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4413 newStream->parentStorage = parentStorage;
4414 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4415 newStream->ownerPropertyIndex = propertyIndex;
4416 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4417 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4418 newStream->numBlocks = 0;
4420 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4422 while (blockIndex != BLOCK_END_OF_CHAIN)
4424 newStream->numBlocks++;
4425 newStream->tailIndex = blockIndex;
4427 if(FAILED(StorageImpl_GetNextBlockInChain(
4432 HeapFree(GetProcessHeap(), 0, newStream);
4440 void BlockChainStream_Destroy(BlockChainStream* This)
4442 HeapFree(GetProcessHeap(), 0, This);
4445 /******************************************************************************
4446 * BlockChainStream_GetHeadOfChain
4448 * Returns the head of this stream chain.
4449 * Some special chains don't have properties, their heads are kept in
4450 * This->headOfStreamPlaceHolder.
4453 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4455 StgProperty chainProperty;
4456 BOOL readSuccessful;
4458 if (This->headOfStreamPlaceHolder != 0)
4459 return *(This->headOfStreamPlaceHolder);
4461 if (This->ownerPropertyIndex != PROPERTY_NULL)
4463 readSuccessful = StorageImpl_ReadProperty(
4464 This->parentStorage,
4465 This->ownerPropertyIndex,
4470 return chainProperty.startingBlock;
4474 return BLOCK_END_OF_CHAIN;
4477 /******************************************************************************
4478 * BlockChainStream_GetCount
4480 * Returns the number of blocks that comprises this chain.
4481 * This is not the size of the stream as the last block may not be full!
4484 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
4489 blockIndex = BlockChainStream_GetHeadOfChain(This);
4491 while (blockIndex != BLOCK_END_OF_CHAIN)
4495 if(FAILED(StorageImpl_GetNextBlockInChain(
4496 This->parentStorage,
4505 /******************************************************************************
4506 * BlockChainStream_ReadAt
4508 * Reads a specified number of bytes from this chain at the specified offset.
4509 * bytesRead may be NULL.
4510 * Failure will be returned if the specified number of bytes has not been read.
4512 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
4513 ULARGE_INTEGER offset,
4518 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4519 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4520 ULONG bytesToReadInBuffer;
4524 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
4527 * Find the first block in the stream that contains part of the buffer.
4529 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4530 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4531 (blockNoInSequence < This->lastBlockNoInSequence) )
4533 blockIndex = BlockChainStream_GetHeadOfChain(This);
4534 This->lastBlockNoInSequence = blockNoInSequence;
4538 ULONG temp = blockNoInSequence;
4540 blockIndex = This->lastBlockNoInSequenceIndex;
4541 blockNoInSequence -= This->lastBlockNoInSequence;
4542 This->lastBlockNoInSequence = temp;
4545 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4547 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4548 return STG_E_DOCFILECORRUPT;
4549 blockNoInSequence--;
4552 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4553 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
4555 This->lastBlockNoInSequenceIndex = blockIndex;
4558 * Start reading the buffer.
4561 bufferWalker = buffer;
4563 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4565 ULARGE_INTEGER ulOffset;
4568 * Calculate how many bytes we can copy from this big block.
4570 bytesToReadInBuffer =
4571 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4573 TRACE("block %i\n",blockIndex);
4574 ulOffset.u.HighPart = 0;
4575 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4578 StorageImpl_ReadAt(This->parentStorage,
4581 bytesToReadInBuffer,
4584 * Step to the next big block.
4586 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4587 return STG_E_DOCFILECORRUPT;
4589 bufferWalker += bytesReadAt;
4590 size -= bytesReadAt;
4591 *bytesRead += bytesReadAt;
4592 offsetInBlock = 0; /* There is no offset on the next block */
4594 if (bytesToReadInBuffer != bytesReadAt)
4598 return (size == 0) ? S_OK : STG_E_READFAULT;
4601 /******************************************************************************
4602 * BlockChainStream_WriteAt
4604 * Writes the specified number of bytes to this chain at the specified offset.
4605 * bytesWritten may be NULL.
4606 * Will fail if not all specified number of bytes have been written.
4608 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
4609 ULARGE_INTEGER offset,
4612 ULONG* bytesWritten)
4614 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4615 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4618 const BYTE* bufferWalker;
4621 * Find the first block in the stream that contains part of the buffer.
4623 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4624 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4625 (blockNoInSequence < This->lastBlockNoInSequence) )
4627 blockIndex = BlockChainStream_GetHeadOfChain(This);
4628 This->lastBlockNoInSequence = blockNoInSequence;
4632 ULONG temp = blockNoInSequence;
4634 blockIndex = This->lastBlockNoInSequenceIndex;
4635 blockNoInSequence -= This->lastBlockNoInSequence;
4636 This->lastBlockNoInSequence = temp;
4639 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4641 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4643 return STG_E_DOCFILECORRUPT;
4644 blockNoInSequence--;
4647 This->lastBlockNoInSequenceIndex = blockIndex;
4649 /* BlockChainStream_SetSize should have already been called to ensure we have
4650 * enough blocks in the chain to write into */
4651 if (blockIndex == BLOCK_END_OF_CHAIN)
4653 ERR("not enough blocks in chain to write data\n");
4654 return STG_E_DOCFILECORRUPT;
4658 bufferWalker = (const BYTE*)buffer;
4660 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4662 ULARGE_INTEGER ulOffset;
4663 DWORD bytesWrittenAt;
4665 * Calculate how many bytes we can copy from this big block.
4668 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4670 TRACE("block %i\n",blockIndex);
4671 ulOffset.u.HighPart = 0;
4672 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4675 StorageImpl_WriteAt(This->parentStorage,
4682 * Step to the next big block.
4684 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4686 return STG_E_DOCFILECORRUPT;
4688 bufferWalker += bytesWrittenAt;
4689 size -= bytesWrittenAt;
4690 *bytesWritten += bytesWrittenAt;
4691 offsetInBlock = 0; /* There is no offset on the next block */
4693 if (bytesWrittenAt != bytesToWrite)
4697 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
4700 /******************************************************************************
4701 * BlockChainStream_Shrink
4703 * Shrinks this chain in the big block depot.
4705 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
4706 ULARGE_INTEGER newSize)
4708 ULONG blockIndex, extraBlock;
4713 * Reset the last accessed block cache.
4715 This->lastBlockNoInSequence = 0xFFFFFFFF;
4716 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4719 * Figure out how many blocks are needed to contain the new size
4721 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4723 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4726 blockIndex = BlockChainStream_GetHeadOfChain(This);
4729 * Go to the new end of chain
4731 while (count < numBlocks)
4733 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4739 /* Get the next block before marking the new end */
4740 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4744 /* Mark the new end of chain */
4745 StorageImpl_SetNextBlockInChain(
4746 This->parentStorage,
4748 BLOCK_END_OF_CHAIN);
4750 This->tailIndex = blockIndex;
4751 This->numBlocks = numBlocks;
4754 * Mark the extra blocks as free
4756 while (extraBlock != BLOCK_END_OF_CHAIN)
4758 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4761 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4762 extraBlock = blockIndex;
4768 /******************************************************************************
4769 * BlockChainStream_Enlarge
4771 * Grows this chain in the big block depot.
4773 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4774 ULARGE_INTEGER newSize)
4776 ULONG blockIndex, currentBlock;
4778 ULONG oldNumBlocks = 0;
4780 blockIndex = BlockChainStream_GetHeadOfChain(This);
4783 * Empty chain. Create the head.
4785 if (blockIndex == BLOCK_END_OF_CHAIN)
4787 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4788 StorageImpl_SetNextBlockInChain(This->parentStorage,
4790 BLOCK_END_OF_CHAIN);
4792 if (This->headOfStreamPlaceHolder != 0)
4794 *(This->headOfStreamPlaceHolder) = blockIndex;
4798 StgProperty chainProp;
4799 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4801 StorageImpl_ReadProperty(
4802 This->parentStorage,
4803 This->ownerPropertyIndex,
4806 chainProp.startingBlock = blockIndex;
4808 StorageImpl_WriteProperty(
4809 This->parentStorage,
4810 This->ownerPropertyIndex,
4814 This->tailIndex = blockIndex;
4815 This->numBlocks = 1;
4819 * Figure out how many blocks are needed to contain this stream
4821 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4823 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4827 * Go to the current end of chain
4829 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4831 currentBlock = blockIndex;
4833 while (blockIndex != BLOCK_END_OF_CHAIN)
4836 currentBlock = blockIndex;
4838 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4843 This->tailIndex = currentBlock;
4846 currentBlock = This->tailIndex;
4847 oldNumBlocks = This->numBlocks;
4850 * Add new blocks to the chain
4852 if (oldNumBlocks < newNumBlocks)
4854 while (oldNumBlocks < newNumBlocks)
4856 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4858 StorageImpl_SetNextBlockInChain(
4859 This->parentStorage,
4863 StorageImpl_SetNextBlockInChain(
4864 This->parentStorage,
4866 BLOCK_END_OF_CHAIN);
4868 currentBlock = blockIndex;
4872 This->tailIndex = blockIndex;
4873 This->numBlocks = newNumBlocks;
4879 /******************************************************************************
4880 * BlockChainStream_SetSize
4882 * Sets the size of this stream. The big block depot will be updated.
4883 * The file will grow if we grow the chain.
4885 * TODO: Free the actual blocks in the file when we shrink the chain.
4886 * Currently, the blocks are still in the file. So the file size
4887 * doesn't shrink even if we shrink streams.
4889 BOOL BlockChainStream_SetSize(
4890 BlockChainStream* This,
4891 ULARGE_INTEGER newSize)
4893 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4895 if (newSize.u.LowPart == size.u.LowPart)
4898 if (newSize.u.LowPart < size.u.LowPart)
4900 BlockChainStream_Shrink(This, newSize);
4904 BlockChainStream_Enlarge(This, newSize);
4910 /******************************************************************************
4911 * BlockChainStream_GetSize
4913 * Returns the size of this chain.
4914 * Will return the block count if this chain doesn't have a property.
4916 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4918 StgProperty chainProperty;
4920 if(This->headOfStreamPlaceHolder == NULL)
4923 * This chain is a data stream read the property and return
4924 * the appropriate size
4926 StorageImpl_ReadProperty(
4927 This->parentStorage,
4928 This->ownerPropertyIndex,
4931 return chainProperty.size;
4936 * this chain is a chain that does not have a property, figure out the
4937 * size by making the product number of used blocks times the
4940 ULARGE_INTEGER result;
4941 result.u.HighPart = 0;
4944 BlockChainStream_GetCount(This) *
4945 This->parentStorage->bigBlockSize;
4951 /******************************************************************************
4952 ** SmallBlockChainStream implementation
4955 SmallBlockChainStream* SmallBlockChainStream_Construct(
4956 StorageImpl* parentStorage,
4957 ULONG propertyIndex)
4959 SmallBlockChainStream* newStream;
4961 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4963 newStream->parentStorage = parentStorage;
4964 newStream->ownerPropertyIndex = propertyIndex;
4969 void SmallBlockChainStream_Destroy(
4970 SmallBlockChainStream* This)
4972 HeapFree(GetProcessHeap(), 0, This);
4975 /******************************************************************************
4976 * SmallBlockChainStream_GetHeadOfChain
4978 * Returns the head of this chain of small blocks.
4980 static ULONG SmallBlockChainStream_GetHeadOfChain(
4981 SmallBlockChainStream* This)
4983 StgProperty chainProperty;
4984 BOOL readSuccessful;
4986 if (This->ownerPropertyIndex)
4988 readSuccessful = StorageImpl_ReadProperty(
4989 This->parentStorage,
4990 This->ownerPropertyIndex,
4995 return chainProperty.startingBlock;
5000 return BLOCK_END_OF_CHAIN;
5003 /******************************************************************************
5004 * SmallBlockChainStream_GetNextBlockInChain
5006 * Returns the index of the next small block in this chain.
5009 * - BLOCK_END_OF_CHAIN: end of this chain
5010 * - BLOCK_UNUSED: small block 'blockIndex' is free
5012 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
5013 SmallBlockChainStream* This,
5015 ULONG* nextBlockInChain)
5017 ULARGE_INTEGER offsetOfBlockInDepot;
5022 *nextBlockInChain = BLOCK_END_OF_CHAIN;
5024 offsetOfBlockInDepot.u.HighPart = 0;
5025 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5028 * Read those bytes in the buffer from the small block file.
5030 res = BlockChainStream_ReadAt(
5031 This->parentStorage->smallBlockDepotChain,
5032 offsetOfBlockInDepot,
5039 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
5046 /******************************************************************************
5047 * SmallBlockChainStream_SetNextBlockInChain
5049 * Writes the index of the next block of the specified block in the small
5051 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5052 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5054 static void SmallBlockChainStream_SetNextBlockInChain(
5055 SmallBlockChainStream* This,
5059 ULARGE_INTEGER offsetOfBlockInDepot;
5063 offsetOfBlockInDepot.u.HighPart = 0;
5064 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5066 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
5069 * Read those bytes in the buffer from the small block file.
5071 BlockChainStream_WriteAt(
5072 This->parentStorage->smallBlockDepotChain,
5073 offsetOfBlockInDepot,
5079 /******************************************************************************
5080 * SmallBlockChainStream_FreeBlock
5082 * Flag small block 'blockIndex' as free in the small block depot.
5084 static void SmallBlockChainStream_FreeBlock(
5085 SmallBlockChainStream* This,
5088 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5091 /******************************************************************************
5092 * SmallBlockChainStream_GetNextFreeBlock
5094 * Returns the index of a free small block. The small block depot will be
5095 * enlarged if necessary. The small block chain will also be enlarged if
5098 static ULONG SmallBlockChainStream_GetNextFreeBlock(
5099 SmallBlockChainStream* This)
5101 ULARGE_INTEGER offsetOfBlockInDepot;
5104 ULONG blockIndex = 0;
5105 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5107 ULONG smallBlocksPerBigBlock;
5109 offsetOfBlockInDepot.u.HighPart = 0;
5112 * Scan the small block depot for a free block
5114 while (nextBlockIndex != BLOCK_UNUSED)
5116 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5118 res = BlockChainStream_ReadAt(
5119 This->parentStorage->smallBlockDepotChain,
5120 offsetOfBlockInDepot,
5126 * If we run out of space for the small block depot, enlarge it
5130 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5132 if (nextBlockIndex != BLOCK_UNUSED)
5138 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5140 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5141 ULONG nextBlock, newsbdIndex;
5142 BYTE smallBlockDepot[BIG_BLOCK_SIZE];
5144 nextBlock = sbdIndex;
5145 while (nextBlock != BLOCK_END_OF_CHAIN)
5147 sbdIndex = nextBlock;
5148 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5151 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5152 if (sbdIndex != BLOCK_END_OF_CHAIN)
5153 StorageImpl_SetNextBlockInChain(
5154 This->parentStorage,
5158 StorageImpl_SetNextBlockInChain(
5159 This->parentStorage,
5161 BLOCK_END_OF_CHAIN);
5164 * Initialize all the small blocks to free
5166 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5167 StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
5172 * We have just created the small block depot.
5174 StgProperty rootProp;
5178 * Save it in the header
5180 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5181 StorageImpl_SaveFileHeader(This->parentStorage);
5184 * And allocate the first big block that will contain small blocks
5187 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5189 StorageImpl_SetNextBlockInChain(
5190 This->parentStorage,
5192 BLOCK_END_OF_CHAIN);
5194 StorageImpl_ReadProperty(
5195 This->parentStorage,
5196 This->parentStorage->base.rootPropertySetIndex,
5199 rootProp.startingBlock = sbStartIndex;
5200 rootProp.size.u.HighPart = 0;
5201 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
5203 StorageImpl_WriteProperty(
5204 This->parentStorage,
5205 This->parentStorage->base.rootPropertySetIndex,
5209 StorageImpl_SaveFileHeader(This->parentStorage);
5213 smallBlocksPerBigBlock =
5214 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5217 * Verify if we have to allocate big blocks to contain small blocks
5219 if (blockIndex % smallBlocksPerBigBlock == 0)
5221 StgProperty rootProp;
5222 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5224 StorageImpl_ReadProperty(
5225 This->parentStorage,
5226 This->parentStorage->base.rootPropertySetIndex,
5229 if (rootProp.size.u.LowPart <
5230 (blocksRequired * This->parentStorage->bigBlockSize))
5232 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5234 BlockChainStream_SetSize(
5235 This->parentStorage->smallBlockRootChain,
5238 StorageImpl_WriteProperty(
5239 This->parentStorage,
5240 This->parentStorage->base.rootPropertySetIndex,
5248 /******************************************************************************
5249 * SmallBlockChainStream_ReadAt
5251 * Reads a specified number of bytes from this chain at the specified offset.
5252 * bytesRead may be NULL.
5253 * Failure will be returned if the specified number of bytes has not been read.
5255 HRESULT SmallBlockChainStream_ReadAt(
5256 SmallBlockChainStream* This,
5257 ULARGE_INTEGER offset,
5263 ULARGE_INTEGER offsetInBigBlockFile;
5264 ULONG blockNoInSequence =
5265 offset.u.LowPart / This->parentStorage->smallBlockSize;
5267 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5268 ULONG bytesToReadInBuffer;
5270 ULONG bytesReadFromBigBlockFile;
5274 * This should never happen on a small block file.
5276 assert(offset.u.HighPart==0);
5279 * Find the first block in the stream that contains part of the buffer.
5281 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5283 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5285 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5288 blockNoInSequence--;
5292 * Start reading the buffer.
5295 bufferWalker = buffer;
5297 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5300 * Calculate how many bytes we can copy from this small block.
5302 bytesToReadInBuffer =
5303 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5306 * Calculate the offset of the small block in the small block file.
5308 offsetInBigBlockFile.u.HighPart = 0;
5309 offsetInBigBlockFile.u.LowPart =
5310 blockIndex * This->parentStorage->smallBlockSize;
5312 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5315 * Read those bytes in the buffer from the small block file.
5316 * The small block has already been identified so it shouldn't fail
5317 * unless the file is corrupt.
5319 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5320 offsetInBigBlockFile,
5321 bytesToReadInBuffer,
5323 &bytesReadFromBigBlockFile);
5329 * Step to the next big block.
5331 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5333 return STG_E_DOCFILECORRUPT;
5335 bufferWalker += bytesReadFromBigBlockFile;
5336 size -= bytesReadFromBigBlockFile;
5337 *bytesRead += bytesReadFromBigBlockFile;
5338 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5341 return (size == 0) ? S_OK : STG_E_READFAULT;
5344 /******************************************************************************
5345 * SmallBlockChainStream_WriteAt
5347 * Writes the specified number of bytes to this chain at the specified offset.
5348 * bytesWritten may be NULL.
5349 * Will fail if not all specified number of bytes have been written.
5351 HRESULT SmallBlockChainStream_WriteAt(
5352 SmallBlockChainStream* This,
5353 ULARGE_INTEGER offset,
5356 ULONG* bytesWritten)
5358 ULARGE_INTEGER offsetInBigBlockFile;
5359 ULONG blockNoInSequence =
5360 offset.u.LowPart / This->parentStorage->smallBlockSize;
5362 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5363 ULONG bytesToWriteInBuffer;
5365 ULONG bytesWrittenToBigBlockFile;
5366 const BYTE* bufferWalker;
5370 * This should never happen on a small block file.
5372 assert(offset.u.HighPart==0);
5375 * Find the first block in the stream that contains part of the buffer.
5377 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5379 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5381 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5382 return STG_E_DOCFILECORRUPT;
5383 blockNoInSequence--;
5387 * Start writing the buffer.
5389 * Here, I'm casting away the constness on the buffer variable
5390 * This is OK since we don't intend to modify that buffer.
5393 bufferWalker = (const BYTE*)buffer;
5394 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5397 * Calculate how many bytes we can copy to this small block.
5399 bytesToWriteInBuffer =
5400 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5403 * Calculate the offset of the small block in the small block file.
5405 offsetInBigBlockFile.u.HighPart = 0;
5406 offsetInBigBlockFile.u.LowPart =
5407 blockIndex * This->parentStorage->smallBlockSize;
5409 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5412 * Write those bytes in the buffer to the small block file.
5414 res = BlockChainStream_WriteAt(
5415 This->parentStorage->smallBlockRootChain,
5416 offsetInBigBlockFile,
5417 bytesToWriteInBuffer,
5419 &bytesWrittenToBigBlockFile);
5424 * Step to the next big block.
5426 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5429 bufferWalker += bytesWrittenToBigBlockFile;
5430 size -= bytesWrittenToBigBlockFile;
5431 *bytesWritten += bytesWrittenToBigBlockFile;
5432 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
5435 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5438 /******************************************************************************
5439 * SmallBlockChainStream_Shrink
5441 * Shrinks this chain in the small block depot.
5443 static BOOL SmallBlockChainStream_Shrink(
5444 SmallBlockChainStream* This,
5445 ULARGE_INTEGER newSize)
5447 ULONG blockIndex, extraBlock;
5451 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5453 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5456 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5459 * Go to the new end of chain
5461 while (count < numBlocks)
5463 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5470 * If the count is 0, we have a special case, the head of the chain was
5475 StgProperty chainProp;
5477 StorageImpl_ReadProperty(This->parentStorage,
5478 This->ownerPropertyIndex,
5481 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5483 StorageImpl_WriteProperty(This->parentStorage,
5484 This->ownerPropertyIndex,
5488 * We start freeing the chain at the head block.
5490 extraBlock = blockIndex;
5494 /* Get the next block before marking the new end */
5495 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5499 /* Mark the new end of chain */
5500 SmallBlockChainStream_SetNextBlockInChain(
5503 BLOCK_END_OF_CHAIN);
5507 * Mark the extra blocks as free
5509 while (extraBlock != BLOCK_END_OF_CHAIN)
5511 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5514 SmallBlockChainStream_FreeBlock(This, extraBlock);
5515 extraBlock = blockIndex;
5521 /******************************************************************************
5522 * SmallBlockChainStream_Enlarge
5524 * Grows this chain in the small block depot.
5526 static BOOL SmallBlockChainStream_Enlarge(
5527 SmallBlockChainStream* This,
5528 ULARGE_INTEGER newSize)
5530 ULONG blockIndex, currentBlock;
5532 ULONG oldNumBlocks = 0;
5534 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5539 if (blockIndex == BLOCK_END_OF_CHAIN)
5542 StgProperty chainProp;
5544 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5547 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5549 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5552 blockIndex = chainProp.startingBlock;
5553 SmallBlockChainStream_SetNextBlockInChain(
5556 BLOCK_END_OF_CHAIN);
5559 currentBlock = blockIndex;
5562 * Figure out how many blocks are needed to contain this stream
5564 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5566 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5570 * Go to the current end of chain
5572 while (blockIndex != BLOCK_END_OF_CHAIN)
5575 currentBlock = blockIndex;
5576 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5581 * Add new blocks to the chain
5583 while (oldNumBlocks < newNumBlocks)
5585 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5586 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5588 SmallBlockChainStream_SetNextBlockInChain(
5591 BLOCK_END_OF_CHAIN);
5593 currentBlock = blockIndex;
5600 /******************************************************************************
5601 * SmallBlockChainStream_SetSize
5603 * Sets the size of this stream.
5604 * The file will grow if we grow the chain.
5606 * TODO: Free the actual blocks in the file when we shrink the chain.
5607 * Currently, the blocks are still in the file. So the file size
5608 * doesn't shrink even if we shrink streams.
5610 BOOL SmallBlockChainStream_SetSize(
5611 SmallBlockChainStream* This,
5612 ULARGE_INTEGER newSize)
5614 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5616 if (newSize.u.LowPart == size.u.LowPart)
5619 if (newSize.u.LowPart < size.u.LowPart)
5621 SmallBlockChainStream_Shrink(This, newSize);
5625 SmallBlockChainStream_Enlarge(This, newSize);
5631 /******************************************************************************
5632 * SmallBlockChainStream_GetSize
5634 * Returns the size of this chain.
5636 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5638 StgProperty chainProperty;
5640 StorageImpl_ReadProperty(
5641 This->parentStorage,
5642 This->ownerPropertyIndex,
5645 return chainProperty.size;
5648 /******************************************************************************
5649 * StgCreateDocfile [OLE32.@]
5650 * Creates a new compound file storage object
5653 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5654 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5655 * reserved [ ?] unused?, usually 0
5656 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5659 * S_OK if the file was successfully created
5660 * some STG_E_ value if error
5662 * if pwcsName is NULL, create file with new unique name
5663 * the function can returns
5664 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5667 HRESULT WINAPI StgCreateDocfile(
5671 IStorage **ppstgOpen)
5673 StorageImpl* newStorage = 0;
5674 HANDLE hFile = INVALID_HANDLE_VALUE;
5675 HRESULT hr = STG_E_INVALIDFLAG;
5679 DWORD fileAttributes;
5680 WCHAR tempFileName[MAX_PATH];
5682 TRACE("(%s, %x, %d, %p)\n",
5683 debugstr_w(pwcsName), grfMode,
5684 reserved, ppstgOpen);
5687 * Validate the parameters
5690 return STG_E_INVALIDPOINTER;
5692 return STG_E_INVALIDPARAMETER;
5694 /* if no share mode given then DENY_NONE is the default */
5695 if (STGM_SHARE_MODE(grfMode) == 0)
5696 grfMode |= STGM_SHARE_DENY_NONE;
5699 * Validate the STGM flags
5701 if ( FAILED( validateSTGM(grfMode) ))
5704 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5705 switch(STGM_ACCESS_MODE(grfMode))
5708 case STGM_READWRITE:
5714 /* in direct mode, can only use SHARE_EXCLUSIVE */
5715 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5718 /* but in transacted mode, any share mode is valid */
5721 * Generate a unique name.
5725 WCHAR tempPath[MAX_PATH];
5726 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5728 memset(tempPath, 0, sizeof(tempPath));
5729 memset(tempFileName, 0, sizeof(tempFileName));
5731 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5734 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5735 pwcsName = tempFileName;
5738 hr = STG_E_INSUFFICIENTMEMORY;
5742 creationMode = TRUNCATE_EXISTING;
5746 creationMode = GetCreationModeFromSTGM(grfMode);
5750 * Interpret the STGM value grfMode
5752 shareMode = GetShareModeFromSTGM(grfMode);
5753 accessMode = GetAccessModeFromSTGM(grfMode);
5755 if (grfMode & STGM_DELETEONRELEASE)
5756 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5758 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5760 if (grfMode & STGM_TRANSACTED)
5761 FIXME("Transacted mode not implemented.\n");
5764 * Initialize the "out" parameter.
5768 hFile = CreateFileW(pwcsName,
5776 if (hFile == INVALID_HANDLE_VALUE)
5778 if(GetLastError() == ERROR_FILE_EXISTS)
5779 hr = STG_E_FILEALREADYEXISTS;
5786 * Allocate and initialize the new IStorage32object.
5788 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5790 if (newStorage == 0)
5792 hr = STG_E_INSUFFICIENTMEMORY;
5796 hr = StorageImpl_Construct(
5807 HeapFree(GetProcessHeap(), 0, newStorage);
5812 * Get an "out" pointer for the caller.
5814 hr = StorageBaseImpl_QueryInterface(
5815 (IStorage*)newStorage,
5816 (REFIID)&IID_IStorage,
5819 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
5824 /******************************************************************************
5825 * StgCreateStorageEx [OLE32.@]
5827 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5829 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5830 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5832 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5834 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5835 return STG_E_INVALIDPARAMETER;
5838 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5840 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5841 return STG_E_INVALIDPARAMETER;
5844 if (stgfmt == STGFMT_FILE)
5846 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5847 return STG_E_INVALIDPARAMETER;
5850 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5852 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5853 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5856 ERR("Invalid stgfmt argument\n");
5857 return STG_E_INVALIDPARAMETER;
5860 /******************************************************************************
5861 * StgCreatePropSetStg [OLE32.@]
5863 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5864 IPropertySetStorage **ppPropSetStg)
5868 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
5870 hr = STG_E_INVALIDPARAMETER;
5872 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5873 (void**)ppPropSetStg);
5877 /******************************************************************************
5878 * StgOpenStorageEx [OLE32.@]
5880 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5882 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5883 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5885 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5887 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5888 return STG_E_INVALIDPARAMETER;
5894 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5895 return STG_E_INVALIDPARAMETER;
5897 case STGFMT_STORAGE:
5900 case STGFMT_DOCFILE:
5901 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
5903 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5904 return STG_E_INVALIDPARAMETER;
5906 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5910 WARN("STGFMT_ANY assuming storage\n");
5914 return STG_E_INVALIDPARAMETER;
5917 return StgOpenStorage(pwcsName, NULL, grfMode, (SNB)NULL, 0, (IStorage **)ppObjectOpen);
5921 /******************************************************************************
5922 * StgOpenStorage [OLE32.@]
5924 HRESULT WINAPI StgOpenStorage(
5925 const OLECHAR *pwcsName,
5926 IStorage *pstgPriority,
5930 IStorage **ppstgOpen)
5932 StorageImpl* newStorage = 0;
5937 WCHAR fullname[MAX_PATH];
5939 TRACE("(%s, %p, %x, %p, %d, %p)\n",
5940 debugstr_w(pwcsName), pstgPriority, grfMode,
5941 snbExclude, reserved, ppstgOpen);
5944 * Perform sanity checks
5948 hr = STG_E_INVALIDNAME;
5954 hr = STG_E_INVALIDPOINTER;
5960 hr = STG_E_INVALIDPARAMETER;
5964 if (grfMode & STGM_PRIORITY)
5966 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
5967 return STG_E_INVALIDFLAG;
5968 if (grfMode & STGM_DELETEONRELEASE)
5969 return STG_E_INVALIDFUNCTION;
5970 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
5971 return STG_E_INVALIDFLAG;
5972 grfMode &= ~0xf0; /* remove the existing sharing mode */
5973 grfMode |= STGM_SHARE_DENY_NONE;
5975 /* STGM_PRIORITY stops other IStorage objects on the same file from
5976 * committing until the STGM_PRIORITY IStorage is closed. it also
5977 * stops non-transacted mode StgOpenStorage calls with write access from
5978 * succeeding. obviously, both of these cannot be achieved through just
5979 * file share flags */
5980 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5984 * Validate the sharing mode
5986 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
5987 switch(STGM_SHARE_MODE(grfMode))
5989 case STGM_SHARE_EXCLUSIVE:
5990 case STGM_SHARE_DENY_WRITE:
5993 hr = STG_E_INVALIDFLAG;
5998 * Validate the STGM flags
6000 if ( FAILED( validateSTGM(grfMode) ) ||
6001 (grfMode&STGM_CREATE))
6003 hr = STG_E_INVALIDFLAG;
6007 /* shared reading requires transacted mode */
6008 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
6009 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
6010 !(grfMode&STGM_TRANSACTED) )
6012 hr = STG_E_INVALIDFLAG;
6017 * Interpret the STGM value grfMode
6019 shareMode = GetShareModeFromSTGM(grfMode);
6020 accessMode = GetAccessModeFromSTGM(grfMode);
6023 * Initialize the "out" parameter.
6027 hFile = CreateFileW( pwcsName,
6032 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
6035 if (hFile==INVALID_HANDLE_VALUE)
6037 DWORD last_error = GetLastError();
6043 case ERROR_FILE_NOT_FOUND:
6044 hr = STG_E_FILENOTFOUND;
6047 case ERROR_PATH_NOT_FOUND:
6048 hr = STG_E_PATHNOTFOUND;
6051 case ERROR_ACCESS_DENIED:
6052 case ERROR_WRITE_PROTECT:
6053 hr = STG_E_ACCESSDENIED;
6056 case ERROR_SHARING_VIOLATION:
6057 hr = STG_E_SHAREVIOLATION;
6068 * Refuse to open the file if it's too small to be a structured storage file
6069 * FIXME: verify the file when reading instead of here
6071 if (GetFileSize(hFile, NULL) < 0x100)
6074 hr = STG_E_FILEALREADYEXISTS;
6079 * Allocate and initialize the new IStorage32object.
6081 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6083 if (newStorage == 0)
6085 hr = STG_E_INSUFFICIENTMEMORY;
6089 /* Initialize the storage */
6090 hr = StorageImpl_Construct(
6101 HeapFree(GetProcessHeap(), 0, newStorage);
6103 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6105 if(hr == STG_E_INVALIDHEADER)
6106 hr = STG_E_FILEALREADYEXISTS;
6110 /* prepare the file name string given in lieu of the root property name */
6111 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
6112 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
6113 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
6116 * Get an "out" pointer for the caller.
6118 hr = StorageBaseImpl_QueryInterface(
6119 (IStorage*)newStorage,
6120 (REFIID)&IID_IStorage,
6124 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6128 /******************************************************************************
6129 * StgCreateDocfileOnILockBytes [OLE32.@]
6131 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6135 IStorage** ppstgOpen)
6137 StorageImpl* newStorage = 0;
6141 * Validate the parameters
6143 if ((ppstgOpen == 0) || (plkbyt == 0))
6144 return STG_E_INVALIDPOINTER;
6147 * Allocate and initialize the new IStorage object.
6149 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6151 if (newStorage == 0)
6152 return STG_E_INSUFFICIENTMEMORY;
6154 hr = StorageImpl_Construct(
6165 HeapFree(GetProcessHeap(), 0, newStorage);
6170 * Get an "out" pointer for the caller.
6172 hr = StorageBaseImpl_QueryInterface(
6173 (IStorage*)newStorage,
6174 (REFIID)&IID_IStorage,
6180 /******************************************************************************
6181 * StgOpenStorageOnILockBytes [OLE32.@]
6183 HRESULT WINAPI StgOpenStorageOnILockBytes(
6185 IStorage *pstgPriority,
6189 IStorage **ppstgOpen)
6191 StorageImpl* newStorage = 0;
6195 * Perform a sanity check
6197 if ((plkbyt == 0) || (ppstgOpen == 0))
6198 return STG_E_INVALIDPOINTER;
6201 * Validate the STGM flags
6203 if ( FAILED( validateSTGM(grfMode) ))
6204 return STG_E_INVALIDFLAG;
6207 * Initialize the "out" parameter.
6212 * Allocate and initialize the new IStorage object.
6214 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6216 if (newStorage == 0)
6217 return STG_E_INSUFFICIENTMEMORY;
6219 hr = StorageImpl_Construct(
6230 HeapFree(GetProcessHeap(), 0, newStorage);
6235 * Get an "out" pointer for the caller.
6237 hr = StorageBaseImpl_QueryInterface(
6238 (IStorage*)newStorage,
6239 (REFIID)&IID_IStorage,
6245 /******************************************************************************
6246 * StgSetTimes [ole32.@]
6247 * StgSetTimes [OLE32.@]
6251 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6252 FILETIME const *patime, FILETIME const *pmtime)
6254 IStorage *stg = NULL;
6257 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6259 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6263 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6264 IStorage_Release(stg);
6270 /******************************************************************************
6271 * StgIsStorageILockBytes [OLE32.@]
6273 * Determines if the ILockBytes contains a storage object.
6275 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6278 ULARGE_INTEGER offset;
6280 offset.u.HighPart = 0;
6281 offset.u.LowPart = 0;
6283 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6285 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6291 /******************************************************************************
6292 * WriteClassStg [OLE32.@]
6294 * This method will store the specified CLSID in the specified storage object
6296 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6301 return E_INVALIDARG;
6303 hRes = IStorage_SetClass(pStg, rclsid);
6308 /***********************************************************************
6309 * ReadClassStg (OLE32.@)
6311 * This method reads the CLSID previously written to a storage object with
6312 * the WriteClassStg.
6315 * pstg [I] IStorage pointer
6316 * pclsid [O] Pointer to where the CLSID is written
6320 * Failure: HRESULT code.
6322 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6327 TRACE("(%p, %p)\n", pstg, pclsid);
6329 if(!pstg || !pclsid)
6330 return E_INVALIDARG;
6333 * read a STATSTG structure (contains the clsid) from the storage
6335 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
6338 *pclsid=pstatstg.clsid;
6343 /***********************************************************************
6344 * OleLoadFromStream (OLE32.@)
6346 * This function loads an object from stream
6348 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6352 LPPERSISTSTREAM xstm;
6354 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6356 res=ReadClassStm(pStm,&clsid);
6357 if (!SUCCEEDED(res))
6359 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6360 if (!SUCCEEDED(res))
6362 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6363 if (!SUCCEEDED(res)) {
6364 IUnknown_Release((IUnknown*)*ppvObj);
6367 res=IPersistStream_Load(xstm,pStm);
6368 IPersistStream_Release(xstm);
6369 /* FIXME: all refcounts ok at this point? I think they should be:
6372 * xstm : 0 (released)
6377 /***********************************************************************
6378 * OleSaveToStream (OLE32.@)
6380 * This function saves an object with the IPersistStream interface on it
6381 * to the specified stream.
6383 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6389 TRACE("(%p,%p)\n",pPStm,pStm);
6391 res=IPersistStream_GetClassID(pPStm,&clsid);
6393 if (SUCCEEDED(res)){
6395 res=WriteClassStm(pStm,&clsid);
6399 res=IPersistStream_Save(pPStm,pStm,TRUE);
6402 TRACE("Finished Save\n");
6406 /****************************************************************************
6407 * This method validate a STGM parameter that can contain the values below
6409 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6410 * The stgm values contained in 0xffff0000 are bitmasks.
6412 * STGM_DIRECT 0x00000000
6413 * STGM_TRANSACTED 0x00010000
6414 * STGM_SIMPLE 0x08000000
6416 * STGM_READ 0x00000000
6417 * STGM_WRITE 0x00000001
6418 * STGM_READWRITE 0x00000002
6420 * STGM_SHARE_DENY_NONE 0x00000040
6421 * STGM_SHARE_DENY_READ 0x00000030
6422 * STGM_SHARE_DENY_WRITE 0x00000020
6423 * STGM_SHARE_EXCLUSIVE 0x00000010
6425 * STGM_PRIORITY 0x00040000
6426 * STGM_DELETEONRELEASE 0x04000000
6428 * STGM_CREATE 0x00001000
6429 * STGM_CONVERT 0x00020000
6430 * STGM_FAILIFTHERE 0x00000000
6432 * STGM_NOSCRATCH 0x00100000
6433 * STGM_NOSNAPSHOT 0x00200000
6435 static HRESULT validateSTGM(DWORD stgm)
6437 DWORD access = STGM_ACCESS_MODE(stgm);
6438 DWORD share = STGM_SHARE_MODE(stgm);
6439 DWORD create = STGM_CREATE_MODE(stgm);
6441 if (stgm&~STGM_KNOWN_FLAGS)
6443 ERR("unknown flags %08x\n", stgm);
6451 case STGM_READWRITE:
6459 case STGM_SHARE_DENY_NONE:
6460 case STGM_SHARE_DENY_READ:
6461 case STGM_SHARE_DENY_WRITE:
6462 case STGM_SHARE_EXCLUSIVE:
6471 case STGM_FAILIFTHERE:
6478 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6480 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6484 * STGM_CREATE | STGM_CONVERT
6485 * if both are false, STGM_FAILIFTHERE is set to TRUE
6487 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6491 * STGM_NOSCRATCH requires STGM_TRANSACTED
6493 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6497 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6498 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6500 if ( (stgm & STGM_NOSNAPSHOT) &&
6501 (!(stgm & STGM_TRANSACTED) ||
6502 share == STGM_SHARE_EXCLUSIVE ||
6503 share == STGM_SHARE_DENY_WRITE) )
6509 /****************************************************************************
6510 * GetShareModeFromSTGM
6512 * This method will return a share mode flag from a STGM value.
6513 * The STGM value is assumed valid.
6515 static DWORD GetShareModeFromSTGM(DWORD stgm)
6517 switch (STGM_SHARE_MODE(stgm))
6519 case STGM_SHARE_DENY_NONE:
6520 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6521 case STGM_SHARE_DENY_READ:
6522 return FILE_SHARE_WRITE;
6523 case STGM_SHARE_DENY_WRITE:
6524 return FILE_SHARE_READ;
6525 case STGM_SHARE_EXCLUSIVE:
6528 ERR("Invalid share mode!\n");
6533 /****************************************************************************
6534 * GetAccessModeFromSTGM
6536 * This method will return an access mode flag from a STGM value.
6537 * The STGM value is assumed valid.
6539 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6541 switch (STGM_ACCESS_MODE(stgm))
6544 return GENERIC_READ;
6546 case STGM_READWRITE:
6547 return GENERIC_READ | GENERIC_WRITE;
6549 ERR("Invalid access mode!\n");
6554 /****************************************************************************
6555 * GetCreationModeFromSTGM
6557 * This method will return a creation mode flag from a STGM value.
6558 * The STGM value is assumed valid.
6560 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6562 switch(STGM_CREATE_MODE(stgm))
6565 return CREATE_ALWAYS;
6567 FIXME("STGM_CONVERT not implemented!\n");
6569 case STGM_FAILIFTHERE:
6572 ERR("Invalid create mode!\n");
6578 /*************************************************************************
6579 * OLECONVERT_LoadOLE10 [Internal]
6581 * Loads the OLE10 STREAM to memory
6584 * pOleStream [I] The OLESTREAM
6585 * pData [I] Data Structure for the OLESTREAM Data
6589 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6590 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6593 * This function is used by OleConvertOLESTREAMToIStorage only.
6595 * Memory allocated for pData must be freed by the caller
6597 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6600 HRESULT hRes = S_OK;
6604 pData->pData = NULL;
6605 pData->pstrOleObjFileName = (CHAR *) NULL;
6607 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6610 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6611 if(dwSize != sizeof(pData->dwOleID))
6613 hRes = CONVERT10_E_OLESTREAM_GET;
6615 else if(pData->dwOleID != OLESTREAM_ID)
6617 hRes = CONVERT10_E_OLESTREAM_FMT;
6628 /* Get the TypeID...more info needed for this field */
6629 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6630 if(dwSize != sizeof(pData->dwTypeID))
6632 hRes = CONVERT10_E_OLESTREAM_GET;
6637 if(pData->dwTypeID != 0)
6639 /* Get the length of the OleTypeName */
6640 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6641 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6643 hRes = CONVERT10_E_OLESTREAM_GET;
6648 if(pData->dwOleTypeNameLength > 0)
6650 /* Get the OleTypeName */
6651 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6652 if(dwSize != pData->dwOleTypeNameLength)
6654 hRes = CONVERT10_E_OLESTREAM_GET;
6660 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6661 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6663 hRes = CONVERT10_E_OLESTREAM_GET;
6667 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6668 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6669 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6670 if(pData->pstrOleObjFileName)
6672 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6673 if(dwSize != pData->dwOleObjFileNameLength)
6675 hRes = CONVERT10_E_OLESTREAM_GET;
6679 hRes = CONVERT10_E_OLESTREAM_GET;
6684 /* Get the Width of the Metafile */
6685 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6686 if(dwSize != sizeof(pData->dwMetaFileWidth))
6688 hRes = CONVERT10_E_OLESTREAM_GET;
6692 /* Get the Height of the Metafile */
6693 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6694 if(dwSize != sizeof(pData->dwMetaFileHeight))
6696 hRes = CONVERT10_E_OLESTREAM_GET;
6702 /* Get the Length of the Data */
6703 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6704 if(dwSize != sizeof(pData->dwDataLength))
6706 hRes = CONVERT10_E_OLESTREAM_GET;
6710 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6712 if(!bStrem1) /* if it is a second OLE stream data */
6714 pData->dwDataLength -= 8;
6715 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6716 if(dwSize != sizeof(pData->strUnknown))
6718 hRes = CONVERT10_E_OLESTREAM_GET;
6724 if(pData->dwDataLength > 0)
6726 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6728 /* Get Data (ex. IStorage, Metafile, or BMP) */
6731 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6732 if(dwSize != pData->dwDataLength)
6734 hRes = CONVERT10_E_OLESTREAM_GET;
6739 hRes = CONVERT10_E_OLESTREAM_GET;
6748 /*************************************************************************
6749 * OLECONVERT_SaveOLE10 [Internal]
6751 * Saves the OLE10 STREAM From memory
6754 * pData [I] Data Structure for the OLESTREAM Data
6755 * pOleStream [I] The OLESTREAM to save
6759 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6762 * This function is used by OleConvertIStorageToOLESTREAM only.
6765 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6768 HRESULT hRes = S_OK;
6772 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6773 if(dwSize != sizeof(pData->dwOleID))
6775 hRes = CONVERT10_E_OLESTREAM_PUT;
6780 /* Set the TypeID */
6781 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6782 if(dwSize != sizeof(pData->dwTypeID))
6784 hRes = CONVERT10_E_OLESTREAM_PUT;
6788 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6790 /* Set the Length of the OleTypeName */
6791 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6792 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6794 hRes = CONVERT10_E_OLESTREAM_PUT;
6799 if(pData->dwOleTypeNameLength > 0)
6801 /* Set the OleTypeName */
6802 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6803 if(dwSize != pData->dwOleTypeNameLength)
6805 hRes = CONVERT10_E_OLESTREAM_PUT;
6812 /* Set the width of the Metafile */
6813 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6814 if(dwSize != sizeof(pData->dwMetaFileWidth))
6816 hRes = CONVERT10_E_OLESTREAM_PUT;
6822 /* Set the height of the Metafile */
6823 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6824 if(dwSize != sizeof(pData->dwMetaFileHeight))
6826 hRes = CONVERT10_E_OLESTREAM_PUT;
6832 /* Set the length of the Data */
6833 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6834 if(dwSize != sizeof(pData->dwDataLength))
6836 hRes = CONVERT10_E_OLESTREAM_PUT;
6842 if(pData->dwDataLength > 0)
6844 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6845 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6846 if(dwSize != pData->dwDataLength)
6848 hRes = CONVERT10_E_OLESTREAM_PUT;
6856 /*************************************************************************
6857 * OLECONVERT_GetOLE20FromOLE10[Internal]
6859 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6860 * opens it, and copies the content to the dest IStorage for
6861 * OleConvertOLESTREAMToIStorage
6865 * pDestStorage [I] The IStorage to copy the data to
6866 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6867 * nBufferLength [I] The size of the buffer
6876 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
6880 IStorage *pTempStorage;
6881 DWORD dwNumOfBytesWritten;
6882 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6883 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6885 /* Create a temp File */
6886 GetTempPathW(MAX_PATH, wstrTempDir);
6887 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6888 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6890 if(hFile != INVALID_HANDLE_VALUE)
6892 /* Write IStorage Data to File */
6893 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6896 /* Open and copy temp storage to the Dest Storage */
6897 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6900 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6901 StorageBaseImpl_Release(pTempStorage);
6903 DeleteFileW(wstrTempFile);
6908 /*************************************************************************
6909 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6911 * Saves the OLE10 STREAM From memory
6914 * pStorage [I] The Src IStorage to copy
6915 * pData [I] The Dest Memory to write to.
6918 * The size in bytes allocated for pData
6921 * Memory allocated for pData must be freed by the caller
6923 * Used by OleConvertIStorageToOLESTREAM only.
6926 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6930 DWORD nDataLength = 0;
6931 IStorage *pTempStorage;
6932 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6933 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6937 /* Create temp Storage */
6938 GetTempPathW(MAX_PATH, wstrTempDir);
6939 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6940 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6944 /* Copy Src Storage to the Temp Storage */
6945 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6946 StorageBaseImpl_Release(pTempStorage);
6948 /* Open Temp Storage as a file and copy to memory */
6949 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6950 if(hFile != INVALID_HANDLE_VALUE)
6952 nDataLength = GetFileSize(hFile, NULL);
6953 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6954 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6957 DeleteFileW(wstrTempFile);
6962 /*************************************************************************
6963 * OLECONVERT_CreateOleStream [Internal]
6965 * Creates the "\001OLE" stream in the IStorage if necessary.
6968 * pStorage [I] Dest storage to create the stream in
6974 * This function is used by OleConvertOLESTREAMToIStorage only.
6976 * This stream is still unknown, MS Word seems to have extra data
6977 * but since the data is stored in the OLESTREAM there should be
6978 * no need to recreate the stream. If the stream is manually
6979 * deleted it will create it with this default data.
6982 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6986 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6987 BYTE pOleStreamHeader [] =
6989 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6990 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6991 0x00, 0x00, 0x00, 0x00
6994 /* Create stream if not present */
6995 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6996 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7000 /* Write default Data */
7001 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
7002 IStream_Release(pStream);
7006 /* write a string to a stream, preceded by its length */
7007 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
7014 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
7015 r = IStream_Write( stm, &len, sizeof(len), NULL);
7020 str = CoTaskMemAlloc( len );
7021 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
7022 r = IStream_Write( stm, str, len, NULL);
7023 CoTaskMemFree( str );
7027 /* read a string preceded by its length from a stream */
7028 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
7031 DWORD len, count = 0;
7035 r = IStream_Read( stm, &len, sizeof(len), &count );
7038 if( count != sizeof(len) )
7039 return E_OUTOFMEMORY;
7041 TRACE("%d bytes\n",len);
7043 str = CoTaskMemAlloc( len );
7045 return E_OUTOFMEMORY;
7047 r = IStream_Read( stm, str, len, &count );
7052 CoTaskMemFree( str );
7053 return E_OUTOFMEMORY;
7056 TRACE("Read string %s\n",debugstr_an(str,len));
7058 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7059 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7061 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7062 CoTaskMemFree( str );
7070 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7071 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7075 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7077 static const BYTE unknown1[12] =
7078 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7079 0xFF, 0xFF, 0xFF, 0xFF};
7080 static const BYTE unknown2[16] =
7081 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7082 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7084 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7085 debugstr_w(lpszUserType), debugstr_w(szClipName),
7086 debugstr_w(szProgIDName));
7088 /* Create a CompObj stream if it doesn't exist */
7089 r = IStorage_CreateStream(pstg, szwStreamName,
7090 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7094 /* Write CompObj Structure to stream */
7095 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7097 if( SUCCEEDED( r ) )
7098 r = WriteClassStm( pstm, clsid );
7100 if( SUCCEEDED( r ) )
7101 r = STREAM_WriteString( pstm, lpszUserType );
7102 if( SUCCEEDED( r ) )
7103 r = STREAM_WriteString( pstm, szClipName );
7104 if( SUCCEEDED( r ) )
7105 r = STREAM_WriteString( pstm, szProgIDName );
7106 if( SUCCEEDED( r ) )
7107 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7109 IStream_Release( pstm );
7114 /***********************************************************************
7115 * WriteFmtUserTypeStg (OLE32.@)
7117 HRESULT WINAPI WriteFmtUserTypeStg(
7118 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7121 WCHAR szwClipName[0x40];
7122 CLSID clsid = CLSID_NULL;
7123 LPWSTR wstrProgID = NULL;
7126 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7128 /* get the clipboard format name */
7129 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
7132 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7134 /* FIXME: There's room to save a CLSID and its ProgID, but
7135 the CLSID is not looked up in the registry and in all the
7136 tests I wrote it was CLSID_NULL. Where does it come from?
7139 /* get the real program ID. This may fail, but that's fine */
7140 ProgIDFromCLSID(&clsid, &wstrProgID);
7142 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7144 r = STORAGE_WriteCompObj( pstg, &clsid,
7145 lpszUserType, szwClipName, wstrProgID );
7147 CoTaskMemFree(wstrProgID);
7153 /******************************************************************************
7154 * ReadFmtUserTypeStg [OLE32.@]
7156 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7160 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7161 unsigned char unknown1[12];
7162 unsigned char unknown2[16];
7164 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7167 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7169 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7170 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7173 WARN("Failed to open stream r = %08x\n", r);
7177 /* read the various parts of the structure */
7178 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7179 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7181 r = ReadClassStm( stm, &clsid );
7185 r = STREAM_ReadString( stm, &szCLSIDName );
7189 r = STREAM_ReadString( stm, &szOleTypeName );
7193 r = STREAM_ReadString( stm, &szProgIDName );
7197 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7198 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7201 /* ok, success... now we just need to store what we found */
7203 *pcf = RegisterClipboardFormatW( szOleTypeName );
7204 CoTaskMemFree( szOleTypeName );
7206 if( lplpszUserType )
7207 *lplpszUserType = szCLSIDName;
7208 CoTaskMemFree( szProgIDName );
7211 IStream_Release( stm );
7217 /*************************************************************************
7218 * OLECONVERT_CreateCompObjStream [Internal]
7220 * Creates a "\001CompObj" is the destination IStorage if necessary.
7223 * pStorage [I] The dest IStorage to create the CompObj Stream
7225 * strOleTypeName [I] The ProgID
7229 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7232 * This function is used by OleConvertOLESTREAMToIStorage only.
7234 * The stream data is stored in the OLESTREAM and there should be
7235 * no need to recreate the stream. If the stream is manually
7236 * deleted it will attempt to create it by querying the registry.
7240 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7243 HRESULT hStorageRes, hRes = S_OK;
7244 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7245 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7246 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7248 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7249 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7251 /* Initialize the CompObj structure */
7252 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7253 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
7254 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
7257 /* Create a CompObj stream if it doesn't exist */
7258 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7259 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7260 if(hStorageRes == S_OK)
7262 /* copy the OleTypeName to the compobj struct */
7263 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7264 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7266 /* copy the OleTypeName to the compobj struct */
7267 /* Note: in the test made, these were Identical */
7268 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7269 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7272 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7273 bufferW, OLESTREAM_MAX_STR_LEN );
7274 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7280 /* Get the CLSID Default Name from the Registry */
7281 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7282 if(hErr == ERROR_SUCCESS)
7284 char strTemp[OLESTREAM_MAX_STR_LEN];
7285 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7286 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7287 if(hErr == ERROR_SUCCESS)
7289 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7295 /* Write CompObj Structure to stream */
7296 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7298 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7300 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7301 if(IStorageCompObj.dwCLSIDNameLength > 0)
7303 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7305 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7306 if(IStorageCompObj.dwOleTypeNameLength > 0)
7308 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7310 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7311 if(IStorageCompObj.dwProgIDNameLength > 0)
7313 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7315 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7316 IStream_Release(pStream);
7322 /*************************************************************************
7323 * OLECONVERT_CreateOlePresStream[Internal]
7325 * Creates the "\002OlePres000" Stream with the Metafile data
7328 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7329 * dwExtentX [I] Width of the Metafile
7330 * dwExtentY [I] Height of the Metafile
7331 * pData [I] Metafile data
7332 * dwDataLength [I] Size of the Metafile data
7336 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7339 * This function is used by OleConvertOLESTREAMToIStorage only.
7342 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7346 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7347 BYTE pOlePresStreamHeader [] =
7349 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7350 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7351 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7352 0x00, 0x00, 0x00, 0x00
7355 BYTE pOlePresStreamHeaderEmpty [] =
7357 0x00, 0x00, 0x00, 0x00,
7358 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7359 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7360 0x00, 0x00, 0x00, 0x00
7363 /* Create the OlePres000 Stream */
7364 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7365 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7370 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7372 memset(&OlePres, 0, sizeof(OlePres));
7373 /* Do we have any metafile data to save */
7374 if(dwDataLength > 0)
7376 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7377 nHeaderSize = sizeof(pOlePresStreamHeader);
7381 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7382 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7384 /* Set width and height of the metafile */
7385 OlePres.dwExtentX = dwExtentX;
7386 OlePres.dwExtentY = -dwExtentY;
7388 /* Set Data and Length */
7389 if(dwDataLength > sizeof(METAFILEPICT16))
7391 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7392 OlePres.pData = &(pData[8]);
7394 /* Save OlePres000 Data to Stream */
7395 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7396 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7397 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7398 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7399 if(OlePres.dwSize > 0)
7401 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7403 IStream_Release(pStream);
7407 /*************************************************************************
7408 * OLECONVERT_CreateOle10NativeStream [Internal]
7410 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7413 * pStorage [I] Dest storage to create the stream in
7414 * pData [I] Ole10 Native Data (ex. bmp)
7415 * dwDataLength [I] Size of the Ole10 Native Data
7421 * This function is used by OleConvertOLESTREAMToIStorage only.
7423 * Might need to verify the data and return appropriate error message
7426 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
7430 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7432 /* Create the Ole10Native Stream */
7433 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7434 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7438 /* Write info to stream */
7439 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7440 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7441 IStream_Release(pStream);
7446 /*************************************************************************
7447 * OLECONVERT_GetOLE10ProgID [Internal]
7449 * Finds the ProgID (or OleTypeID) from the IStorage
7452 * pStorage [I] The Src IStorage to get the ProgID
7453 * strProgID [I] the ProgID string to get
7454 * dwSize [I] the size of the string
7458 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7461 * This function is used by OleConvertIStorageToOLESTREAM only.
7465 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7469 LARGE_INTEGER iSeekPos;
7470 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7471 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7473 /* Open the CompObj Stream */
7474 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7475 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7479 /*Get the OleType from the CompObj Stream */
7480 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7481 iSeekPos.u.HighPart = 0;
7483 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7484 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7485 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7486 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7487 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7488 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7489 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7491 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7494 IStream_Read(pStream, strProgID, *dwSize, NULL);
7496 IStream_Release(pStream);
7501 LPOLESTR wstrProgID;
7503 /* Get the OleType from the registry */
7504 REFCLSID clsid = &(stat.clsid);
7505 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7506 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7509 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7516 /*************************************************************************
7517 * OLECONVERT_GetOle10PresData [Internal]
7519 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7522 * pStorage [I] Src IStroage
7523 * pOleStream [I] Dest OleStream Mem Struct
7529 * This function is used by OleConvertIStorageToOLESTREAM only.
7531 * Memory allocated for pData must be freed by the caller
7535 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7540 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7542 /* Initialize Default data for OLESTREAM */
7543 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7544 pOleStreamData[0].dwTypeID = 2;
7545 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7546 pOleStreamData[1].dwTypeID = 0;
7547 pOleStreamData[0].dwMetaFileWidth = 0;
7548 pOleStreamData[0].dwMetaFileHeight = 0;
7549 pOleStreamData[0].pData = NULL;
7550 pOleStreamData[1].pData = NULL;
7552 /* Open Ole10Native Stream */
7553 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7554 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7558 /* Read Size and Data */
7559 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7560 if(pOleStreamData->dwDataLength > 0)
7562 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7563 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7565 IStream_Release(pStream);
7571 /*************************************************************************
7572 * OLECONVERT_GetOle20PresData[Internal]
7574 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7577 * pStorage [I] Src IStroage
7578 * pOleStreamData [I] Dest OleStream Mem Struct
7584 * This function is used by OleConvertIStorageToOLESTREAM only.
7586 * Memory allocated for pData must be freed by the caller
7588 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7592 OLECONVERT_ISTORAGE_OLEPRES olePress;
7593 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7595 /* Initialize Default data for OLESTREAM */
7596 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7597 pOleStreamData[0].dwTypeID = 2;
7598 pOleStreamData[0].dwMetaFileWidth = 0;
7599 pOleStreamData[0].dwMetaFileHeight = 0;
7600 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7601 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7602 pOleStreamData[1].dwTypeID = 0;
7603 pOleStreamData[1].dwOleTypeNameLength = 0;
7604 pOleStreamData[1].strOleTypeName[0] = 0;
7605 pOleStreamData[1].dwMetaFileWidth = 0;
7606 pOleStreamData[1].dwMetaFileHeight = 0;
7607 pOleStreamData[1].pData = NULL;
7608 pOleStreamData[1].dwDataLength = 0;
7611 /* Open OlePress000 stream */
7612 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7613 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7616 LARGE_INTEGER iSeekPos;
7617 METAFILEPICT16 MetaFilePict;
7618 static const char strMetafilePictName[] = "METAFILEPICT";
7620 /* Set the TypeID for a Metafile */
7621 pOleStreamData[1].dwTypeID = 5;
7623 /* Set the OleTypeName to Metafile */
7624 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7625 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7627 iSeekPos.u.HighPart = 0;
7628 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7630 /* Get Presentation Data */
7631 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7632 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7633 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7634 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7636 /*Set width and Height */
7637 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7638 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7639 if(olePress.dwSize > 0)
7642 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7644 /* Set MetaFilePict struct */
7645 MetaFilePict.mm = 8;
7646 MetaFilePict.xExt = olePress.dwExtentX;
7647 MetaFilePict.yExt = olePress.dwExtentY;
7648 MetaFilePict.hMF = 0;
7650 /* Get Metafile Data */
7651 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7652 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7653 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7655 IStream_Release(pStream);
7659 /*************************************************************************
7660 * OleConvertOLESTREAMToIStorage [OLE32.@]
7665 * DVTARGETDEVICE paramenter is not handled
7666 * Still unsure of some mem fields for OLE 10 Stream
7667 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7668 * and "\001OLE" streams
7671 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7672 LPOLESTREAM pOleStream,
7674 const DVTARGETDEVICE* ptd)
7678 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7680 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
7682 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7686 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7689 if(pstg == NULL || pOleStream == NULL)
7691 hRes = E_INVALIDARG;
7696 /* Load the OLESTREAM to Memory */
7697 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7702 /* Load the OLESTREAM to Memory (part 2)*/
7703 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7709 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7711 /* Do we have the IStorage Data in the OLESTREAM */
7712 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7714 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7715 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7719 /* It must be an original OLE 1.0 source */
7720 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7725 /* It must be an original OLE 1.0 source */
7726 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7729 /* Create CompObj Stream if necessary */
7730 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7733 /*Create the Ole Stream if necessary */
7734 OLECONVERT_CreateOleStream(pstg);
7739 /* Free allocated memory */
7740 for(i=0; i < 2; i++)
7742 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7743 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7744 pOleStreamData[i].pstrOleObjFileName = NULL;
7749 /*************************************************************************
7750 * OleConvertIStorageToOLESTREAM [OLE32.@]
7757 * Still unsure of some mem fields for OLE 10 Stream
7758 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7759 * and "\001OLE" streams.
7762 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7764 LPOLESTREAM pOleStream)
7767 HRESULT hRes = S_OK;
7769 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7770 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7772 TRACE("%p %p\n", pstg, pOleStream);
7774 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7776 if(pstg == NULL || pOleStream == NULL)
7778 hRes = E_INVALIDARG;
7782 /* Get the ProgID */
7783 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7784 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7788 /* Was it originally Ole10 */
7789 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7792 IStream_Release(pStream);
7793 /* Get Presentation Data for Ole10Native */
7794 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7798 /* Get Presentation Data (OLE20) */
7799 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7802 /* Save OLESTREAM */
7803 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7806 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7811 /* Free allocated memory */
7812 for(i=0; i < 2; i++)
7814 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7820 /***********************************************************************
7821 * GetConvertStg (OLE32.@)
7823 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7824 FIXME("unimplemented stub!\n");
7828 /******************************************************************************
7829 * StgIsStorageFile [OLE32.@]
7830 * Verify if the file contains a storage object
7836 * S_OK if file has magic bytes as a storage object
7837 * S_FALSE if file is not storage
7840 StgIsStorageFile(LPCOLESTR fn)
7846 TRACE("%s\n", debugstr_w(fn));
7847 hf = CreateFileW(fn, GENERIC_READ,
7848 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7849 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7851 if (hf == INVALID_HANDLE_VALUE)
7852 return STG_E_FILENOTFOUND;
7854 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7856 WARN(" unable to read file\n");
7863 if (bytes_read != 8) {
7864 WARN(" too short\n");
7868 if (!memcmp(magic,STORAGE_magic,8)) {
7873 WARN(" -> Invalid header.\n");
7877 /***********************************************************************
7878 * WriteClassStm (OLE32.@)
7880 * Writes a CLSID to a stream.
7883 * pStm [I] Stream to write to.
7884 * rclsid [I] CLSID to write.
7888 * Failure: HRESULT code.
7890 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
7892 TRACE("(%p,%p)\n",pStm,rclsid);
7894 if (!pStm || !rclsid)
7895 return E_INVALIDARG;
7897 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
7900 /***********************************************************************
7901 * ReadClassStm (OLE32.@)
7903 * Reads a CLSID from a stream.
7906 * pStm [I] Stream to read from.
7907 * rclsid [O] CLSID to read.
7911 * Failure: HRESULT code.
7913 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
7918 TRACE("(%p,%p)\n",pStm,pclsid);
7920 if (!pStm || !pclsid)
7921 return E_INVALIDARG;
7923 /* clear the output args */
7924 memcpy(pclsid, &CLSID_NULL, sizeof(*pclsid));
7926 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
7931 if (nbByte != sizeof(CLSID))
7932 return STG_E_READFAULT;