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 IStorage_DestroyElement(iface, pwcsName);
1021 return STG_E_FILEALREADYEXISTS;
1023 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1025 WARN("read-only storage\n");
1026 return STG_E_ACCESSDENIED;
1030 * memset the empty property
1032 memset(&newStreamProperty, 0, sizeof(StgProperty));
1034 newStreamProperty.sizeOfNameString =
1035 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
1037 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1038 return STG_E_INVALIDNAME;
1040 strcpyW(newStreamProperty.name, pwcsName);
1042 newStreamProperty.propertyType = PROPTYPE_STREAM;
1043 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
1044 newStreamProperty.size.u.LowPart = 0;
1045 newStreamProperty.size.u.HighPart = 0;
1047 newStreamProperty.previousProperty = PROPERTY_NULL;
1048 newStreamProperty.nextProperty = PROPERTY_NULL;
1049 newStreamProperty.dirProperty = PROPERTY_NULL;
1051 /* call CoFileTime to get the current time
1052 newStreamProperty.timeStampS1
1053 newStreamProperty.timeStampD1
1054 newStreamProperty.timeStampS2
1055 newStreamProperty.timeStampD2
1058 /* newStreamProperty.propertyUniqueID */
1061 * Get a free property or create a new one
1063 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1066 * Save the new property into the new property spot
1068 StorageImpl_WriteProperty(
1069 This->ancestorStorage,
1071 &newStreamProperty);
1074 * Find a spot in the property chain for our newly created property.
1076 updatePropertyChain(
1082 * Open the stream to return it.
1084 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
1088 *ppstm = (IStream*)newStream;
1091 * Since we are returning a pointer to the interface, we have to nail down
1094 IStream_AddRef(*ppstm);
1098 return STG_E_INSUFFICIENTMEMORY;
1104 /************************************************************************
1105 * Storage32BaseImpl_SetClass (IStorage)
1107 * This method will write the specified CLSID in the property of this
1110 * See Windows documentation for more details on IStorage methods.
1112 static HRESULT WINAPI StorageBaseImpl_SetClass(
1114 REFCLSID clsid) /* [in] */
1116 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1117 HRESULT hRes = E_FAIL;
1118 StgProperty curProperty;
1121 TRACE("(%p, %p)\n", iface, clsid);
1123 success = StorageImpl_ReadProperty(This->ancestorStorage,
1124 This->rootPropertySetIndex,
1128 curProperty.propertyUniqueID = *clsid;
1130 success = StorageImpl_WriteProperty(This->ancestorStorage,
1131 This->rootPropertySetIndex,
1140 /************************************************************************
1141 ** Storage32Impl implementation
1144 /************************************************************************
1145 * Storage32Impl_CreateStorage (IStorage)
1147 * This method will create the storage object within the provided storage.
1149 * See Windows documentation for more details on IStorage methods.
1151 static HRESULT WINAPI StorageImpl_CreateStorage(
1153 const OLECHAR *pwcsName, /* [string][in] */
1154 DWORD grfMode, /* [in] */
1155 DWORD reserved1, /* [in] */
1156 DWORD reserved2, /* [in] */
1157 IStorage **ppstg) /* [out] */
1159 StorageImpl* const This=(StorageImpl*)iface;
1161 IEnumSTATSTGImpl *propertyEnumeration;
1162 StgProperty currentProperty;
1163 StgProperty newProperty;
1164 ULONG foundPropertyIndex;
1165 ULONG newPropertyIndex;
1168 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1169 iface, debugstr_w(pwcsName), grfMode,
1170 reserved1, reserved2, ppstg);
1173 * Validate parameters
1176 return STG_E_INVALIDPOINTER;
1179 return STG_E_INVALIDNAME;
1182 * Initialize the out parameter
1187 * Validate the STGM flags
1189 if ( FAILED( validateSTGM(grfMode) ) ||
1190 (grfMode & STGM_DELETEONRELEASE) )
1192 WARN("bad grfMode: 0x%x\n", grfMode);
1193 return STG_E_INVALIDFLAG;
1197 * Check that we're compatible with the parent's storage mode
1199 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->base.openFlags ) )
1201 WARN("access denied\n");
1202 return STG_E_ACCESSDENIED;
1206 * Create a property enumeration and search the properties
1208 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1209 This->base.rootPropertySetIndex);
1211 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1214 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1216 if (foundPropertyIndex != PROPERTY_NULL)
1219 * An element with this name already exists
1221 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1222 IStorage_DestroyElement(iface, pwcsName);
1225 WARN("file already exists\n");
1226 return STG_E_FILEALREADYEXISTS;
1229 else if (STGM_ACCESS_MODE(This->base.openFlags) == STGM_READ)
1231 WARN("read-only storage\n");
1232 return STG_E_ACCESSDENIED;
1236 * memset the empty property
1238 memset(&newProperty, 0, sizeof(StgProperty));
1240 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1242 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1244 FIXME("name too long\n");
1245 return STG_E_INVALIDNAME;
1248 strcpyW(newProperty.name, pwcsName);
1250 newProperty.propertyType = PROPTYPE_STORAGE;
1251 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1252 newProperty.size.u.LowPart = 0;
1253 newProperty.size.u.HighPart = 0;
1255 newProperty.previousProperty = PROPERTY_NULL;
1256 newProperty.nextProperty = PROPERTY_NULL;
1257 newProperty.dirProperty = PROPERTY_NULL;
1259 /* call CoFileTime to get the current time
1260 newProperty.timeStampS1
1261 newProperty.timeStampD1
1262 newProperty.timeStampS2
1263 newProperty.timeStampD2
1266 /* newStorageProperty.propertyUniqueID */
1269 * Obtain a free property in the property chain
1271 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1274 * Save the new property into the new property spot
1276 StorageImpl_WriteProperty(
1277 This->base.ancestorStorage,
1282 * Find a spot in the property chain for our newly created property.
1284 updatePropertyChain(
1290 * Open it to get a pointer to return.
1292 hr = IStorage_OpenStorage(
1294 (const OLECHAR*)pwcsName,
1301 if( (hr != S_OK) || (*ppstg == NULL))
1311 /***************************************************************************
1315 * Get a free property or create a new one.
1317 static ULONG getFreeProperty(
1318 StorageImpl *storage)
1320 ULONG currentPropertyIndex = 0;
1321 ULONG newPropertyIndex = PROPERTY_NULL;
1322 BOOL readSuccessful = TRUE;
1323 StgProperty currentProperty;
1328 * Start by reading the root property
1330 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1331 currentPropertyIndex,
1335 if (currentProperty.sizeOfNameString == 0)
1338 * The property existis and is available, we found it.
1340 newPropertyIndex = currentPropertyIndex;
1346 * We exhausted the property list, we will create more space below
1348 newPropertyIndex = currentPropertyIndex;
1350 currentPropertyIndex++;
1352 } while (newPropertyIndex == PROPERTY_NULL);
1355 * grow the property chain
1357 if (! readSuccessful)
1359 StgProperty emptyProperty;
1360 ULARGE_INTEGER newSize;
1361 ULONG propertyIndex;
1362 ULONG lastProperty = 0;
1363 ULONG blockCount = 0;
1366 * obtain the new count of property blocks
1368 blockCount = BlockChainStream_GetCount(
1369 storage->base.ancestorStorage->rootBlockChain)+1;
1372 * initialize the size used by the property stream
1374 newSize.u.HighPart = 0;
1375 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1378 * add a property block to the property chain
1380 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1383 * memset the empty property in order to initialize the unused newly
1386 memset(&emptyProperty, 0, sizeof(StgProperty));
1391 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1394 propertyIndex = newPropertyIndex;
1395 propertyIndex < lastProperty;
1398 StorageImpl_WriteProperty(
1399 storage->base.ancestorStorage,
1405 return newPropertyIndex;
1408 /****************************************************************************
1412 * Case insensitive comparaison of StgProperty.name by first considering
1415 * Returns <0 when newPrpoerty < currentProperty
1416 * >0 when newPrpoerty > currentProperty
1417 * 0 when newPrpoerty == currentProperty
1419 static LONG propertyNameCmp(
1420 const OLECHAR *newProperty,
1421 const OLECHAR *currentProperty)
1423 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1428 * We compare the string themselves only when they are of the same length
1430 diff = lstrcmpiW( newProperty, currentProperty);
1436 /****************************************************************************
1440 * Properly link this new element in the property chain.
1442 static void updatePropertyChain(
1443 StorageImpl *storage,
1444 ULONG newPropertyIndex,
1445 StgProperty newProperty)
1447 StgProperty currentProperty;
1450 * Read the root property
1452 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1453 storage->base.rootPropertySetIndex,
1456 if (currentProperty.dirProperty != PROPERTY_NULL)
1459 * The root storage contains some element, therefore, start the research
1460 * for the appropriate location.
1463 ULONG current, next, previous, currentPropertyId;
1466 * Keep the StgProperty sequence number of the storage first property
1468 currentPropertyId = currentProperty.dirProperty;
1473 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1474 currentProperty.dirProperty,
1477 previous = currentProperty.previousProperty;
1478 next = currentProperty.nextProperty;
1479 current = currentPropertyId;
1483 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1487 if (previous != PROPERTY_NULL)
1489 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1496 currentProperty.previousProperty = newPropertyIndex;
1497 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1505 if (next != PROPERTY_NULL)
1507 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1514 currentProperty.nextProperty = newPropertyIndex;
1515 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1524 * Trying to insert an item with the same name in the
1525 * subtree structure.
1530 previous = currentProperty.previousProperty;
1531 next = currentProperty.nextProperty;
1537 * The root storage is empty, link the new property to its dir property
1539 currentProperty.dirProperty = newPropertyIndex;
1540 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1541 storage->base.rootPropertySetIndex,
1547 /*************************************************************************
1550 static HRESULT WINAPI StorageImpl_CopyTo(
1552 DWORD ciidExclude, /* [in] */
1553 const IID* rgiidExclude, /* [size_is][unique][in] */
1554 SNB snbExclude, /* [unique][in] */
1555 IStorage* pstgDest) /* [unique][in] */
1557 IEnumSTATSTG *elements = 0;
1558 STATSTG curElement, strStat;
1560 IStorage *pstgTmp, *pstgChild;
1561 IStream *pstrTmp, *pstrChild;
1563 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1564 FIXME("Exclude option not implemented\n");
1566 TRACE("(%p, %d, %p, %p, %p)\n",
1567 iface, ciidExclude, rgiidExclude,
1568 snbExclude, pstgDest);
1571 * Perform a sanity check
1573 if ( pstgDest == 0 )
1574 return STG_E_INVALIDPOINTER;
1577 * Enumerate the elements
1579 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1587 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1588 IStorage_SetClass( pstgDest, &curElement.clsid );
1593 * Obtain the next element
1595 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1597 if ( hr == S_FALSE )
1599 hr = S_OK; /* done, every element has been copied */
1603 if (curElement.type == STGTY_STORAGE)
1606 * open child source storage
1608 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1609 STGM_READ|STGM_SHARE_EXCLUSIVE,
1610 NULL, 0, &pstgChild );
1616 * Check if destination storage is not a child of the source
1617 * storage, which will cause an infinite loop
1619 if (pstgChild == pstgDest)
1621 IEnumSTATSTG_Release(elements);
1623 return STG_E_ACCESSDENIED;
1627 * create a new storage in destination storage
1629 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1630 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1634 * if it already exist, don't create a new one use this one
1636 if (hr == STG_E_FILEALREADYEXISTS)
1638 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1639 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1640 NULL, 0, &pstgTmp );
1648 * do the copy recursively
1650 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1651 snbExclude, pstgTmp );
1653 IStorage_Release( pstgTmp );
1654 IStorage_Release( pstgChild );
1656 else if (curElement.type == STGTY_STREAM)
1659 * create a new stream in destination storage. If the stream already
1660 * exist, it will be deleted and a new one will be created.
1662 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1663 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1670 * open child stream storage
1672 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1673 STGM_READ|STGM_SHARE_EXCLUSIVE,
1680 * Get the size of the source stream
1682 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1685 * Set the size of the destination stream.
1687 IStream_SetSize(pstrTmp, strStat.cbSize);
1692 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1695 IStream_Release( pstrTmp );
1696 IStream_Release( pstrChild );
1700 WARN("unknown element type: %d\n", curElement.type);
1703 } while (hr == S_OK);
1708 IEnumSTATSTG_Release(elements);
1713 /*************************************************************************
1714 * MoveElementTo (IStorage)
1716 static HRESULT WINAPI StorageImpl_MoveElementTo(
1718 const OLECHAR *pwcsName, /* [string][in] */
1719 IStorage *pstgDest, /* [unique][in] */
1720 const OLECHAR *pwcsNewName,/* [string][in] */
1721 DWORD grfFlags) /* [in] */
1723 FIXME("(%p %s %p %s %u): stub\n", iface,
1724 debugstr_w(pwcsName), pstgDest,
1725 debugstr_w(pwcsNewName), grfFlags);
1729 /*************************************************************************
1732 * Ensures that any changes made to a storage object open in transacted mode
1733 * are reflected in the parent storage
1736 * Wine doesn't implement transacted mode, which seems to be a basic
1737 * optimization, so we can ignore this stub for now.
1739 static HRESULT WINAPI StorageImpl_Commit(
1741 DWORD grfCommitFlags)/* [in] */
1743 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1747 /*************************************************************************
1750 * Discard all changes that have been made since the last commit operation
1752 static HRESULT WINAPI StorageImpl_Revert(
1755 FIXME("(%p): stub\n", iface);
1759 /*************************************************************************
1760 * DestroyElement (IStorage)
1762 * Strategy: This implementation is built this way for simplicity not for speed.
1763 * I always delete the topmost element of the enumeration and adjust
1764 * the deleted element pointer all the time. This takes longer to
1765 * do but allow to reinvoke DestroyElement whenever we encounter a
1766 * storage object. The optimisation resides in the usage of another
1767 * enumeration strategy that would give all the leaves of a storage
1768 * first. (postfix order)
1770 static HRESULT WINAPI StorageImpl_DestroyElement(
1772 const OLECHAR *pwcsName)/* [string][in] */
1774 StorageImpl* const This=(StorageImpl*)iface;
1776 IEnumSTATSTGImpl* propertyEnumeration;
1779 StgProperty propertyToDelete;
1780 StgProperty parentProperty;
1781 ULONG foundPropertyIndexToDelete;
1782 ULONG typeOfRelation;
1783 ULONG parentPropertyId = 0;
1786 iface, debugstr_w(pwcsName));
1789 * Perform a sanity check on the parameters.
1792 return STG_E_INVALIDPOINTER;
1795 * Create a property enumeration to search the property with the given name
1797 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1798 This->base.ancestorStorage,
1799 This->base.rootPropertySetIndex);
1801 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1802 propertyEnumeration,
1806 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1808 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1810 return STG_E_FILENOTFOUND;
1814 * Find the parent property of the property to delete (the one that
1815 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1816 * the parent is This. Otherwise, the parent is one of its sibling...
1820 * First, read This's StgProperty..
1822 res = StorageImpl_ReadProperty(
1823 This->base.ancestorStorage,
1824 This->base.rootPropertySetIndex,
1830 * Second, check to see if by any chance the actual storage (This) is not
1831 * the parent of the property to delete... We never know...
1833 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1836 * Set data as it would have been done in the else part...
1838 typeOfRelation = PROPERTY_RELATION_DIR;
1839 parentPropertyId = This->base.rootPropertySetIndex;
1844 * Create a property enumeration to search the parent properties, and
1845 * delete it once done.
1847 IEnumSTATSTGImpl* propertyEnumeration2;
1849 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1850 This->base.ancestorStorage,
1851 This->base.rootPropertySetIndex);
1853 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1854 propertyEnumeration2,
1855 foundPropertyIndexToDelete,
1859 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1862 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1864 hr = deleteStorageProperty(
1866 foundPropertyIndexToDelete,
1869 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1871 hr = deleteStreamProperty(
1873 foundPropertyIndexToDelete,
1881 * Adjust the property chain
1883 hr = adjustPropertyChain(
1894 /************************************************************************
1895 * StorageImpl_Stat (IStorage)
1897 * This method will retrieve information about this storage object.
1899 * See Windows documentation for more details on IStorage methods.
1901 static HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1902 STATSTG* pstatstg, /* [out] */
1903 DWORD grfStatFlag) /* [in] */
1905 StorageImpl* const This = (StorageImpl*)iface;
1906 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1908 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1910 CoTaskMemFree(pstatstg->pwcsName);
1911 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1912 strcpyW(pstatstg->pwcsName, This->pwcsName);
1918 /******************************************************************************
1919 * Internal stream list handlers
1922 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1924 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1925 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1928 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1930 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1931 list_remove(&(strm->StrmListEntry));
1934 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1936 struct list *cur, *cur2;
1937 StgStreamImpl *strm=NULL;
1939 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1940 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1941 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1942 strm->parentStorage = NULL;
1948 /*********************************************************************
1952 * Perform the deletion of a complete storage node
1955 static HRESULT deleteStorageProperty(
1956 StorageImpl *parentStorage,
1957 ULONG indexOfPropertyToDelete,
1958 StgProperty propertyToDelete)
1960 IEnumSTATSTG *elements = 0;
1961 IStorage *childStorage = 0;
1962 STATSTG currentElement;
1964 HRESULT destroyHr = S_OK;
1967 * Open the storage and enumerate it
1969 hr = StorageBaseImpl_OpenStorage(
1970 (IStorage*)parentStorage,
1971 propertyToDelete.name,
1973 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1984 * Enumerate the elements
1986 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1991 * Obtain the next element
1993 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
1996 destroyHr = StorageImpl_DestroyElement(
1997 (IStorage*)childStorage,
1998 (OLECHAR*)currentElement.pwcsName);
2000 CoTaskMemFree(currentElement.pwcsName);
2004 * We need to Reset the enumeration every time because we delete elements
2005 * and the enumeration could be invalid
2007 IEnumSTATSTG_Reset(elements);
2009 } while ((hr == S_OK) && (destroyHr == S_OK));
2012 * Invalidate the property by zeroing its name member.
2014 propertyToDelete.sizeOfNameString = 0;
2016 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
2017 indexOfPropertyToDelete,
2020 IStorage_Release(childStorage);
2021 IEnumSTATSTG_Release(elements);
2026 /*********************************************************************
2030 * Perform the deletion of a stream node
2033 static HRESULT deleteStreamProperty(
2034 StorageImpl *parentStorage,
2035 ULONG indexOfPropertyToDelete,
2036 StgProperty propertyToDelete)
2040 ULARGE_INTEGER size;
2042 size.u.HighPart = 0;
2045 hr = StorageBaseImpl_OpenStream(
2046 (IStorage*)parentStorage,
2047 (OLECHAR*)propertyToDelete.name,
2049 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2061 hr = IStream_SetSize(pis, size);
2069 * Release the stream object.
2071 IStream_Release(pis);
2074 * Invalidate the property by zeroing its name member.
2076 propertyToDelete.sizeOfNameString = 0;
2079 * Here we should re-read the property so we get the updated pointer
2080 * but since we are here to zap it, I don't do it...
2082 StorageImpl_WriteProperty(
2083 parentStorage->base.ancestorStorage,
2084 indexOfPropertyToDelete,
2090 /*********************************************************************
2094 * Finds a placeholder for the StgProperty within the Storage
2097 static HRESULT findPlaceholder(
2098 StorageImpl *storage,
2099 ULONG propertyIndexToStore,
2100 ULONG storePropertyIndex,
2103 StgProperty storeProperty;
2108 * Read the storage property
2110 res = StorageImpl_ReadProperty(
2111 storage->base.ancestorStorage,
2120 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2122 if (storeProperty.previousProperty != PROPERTY_NULL)
2124 return findPlaceholder(
2126 propertyIndexToStore,
2127 storeProperty.previousProperty,
2132 storeProperty.previousProperty = propertyIndexToStore;
2135 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2137 if (storeProperty.nextProperty != PROPERTY_NULL)
2139 return findPlaceholder(
2141 propertyIndexToStore,
2142 storeProperty.nextProperty,
2147 storeProperty.nextProperty = propertyIndexToStore;
2150 else if (typeOfRelation == PROPERTY_RELATION_DIR)
2152 if (storeProperty.dirProperty != PROPERTY_NULL)
2154 return findPlaceholder(
2156 propertyIndexToStore,
2157 storeProperty.dirProperty,
2162 storeProperty.dirProperty = propertyIndexToStore;
2166 hr = StorageImpl_WriteProperty(
2167 storage->base.ancestorStorage,
2179 /*************************************************************************
2183 * This method takes the previous and the next property link of a property
2184 * to be deleted and find them a place in the Storage.
2186 static HRESULT adjustPropertyChain(
2188 StgProperty propertyToDelete,
2189 StgProperty parentProperty,
2190 ULONG parentPropertyId,
2193 ULONG newLinkProperty = PROPERTY_NULL;
2194 BOOL needToFindAPlaceholder = FALSE;
2195 ULONG storeNode = PROPERTY_NULL;
2196 ULONG toStoreNode = PROPERTY_NULL;
2197 INT relationType = 0;
2201 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2203 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2206 * Set the parent previous to the property to delete previous
2208 newLinkProperty = propertyToDelete.previousProperty;
2210 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2213 * We also need to find a storage for the other link, setup variables
2214 * to do this at the end...
2216 needToFindAPlaceholder = TRUE;
2217 storeNode = propertyToDelete.previousProperty;
2218 toStoreNode = propertyToDelete.nextProperty;
2219 relationType = PROPERTY_RELATION_NEXT;
2222 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2225 * Set the parent previous to the property to delete next
2227 newLinkProperty = propertyToDelete.nextProperty;
2231 * Link it for real...
2233 parentProperty.previousProperty = newLinkProperty;
2236 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2238 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2241 * Set the parent next to the property to delete next previous
2243 newLinkProperty = propertyToDelete.previousProperty;
2245 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2248 * We also need to find a storage for the other link, setup variables
2249 * to do this at the end...
2251 needToFindAPlaceholder = TRUE;
2252 storeNode = propertyToDelete.previousProperty;
2253 toStoreNode = propertyToDelete.nextProperty;
2254 relationType = PROPERTY_RELATION_NEXT;
2257 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2260 * Set the parent next to the property to delete next
2262 newLinkProperty = propertyToDelete.nextProperty;
2266 * Link it for real...
2268 parentProperty.nextProperty = newLinkProperty;
2270 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2272 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2275 * Set the parent dir to the property to delete previous
2277 newLinkProperty = propertyToDelete.previousProperty;
2279 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2282 * We also need to find a storage for the other link, setup variables
2283 * to do this at the end...
2285 needToFindAPlaceholder = TRUE;
2286 storeNode = propertyToDelete.previousProperty;
2287 toStoreNode = propertyToDelete.nextProperty;
2288 relationType = PROPERTY_RELATION_NEXT;
2291 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2294 * Set the parent dir to the property to delete next
2296 newLinkProperty = propertyToDelete.nextProperty;
2300 * Link it for real...
2302 parentProperty.dirProperty = newLinkProperty;
2306 * Write back the parent property
2308 res = StorageImpl_WriteProperty(
2309 This->base.ancestorStorage,
2318 * If a placeholder is required for the other link, then, find one and
2319 * get out of here...
2321 if (needToFindAPlaceholder)
2323 hr = findPlaceholder(
2334 /******************************************************************************
2335 * SetElementTimes (IStorage)
2337 static HRESULT WINAPI StorageImpl_SetElementTimes(
2339 const OLECHAR *pwcsName,/* [string][in] */
2340 const FILETIME *pctime, /* [in] */
2341 const FILETIME *patime, /* [in] */
2342 const FILETIME *pmtime) /* [in] */
2344 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2348 /******************************************************************************
2349 * SetStateBits (IStorage)
2351 static HRESULT WINAPI StorageImpl_SetStateBits(
2353 DWORD grfStateBits,/* [in] */
2354 DWORD grfMask) /* [in] */
2356 StorageImpl* const This = (StorageImpl*)iface;
2357 This->base.stateBits = (This->base.stateBits & ~grfMask) | (grfStateBits & grfMask);
2362 * Virtual function table for the IStorage32Impl class.
2364 static const IStorageVtbl Storage32Impl_Vtbl =
2366 StorageBaseImpl_QueryInterface,
2367 StorageBaseImpl_AddRef,
2368 StorageBaseImpl_Release,
2369 StorageBaseImpl_CreateStream,
2370 StorageBaseImpl_OpenStream,
2371 StorageImpl_CreateStorage,
2372 StorageBaseImpl_OpenStorage,
2374 StorageImpl_MoveElementTo,
2377 StorageBaseImpl_EnumElements,
2378 StorageImpl_DestroyElement,
2379 StorageBaseImpl_RenameElement,
2380 StorageImpl_SetElementTimes,
2381 StorageBaseImpl_SetClass,
2382 StorageImpl_SetStateBits,
2386 static HRESULT StorageImpl_Construct(
2396 StgProperty currentProperty;
2397 BOOL readSuccessful;
2398 ULONG currentPropertyIndex;
2400 if ( FAILED( validateSTGM(openFlags) ))
2401 return STG_E_INVALIDFLAG;
2403 memset(This, 0, sizeof(StorageImpl));
2406 * Initialize stream list
2409 list_init(&This->base.strmHead);
2412 * Initialize the virtual function table.
2414 This->base.lpVtbl = &Storage32Impl_Vtbl;
2415 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2416 This->base.v_destructor = &StorageImpl_Destroy;
2417 This->base.openFlags = (openFlags & ~STGM_CREATE);
2420 * This is the top-level storage so initialize the ancestor pointer
2423 This->base.ancestorStorage = This;
2426 * Initialize the physical support of the storage.
2428 This->hFile = hFile;
2431 * Store copy of file path.
2434 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2435 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2436 if (!This->pwcsName)
2437 return STG_E_INSUFFICIENTMEMORY;
2438 strcpyW(This->pwcsName, pwcsName);
2442 * Initialize the big block cache.
2444 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2445 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2446 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2452 if (This->bigBlockFile == 0)
2457 ULARGE_INTEGER size;
2458 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2461 * Initialize all header variables:
2462 * - The big block depot consists of one block and it is at block 0
2463 * - The properties start at block 1
2464 * - There is no small block depot
2466 memset( This->bigBlockDepotStart,
2468 sizeof(This->bigBlockDepotStart));
2470 This->bigBlockDepotCount = 1;
2471 This->bigBlockDepotStart[0] = 0;
2472 This->rootStartBlock = 1;
2473 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2474 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2475 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2476 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2477 This->extBigBlockDepotCount = 0;
2479 StorageImpl_SaveFileHeader(This);
2482 * Add one block for the big block depot and one block for the properties
2484 size.u.HighPart = 0;
2485 size.u.LowPart = This->bigBlockSize * 3;
2486 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2489 * Initialize the big block depot
2491 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2492 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2493 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2494 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2499 * Load the header for the file.
2501 hr = StorageImpl_LoadFileHeader(This);
2505 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2512 * There is no block depot cached yet.
2514 This->indexBlockDepotCached = 0xFFFFFFFF;
2517 * Start searching for free blocks with block 0.
2519 This->prevFreeBlock = 0;
2522 * Create the block chain abstractions.
2524 if(!(This->rootBlockChain =
2525 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2526 return STG_E_READFAULT;
2528 if(!(This->smallBlockDepotChain =
2529 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2531 return STG_E_READFAULT;
2534 * Write the root property (memory only)
2538 StgProperty rootProp;
2540 * Initialize the property chain
2542 memset(&rootProp, 0, sizeof(rootProp));
2543 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2544 sizeof(rootProp.name)/sizeof(WCHAR) );
2545 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2546 rootProp.propertyType = PROPTYPE_ROOT;
2547 rootProp.previousProperty = PROPERTY_NULL;
2548 rootProp.nextProperty = PROPERTY_NULL;
2549 rootProp.dirProperty = PROPERTY_NULL;
2550 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2551 rootProp.size.u.HighPart = 0;
2552 rootProp.size.u.LowPart = 0;
2554 StorageImpl_WriteProperty(This, 0, &rootProp);
2558 * Find the ID of the root in the property sets.
2560 currentPropertyIndex = 0;
2564 readSuccessful = StorageImpl_ReadProperty(
2566 currentPropertyIndex,
2571 if ( (currentProperty.sizeOfNameString != 0 ) &&
2572 (currentProperty.propertyType == PROPTYPE_ROOT) )
2574 This->base.rootPropertySetIndex = currentPropertyIndex;
2578 currentPropertyIndex++;
2580 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2582 if (!readSuccessful)
2585 return STG_E_READFAULT;
2589 * Create the block chain abstraction for the small block root chain.
2591 if(!(This->smallBlockRootChain =
2592 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2593 return STG_E_READFAULT;
2598 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2600 StorageImpl *This = (StorageImpl*) iface;
2601 TRACE("(%p)\n", This);
2603 StorageBaseImpl_DeleteAll(&This->base);
2605 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2607 BlockChainStream_Destroy(This->smallBlockRootChain);
2608 BlockChainStream_Destroy(This->rootBlockChain);
2609 BlockChainStream_Destroy(This->smallBlockDepotChain);
2611 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2612 HeapFree(GetProcessHeap(), 0, This);
2615 /******************************************************************************
2616 * Storage32Impl_GetNextFreeBigBlock
2618 * Returns the index of the next free big block.
2619 * If the big block depot is filled, this method will enlarge it.
2622 static ULONG StorageImpl_GetNextFreeBigBlock(
2625 ULONG depotBlockIndexPos;
2626 BYTE depotBuffer[BIG_BLOCK_SIZE];
2628 ULONG depotBlockOffset;
2629 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2630 ULONG nextBlockIndex = BLOCK_SPECIAL;
2632 ULONG freeBlock = BLOCK_UNUSED;
2634 depotIndex = This->prevFreeBlock / blocksPerDepot;
2635 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2638 * Scan the entire big block depot until we find a block marked free
2640 while (nextBlockIndex != BLOCK_UNUSED)
2642 if (depotIndex < COUNT_BBDEPOTINHEADER)
2644 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2647 * Grow the primary depot.
2649 if (depotBlockIndexPos == BLOCK_UNUSED)
2651 depotBlockIndexPos = depotIndex*blocksPerDepot;
2654 * Add a block depot.
2656 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2657 This->bigBlockDepotCount++;
2658 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2661 * Flag it as a block depot.
2663 StorageImpl_SetNextBlockInChain(This,
2667 /* Save new header information.
2669 StorageImpl_SaveFileHeader(This);
2674 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2676 if (depotBlockIndexPos == BLOCK_UNUSED)
2679 * Grow the extended depot.
2681 ULONG extIndex = BLOCK_UNUSED;
2682 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2683 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2685 if (extBlockOffset == 0)
2687 /* We need an extended block.
2689 extIndex = Storage32Impl_AddExtBlockDepot(This);
2690 This->extBigBlockDepotCount++;
2691 depotBlockIndexPos = extIndex + 1;
2694 depotBlockIndexPos = depotIndex * blocksPerDepot;
2697 * Add a block depot and mark it in the extended block.
2699 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2700 This->bigBlockDepotCount++;
2701 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2703 /* Flag the block depot.
2705 StorageImpl_SetNextBlockInChain(This,
2709 /* If necessary, flag the extended depot block.
2711 if (extIndex != BLOCK_UNUSED)
2712 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2714 /* Save header information.
2716 StorageImpl_SaveFileHeader(This);
2720 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2724 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2725 ( nextBlockIndex != BLOCK_UNUSED))
2727 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2729 if (nextBlockIndex == BLOCK_UNUSED)
2731 freeBlock = (depotIndex * blocksPerDepot) +
2732 (depotBlockOffset/sizeof(ULONG));
2735 depotBlockOffset += sizeof(ULONG);
2740 depotBlockOffset = 0;
2744 * make sure that the block physically exists before using it
2746 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2748 This->prevFreeBlock = freeBlock;
2753 /******************************************************************************
2754 * Storage32Impl_AddBlockDepot
2756 * This will create a depot block, essentially it is a block initialized
2759 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2761 BYTE blockBuffer[BIG_BLOCK_SIZE];
2764 * Initialize blocks as free
2766 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2767 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2770 /******************************************************************************
2771 * Storage32Impl_GetExtDepotBlock
2773 * Returns the index of the block that corresponds to the specified depot
2774 * index. This method is only for depot indexes equal or greater than
2775 * COUNT_BBDEPOTINHEADER.
2777 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2779 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2780 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2781 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2782 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2783 ULONG blockIndex = BLOCK_UNUSED;
2784 ULONG extBlockIndex = This->extBigBlockDepotStart;
2786 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2788 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2789 return BLOCK_UNUSED;
2791 while (extBlockCount > 0)
2793 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2797 if (extBlockIndex != BLOCK_UNUSED)
2798 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
2799 extBlockOffset * sizeof(ULONG), &blockIndex);
2804 /******************************************************************************
2805 * Storage32Impl_SetExtDepotBlock
2807 * Associates the specified block index to the specified depot index.
2808 * This method is only for depot indexes equal or greater than
2809 * COUNT_BBDEPOTINHEADER.
2811 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2813 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2814 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2815 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2816 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2817 ULONG extBlockIndex = This->extBigBlockDepotStart;
2819 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2821 while (extBlockCount > 0)
2823 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2827 if (extBlockIndex != BLOCK_UNUSED)
2829 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
2830 extBlockOffset * sizeof(ULONG),
2835 /******************************************************************************
2836 * Storage32Impl_AddExtBlockDepot
2838 * Creates an extended depot block.
2840 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2842 ULONG numExtBlocks = This->extBigBlockDepotCount;
2843 ULONG nextExtBlock = This->extBigBlockDepotStart;
2844 BYTE depotBuffer[BIG_BLOCK_SIZE];
2845 ULONG index = BLOCK_UNUSED;
2846 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2847 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2848 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2850 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2851 blocksPerDepotBlock;
2853 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2856 * The first extended block.
2858 This->extBigBlockDepotStart = index;
2864 * Follow the chain to the last one.
2866 for (i = 0; i < (numExtBlocks - 1); i++)
2868 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2872 * Add the new extended block to the chain.
2874 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
2879 * Initialize this block.
2881 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2882 StorageImpl_WriteBigBlock(This, index, depotBuffer);
2887 /******************************************************************************
2888 * Storage32Impl_FreeBigBlock
2890 * This method will flag the specified block as free in the big block depot.
2892 static void StorageImpl_FreeBigBlock(
2896 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2898 if (blockIndex < This->prevFreeBlock)
2899 This->prevFreeBlock = blockIndex;
2902 /************************************************************************
2903 * Storage32Impl_GetNextBlockInChain
2905 * This method will retrieve the block index of the next big block in
2908 * Params: This - Pointer to the Storage object.
2909 * blockIndex - Index of the block to retrieve the chain
2911 * nextBlockIndex - receives the return value.
2913 * Returns: This method returns the index of the next block in the chain.
2914 * It will return the constants:
2915 * BLOCK_SPECIAL - If the block given was not part of a
2917 * BLOCK_END_OF_CHAIN - If the block given was the last in
2919 * BLOCK_UNUSED - If the block given was not past of a chain
2921 * BLOCK_EXTBBDEPOT - This block is part of the extended
2924 * See Windows documentation for more details on IStorage methods.
2926 static HRESULT StorageImpl_GetNextBlockInChain(
2929 ULONG* nextBlockIndex)
2931 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2932 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2933 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2934 BYTE depotBuffer[BIG_BLOCK_SIZE];
2936 ULONG depotBlockIndexPos;
2939 *nextBlockIndex = BLOCK_SPECIAL;
2941 if(depotBlockCount >= This->bigBlockDepotCount)
2943 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
2944 This->bigBlockDepotCount);
2945 return STG_E_READFAULT;
2949 * Cache the currently accessed depot block.
2951 if (depotBlockCount != This->indexBlockDepotCached)
2953 This->indexBlockDepotCached = depotBlockCount;
2955 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2957 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2962 * We have to look in the extended depot.
2964 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2967 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2970 return STG_E_READFAULT;
2972 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2974 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2975 This->blockDepotCached[index] = *nextBlockIndex;
2979 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2984 /******************************************************************************
2985 * Storage32Impl_GetNextExtendedBlock
2987 * Given an extended block this method will return the next extended block.
2990 * The last ULONG of an extended block is the block index of the next
2991 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2995 * - The index of the next extended block
2996 * - BLOCK_UNUSED: there is no next extended block.
2997 * - Any other return values denotes failure.
2999 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3001 ULONG nextBlockIndex = BLOCK_SPECIAL;
3002 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3004 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3007 return nextBlockIndex;
3010 /******************************************************************************
3011 * Storage32Impl_SetNextBlockInChain
3013 * This method will write the index of the specified block's next block
3014 * in the big block depot.
3016 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3019 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3020 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3021 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3024 static void StorageImpl_SetNextBlockInChain(
3029 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3030 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3031 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3032 ULONG depotBlockIndexPos;
3034 assert(depotBlockCount < This->bigBlockDepotCount);
3035 assert(blockIndex != nextBlock);
3037 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3039 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3044 * We have to look in the extended depot.
3046 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3049 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3052 * Update the cached block depot, if necessary.
3054 if (depotBlockCount == This->indexBlockDepotCached)
3056 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3060 /******************************************************************************
3061 * Storage32Impl_LoadFileHeader
3063 * This method will read in the file header, i.e. big block index -1.
3065 static HRESULT StorageImpl_LoadFileHeader(
3068 HRESULT hr = STG_E_FILENOTFOUND;
3069 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3075 * Get a pointer to the big block of data containing the header.
3077 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3080 * Extract the information from the header.
3085 * Check for the "magic number" signature and return an error if it is not
3088 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3090 return STG_E_OLDFORMAT;
3093 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3095 return STG_E_INVALIDHEADER;
3098 StorageUtl_ReadWord(
3100 OFFSET_BIGBLOCKSIZEBITS,
3101 &This->bigBlockSizeBits);
3103 StorageUtl_ReadWord(
3105 OFFSET_SMALLBLOCKSIZEBITS,
3106 &This->smallBlockSizeBits);
3108 StorageUtl_ReadDWord(
3110 OFFSET_BBDEPOTCOUNT,
3111 &This->bigBlockDepotCount);
3113 StorageUtl_ReadDWord(
3115 OFFSET_ROOTSTARTBLOCK,
3116 &This->rootStartBlock);
3118 StorageUtl_ReadDWord(
3120 OFFSET_SBDEPOTSTART,
3121 &This->smallBlockDepotStart);
3123 StorageUtl_ReadDWord(
3125 OFFSET_EXTBBDEPOTSTART,
3126 &This->extBigBlockDepotStart);
3128 StorageUtl_ReadDWord(
3130 OFFSET_EXTBBDEPOTCOUNT,
3131 &This->extBigBlockDepotCount);
3133 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3135 StorageUtl_ReadDWord(
3137 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3138 &(This->bigBlockDepotStart[index]));
3142 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3146 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3147 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3151 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
3152 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
3156 * Right now, the code is making some assumptions about the size of the
3157 * blocks, just make sure they are what we're expecting.
3159 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3160 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3162 WARN("Broken OLE storage file\n");
3163 hr = STG_E_INVALIDHEADER;
3172 /******************************************************************************
3173 * Storage32Impl_SaveFileHeader
3175 * This method will save to the file the header, i.e. big block -1.
3177 static void StorageImpl_SaveFileHeader(
3180 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3185 * Get a pointer to the big block of data containing the header.
3187 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3190 * If the block read failed, the file is probably new.
3195 * Initialize for all unknown fields.
3197 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3200 * Initialize the magic number.
3202 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3205 * And a bunch of things we don't know what they mean
3207 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3208 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3209 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3210 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3214 * Write the information to the header.
3216 StorageUtl_WriteWord(
3218 OFFSET_BIGBLOCKSIZEBITS,
3219 This->bigBlockSizeBits);
3221 StorageUtl_WriteWord(
3223 OFFSET_SMALLBLOCKSIZEBITS,
3224 This->smallBlockSizeBits);
3226 StorageUtl_WriteDWord(
3228 OFFSET_BBDEPOTCOUNT,
3229 This->bigBlockDepotCount);
3231 StorageUtl_WriteDWord(
3233 OFFSET_ROOTSTARTBLOCK,
3234 This->rootStartBlock);
3236 StorageUtl_WriteDWord(
3238 OFFSET_SBDEPOTSTART,
3239 This->smallBlockDepotStart);
3241 StorageUtl_WriteDWord(
3243 OFFSET_SBDEPOTCOUNT,
3244 This->smallBlockDepotChain ?
3245 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3247 StorageUtl_WriteDWord(
3249 OFFSET_EXTBBDEPOTSTART,
3250 This->extBigBlockDepotStart);
3252 StorageUtl_WriteDWord(
3254 OFFSET_EXTBBDEPOTCOUNT,
3255 This->extBigBlockDepotCount);
3257 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3259 StorageUtl_WriteDWord(
3261 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3262 (This->bigBlockDepotStart[index]));
3266 * Write the big block back to the file.
3268 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3271 /******************************************************************************
3272 * Storage32Impl_ReadProperty
3274 * This method will read the specified property from the property chain.
3276 BOOL StorageImpl_ReadProperty(
3279 StgProperty* buffer)
3281 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3282 ULARGE_INTEGER offsetInPropSet;
3286 offsetInPropSet.u.HighPart = 0;
3287 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3289 readRes = BlockChainStream_ReadAt(
3290 This->rootBlockChain,
3296 if (SUCCEEDED(readRes))
3298 /* replace the name of root entry (often "Root Entry") by the file name */
3299 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3300 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3302 memset(buffer->name, 0, sizeof(buffer->name));
3306 PROPERTY_NAME_BUFFER_LEN );
3307 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3309 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3311 StorageUtl_ReadWord(
3313 OFFSET_PS_NAMELENGTH,
3314 &buffer->sizeOfNameString);
3316 StorageUtl_ReadDWord(
3318 OFFSET_PS_PREVIOUSPROP,
3319 &buffer->previousProperty);
3321 StorageUtl_ReadDWord(
3324 &buffer->nextProperty);
3326 StorageUtl_ReadDWord(
3329 &buffer->dirProperty);
3331 StorageUtl_ReadGUID(
3334 &buffer->propertyUniqueID);
3336 StorageUtl_ReadDWord(
3339 &buffer->timeStampS1);
3341 StorageUtl_ReadDWord(
3344 &buffer->timeStampD1);
3346 StorageUtl_ReadDWord(
3349 &buffer->timeStampS2);
3351 StorageUtl_ReadDWord(
3354 &buffer->timeStampD2);
3356 StorageUtl_ReadDWord(
3358 OFFSET_PS_STARTBLOCK,
3359 &buffer->startingBlock);
3361 StorageUtl_ReadDWord(
3364 &buffer->size.u.LowPart);
3366 buffer->size.u.HighPart = 0;
3369 return SUCCEEDED(readRes) ? TRUE : FALSE;
3372 /*********************************************************************
3373 * Write the specified property into the property chain
3375 BOOL StorageImpl_WriteProperty(
3378 const StgProperty* buffer)
3380 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3381 ULARGE_INTEGER offsetInPropSet;
3385 offsetInPropSet.u.HighPart = 0;
3386 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3388 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3391 currentProperty + OFFSET_PS_NAME,
3393 PROPERTY_NAME_BUFFER_LEN );
3395 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3397 StorageUtl_WriteWord(
3399 OFFSET_PS_NAMELENGTH,
3400 buffer->sizeOfNameString);
3402 StorageUtl_WriteDWord(
3404 OFFSET_PS_PREVIOUSPROP,
3405 buffer->previousProperty);
3407 StorageUtl_WriteDWord(
3410 buffer->nextProperty);
3412 StorageUtl_WriteDWord(
3415 buffer->dirProperty);
3417 StorageUtl_WriteGUID(
3420 &buffer->propertyUniqueID);
3422 StorageUtl_WriteDWord(
3425 buffer->timeStampS1);
3427 StorageUtl_WriteDWord(
3430 buffer->timeStampD1);
3432 StorageUtl_WriteDWord(
3435 buffer->timeStampS2);
3437 StorageUtl_WriteDWord(
3440 buffer->timeStampD2);
3442 StorageUtl_WriteDWord(
3444 OFFSET_PS_STARTBLOCK,
3445 buffer->startingBlock);
3447 StorageUtl_WriteDWord(
3450 buffer->size.u.LowPart);
3452 writeRes = BlockChainStream_WriteAt(This->rootBlockChain,
3457 return SUCCEEDED(writeRes) ? TRUE : FALSE;
3460 static BOOL StorageImpl_ReadBigBlock(
3465 ULARGE_INTEGER ulOffset;
3468 ulOffset.u.HighPart = 0;
3469 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3471 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3472 return (read == This->bigBlockSize);
3475 static BOOL StorageImpl_ReadDWordFromBigBlock(
3481 ULARGE_INTEGER ulOffset;
3485 ulOffset.u.HighPart = 0;
3486 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3487 ulOffset.u.LowPart += offset;
3489 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3490 *value = le32toh(tmp);
3491 return (read == sizeof(DWORD));
3494 static BOOL StorageImpl_WriteBigBlock(
3499 ULARGE_INTEGER ulOffset;
3502 ulOffset.u.HighPart = 0;
3503 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3505 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3506 return (wrote == This->bigBlockSize);
3509 static BOOL StorageImpl_WriteDWordToBigBlock(
3515 ULARGE_INTEGER ulOffset;
3518 ulOffset.u.HighPart = 0;
3519 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3520 ulOffset.u.LowPart += offset;
3522 value = htole32(value);
3523 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3524 return (wrote == sizeof(DWORD));
3527 /******************************************************************************
3528 * Storage32Impl_SmallBlocksToBigBlocks
3530 * This method will convert a small block chain to a big block chain.
3531 * The small block chain will be destroyed.
3533 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3535 SmallBlockChainStream** ppsbChain)
3537 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3538 ULARGE_INTEGER size, offset;
3539 ULONG cbRead, cbWritten;
3540 ULARGE_INTEGER cbTotalRead;
3541 ULONG propertyIndex;
3542 HRESULT resWrite = S_OK;
3544 StgProperty chainProperty;
3546 BlockChainStream *bbTempChain = NULL;
3547 BlockChainStream *bigBlockChain = NULL;
3550 * Create a temporary big block chain that doesn't have
3551 * an associated property. This temporary chain will be
3552 * used to copy data from small blocks to big blocks.
3554 bbTempChain = BlockChainStream_Construct(This,
3557 if(!bbTempChain) return NULL;
3559 * Grow the big block chain.
3561 size = SmallBlockChainStream_GetSize(*ppsbChain);
3562 BlockChainStream_SetSize(bbTempChain, size);
3565 * Copy the contents of the small block chain to the big block chain
3566 * by small block size increments.
3568 offset.u.LowPart = 0;
3569 offset.u.HighPart = 0;
3570 cbTotalRead.QuadPart = 0;
3572 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3575 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3577 This->smallBlockSize,
3580 if (FAILED(resRead))
3585 cbTotalRead.QuadPart += cbRead;
3587 resWrite = BlockChainStream_WriteAt(bbTempChain,
3593 if (FAILED(resWrite))
3596 offset.u.LowPart += This->smallBlockSize;
3598 } while (cbTotalRead.QuadPart < size.QuadPart);
3599 HeapFree(GetProcessHeap(),0,buffer);
3601 if (FAILED(resRead) || FAILED(resWrite))
3603 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3604 BlockChainStream_Destroy(bbTempChain);
3609 * Destroy the small block chain.
3611 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3612 size.u.HighPart = 0;
3614 SmallBlockChainStream_SetSize(*ppsbChain, size);
3615 SmallBlockChainStream_Destroy(*ppsbChain);
3619 * Change the property information. This chain is now a big block chain
3620 * and it doesn't reside in the small blocks chain anymore.
3622 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3624 chainProperty.startingBlock = bbHeadOfChain;
3626 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3629 * Destroy the temporary propertyless big block chain.
3630 * Create a new big block chain associated with this property.
3632 BlockChainStream_Destroy(bbTempChain);
3633 bigBlockChain = BlockChainStream_Construct(This,
3637 return bigBlockChain;
3640 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3642 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3644 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3645 HeapFree(GetProcessHeap(), 0, This);
3648 /******************************************************************************
3650 ** Storage32InternalImpl_Commit
3652 ** The non-root storages cannot be opened in transacted mode thus this function
3655 static HRESULT WINAPI StorageInternalImpl_Commit(
3657 DWORD grfCommitFlags) /* [in] */
3662 /******************************************************************************
3664 ** Storage32InternalImpl_Revert
3666 ** The non-root storages cannot be opened in transacted mode thus this function
3669 static HRESULT WINAPI StorageInternalImpl_Revert(
3675 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3677 IStorage_Release((IStorage*)This->parentStorage);
3678 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3679 HeapFree(GetProcessHeap(), 0, This);
3682 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3683 IEnumSTATSTG* iface,
3687 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3690 * Perform a sanity check on the parameters.
3693 return E_INVALIDARG;
3696 * Initialize the return parameter.
3701 * Compare the riid with the interface IDs implemented by this object.
3703 if (IsEqualGUID(&IID_IUnknown, riid) ||
3704 IsEqualGUID(&IID_IEnumSTATSTG, riid))
3706 *ppvObject = (IEnumSTATSTG*)This;
3707 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3711 return E_NOINTERFACE;
3714 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3715 IEnumSTATSTG* iface)
3717 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3718 return InterlockedIncrement(&This->ref);
3721 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3722 IEnumSTATSTG* iface)
3724 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3728 newRef = InterlockedDecrement(&This->ref);
3731 * If the reference count goes down to 0, perform suicide.
3735 IEnumSTATSTGImpl_Destroy(This);
3741 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3742 IEnumSTATSTG* iface,
3745 ULONG* pceltFetched)
3747 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3749 StgProperty currentProperty;
3750 STATSTG* currentReturnStruct = rgelt;
3751 ULONG objectFetched = 0;
3752 ULONG currentSearchNode;
3755 * Perform a sanity check on the parameters.
3757 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3758 return E_INVALIDARG;
3761 * To avoid the special case, get another pointer to a ULONG value if
3762 * the caller didn't supply one.
3764 if (pceltFetched==0)
3765 pceltFetched = &objectFetched;
3768 * Start the iteration, we will iterate until we hit the end of the
3769 * linked list or until we hit the number of items to iterate through
3774 * Start with the node at the top of the stack.
3776 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3778 while ( ( *pceltFetched < celt) &&
3779 ( currentSearchNode!=PROPERTY_NULL) )
3782 * Remove the top node from the stack
3784 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3787 * Read the property from the storage.
3789 StorageImpl_ReadProperty(This->parentStorage,
3794 * Copy the information to the return buffer.
3796 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3801 * Step to the next item in the iteration
3804 currentReturnStruct++;
3807 * Push the next search node in the search stack.
3809 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3812 * continue the iteration.
3814 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3817 if (*pceltFetched == celt)
3824 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3825 IEnumSTATSTG* iface,
3828 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3830 StgProperty currentProperty;
3831 ULONG objectFetched = 0;
3832 ULONG currentSearchNode;
3835 * Start with the node at the top of the stack.
3837 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3839 while ( (objectFetched < celt) &&
3840 (currentSearchNode!=PROPERTY_NULL) )
3843 * Remove the top node from the stack
3845 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3848 * Read the property from the storage.
3850 StorageImpl_ReadProperty(This->parentStorage,
3855 * Step to the next item in the iteration
3860 * Push the next search node in the search stack.
3862 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3865 * continue the iteration.
3867 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3870 if (objectFetched == celt)
3876 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3877 IEnumSTATSTG* iface)
3879 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3881 StgProperty rootProperty;
3882 BOOL readSuccessful;
3885 * Re-initialize the search stack to an empty stack
3887 This->stackSize = 0;
3890 * Read the root property from the storage.
3892 readSuccessful = StorageImpl_ReadProperty(
3893 This->parentStorage,
3894 This->firstPropertyNode,
3899 assert(rootProperty.sizeOfNameString!=0);
3902 * Push the search node in the search stack.
3904 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3910 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3911 IEnumSTATSTG* iface,
3912 IEnumSTATSTG** ppenum)
3914 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3916 IEnumSTATSTGImpl* newClone;
3919 * Perform a sanity check on the parameters.
3922 return E_INVALIDARG;
3924 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3925 This->firstPropertyNode);
3929 * The new clone enumeration must point to the same current node as
3932 newClone->stackSize = This->stackSize ;
3933 newClone->stackMaxSize = This->stackMaxSize ;
3934 newClone->stackToVisit =
3935 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3938 newClone->stackToVisit,
3940 sizeof(ULONG) * newClone->stackSize);
3942 *ppenum = (IEnumSTATSTG*)newClone;
3945 * Don't forget to nail down a reference to the clone before
3948 IEnumSTATSTGImpl_AddRef(*ppenum);
3953 static INT IEnumSTATSTGImpl_FindParentProperty(
3954 IEnumSTATSTGImpl *This,
3955 ULONG childProperty,
3956 StgProperty *currentProperty,
3959 ULONG currentSearchNode;
3963 * To avoid the special case, get another pointer to a ULONG value if
3964 * the caller didn't supply one.
3967 thisNodeId = &foundNode;
3970 * Start with the node at the top of the stack.
3972 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3975 while (currentSearchNode!=PROPERTY_NULL)
3978 * Store the current node in the returned parameters
3980 *thisNodeId = currentSearchNode;
3983 * Remove the top node from the stack
3985 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3988 * Read the property from the storage.
3990 StorageImpl_ReadProperty(
3991 This->parentStorage,
3995 if (currentProperty->previousProperty == childProperty)
3996 return PROPERTY_RELATION_PREVIOUS;
3998 else if (currentProperty->nextProperty == childProperty)
3999 return PROPERTY_RELATION_NEXT;
4001 else if (currentProperty->dirProperty == childProperty)
4002 return PROPERTY_RELATION_DIR;
4005 * Push the next search node in the search stack.
4007 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4010 * continue the iteration.
4012 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4015 return PROPERTY_NULL;
4018 static ULONG IEnumSTATSTGImpl_FindProperty(
4019 IEnumSTATSTGImpl* This,
4020 const OLECHAR* lpszPropName,
4021 StgProperty* currentProperty)
4023 ULONG currentSearchNode;
4026 * Start with the node at the top of the stack.
4028 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4030 while (currentSearchNode!=PROPERTY_NULL)
4033 * Remove the top node from the stack
4035 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4038 * Read the property from the storage.
4040 StorageImpl_ReadProperty(This->parentStorage,
4044 if ( propertyNameCmp(
4045 (const OLECHAR*)currentProperty->name,
4046 (const OLECHAR*)lpszPropName) == 0)
4047 return currentSearchNode;
4050 * Push the next search node in the search stack.
4052 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4055 * continue the iteration.
4057 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4060 return PROPERTY_NULL;
4063 static void IEnumSTATSTGImpl_PushSearchNode(
4064 IEnumSTATSTGImpl* This,
4067 StgProperty rootProperty;
4068 BOOL readSuccessful;
4071 * First, make sure we're not trying to push an unexisting node.
4073 if (nodeToPush==PROPERTY_NULL)
4077 * First push the node to the stack
4079 if (This->stackSize == This->stackMaxSize)
4081 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4083 This->stackToVisit = HeapReAlloc(
4087 sizeof(ULONG) * This->stackMaxSize);
4090 This->stackToVisit[This->stackSize] = nodeToPush;
4094 * Read the root property from the storage.
4096 readSuccessful = StorageImpl_ReadProperty(
4097 This->parentStorage,
4103 assert(rootProperty.sizeOfNameString!=0);
4106 * Push the previous search node in the search stack.
4108 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4112 static ULONG IEnumSTATSTGImpl_PopSearchNode(
4113 IEnumSTATSTGImpl* This,
4118 if (This->stackSize == 0)
4119 return PROPERTY_NULL;
4121 topNode = This->stackToVisit[This->stackSize-1];
4130 * Virtual function table for the IEnumSTATSTGImpl class.
4132 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4134 IEnumSTATSTGImpl_QueryInterface,
4135 IEnumSTATSTGImpl_AddRef,
4136 IEnumSTATSTGImpl_Release,
4137 IEnumSTATSTGImpl_Next,
4138 IEnumSTATSTGImpl_Skip,
4139 IEnumSTATSTGImpl_Reset,
4140 IEnumSTATSTGImpl_Clone
4143 /******************************************************************************
4144 ** IEnumSTATSTGImpl implementation
4147 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4148 StorageImpl* parentStorage,
4149 ULONG firstPropertyNode)
4151 IEnumSTATSTGImpl* newEnumeration;
4153 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4155 if (newEnumeration!=0)
4158 * Set-up the virtual function table and reference count.
4160 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4161 newEnumeration->ref = 0;
4164 * We want to nail-down the reference to the storage in case the
4165 * enumeration out-lives the storage in the client application.
4167 newEnumeration->parentStorage = parentStorage;
4168 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4170 newEnumeration->firstPropertyNode = firstPropertyNode;
4173 * Initialize the search stack
4175 newEnumeration->stackSize = 0;
4176 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4177 newEnumeration->stackToVisit =
4178 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4181 * Make sure the current node of the iterator is the first one.
4183 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4186 return newEnumeration;
4190 * Virtual function table for the Storage32InternalImpl class.
4192 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4194 StorageBaseImpl_QueryInterface,
4195 StorageBaseImpl_AddRef,
4196 StorageBaseImpl_Release,
4197 StorageBaseImpl_CreateStream,
4198 StorageBaseImpl_OpenStream,
4199 StorageImpl_CreateStorage,
4200 StorageBaseImpl_OpenStorage,
4202 StorageImpl_MoveElementTo,
4203 StorageInternalImpl_Commit,
4204 StorageInternalImpl_Revert,
4205 StorageBaseImpl_EnumElements,
4206 StorageImpl_DestroyElement,
4207 StorageBaseImpl_RenameElement,
4208 StorageImpl_SetElementTimes,
4209 StorageBaseImpl_SetClass,
4210 StorageImpl_SetStateBits,
4211 StorageBaseImpl_Stat
4214 /******************************************************************************
4215 ** Storage32InternalImpl implementation
4218 static StorageInternalImpl* StorageInternalImpl_Construct(
4219 StorageImpl* ancestorStorage,
4221 ULONG rootPropertyIndex)
4223 StorageInternalImpl* newStorage;
4226 * Allocate space for the new storage object
4228 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
4233 * Initialize the stream list
4235 list_init(&newStorage->base.strmHead);
4238 * Initialize the virtual function table.
4240 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4241 newStorage->base.v_destructor = &StorageInternalImpl_Destroy;
4242 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4245 * Keep the ancestor storage pointer and nail a reference to it.
4247 newStorage->base.ancestorStorage = ancestorStorage;
4248 StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
4251 * Keep the index of the root property set for this storage,
4253 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4261 /******************************************************************************
4262 ** StorageUtl implementation
4265 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4269 memcpy(&tmp, buffer+offset, sizeof(WORD));
4270 *value = le16toh(tmp);
4273 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4275 value = htole16(value);
4276 memcpy(buffer+offset, &value, sizeof(WORD));
4279 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4283 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4284 *value = le32toh(tmp);
4287 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4289 value = htole32(value);
4290 memcpy(buffer+offset, &value, sizeof(DWORD));
4293 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4294 ULARGE_INTEGER* value)
4296 #ifdef WORDS_BIGENDIAN
4299 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4300 value->u.LowPart = htole32(tmp.u.HighPart);
4301 value->u.HighPart = htole32(tmp.u.LowPart);
4303 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4307 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4308 const ULARGE_INTEGER *value)
4310 #ifdef WORDS_BIGENDIAN
4313 tmp.u.LowPart = htole32(value->u.HighPart);
4314 tmp.u.HighPart = htole32(value->u.LowPart);
4315 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4317 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4321 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4323 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4324 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4325 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4327 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4330 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4332 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4333 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4334 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4336 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4339 void StorageUtl_CopyPropertyToSTATSTG(
4340 STATSTG* destination,
4341 const StgProperty* source,
4345 * The copy of the string occurs only when the flag is not set
4347 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4348 (source->name == NULL) ||
4349 (source->name[0] == 0) )
4351 destination->pwcsName = 0;
4355 destination->pwcsName =
4356 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4358 strcpyW((LPWSTR)destination->pwcsName, source->name);
4361 switch (source->propertyType)
4363 case PROPTYPE_STORAGE:
4365 destination->type = STGTY_STORAGE;
4367 case PROPTYPE_STREAM:
4368 destination->type = STGTY_STREAM;
4371 destination->type = STGTY_STREAM;
4375 destination->cbSize = source->size;
4377 currentReturnStruct->mtime = {0}; TODO
4378 currentReturnStruct->ctime = {0};
4379 currentReturnStruct->atime = {0};
4381 destination->grfMode = 0;
4382 destination->grfLocksSupported = 0;
4383 destination->clsid = source->propertyUniqueID;
4384 destination->grfStateBits = 0;
4385 destination->reserved = 0;
4388 /******************************************************************************
4389 ** BlockChainStream implementation
4392 BlockChainStream* BlockChainStream_Construct(
4393 StorageImpl* parentStorage,
4394 ULONG* headOfStreamPlaceHolder,
4395 ULONG propertyIndex)
4397 BlockChainStream* newStream;
4400 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4402 newStream->parentStorage = parentStorage;
4403 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4404 newStream->ownerPropertyIndex = propertyIndex;
4405 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4406 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4407 newStream->numBlocks = 0;
4409 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4411 while (blockIndex != BLOCK_END_OF_CHAIN)
4413 newStream->numBlocks++;
4414 newStream->tailIndex = blockIndex;
4416 if(FAILED(StorageImpl_GetNextBlockInChain(
4421 HeapFree(GetProcessHeap(), 0, newStream);
4429 void BlockChainStream_Destroy(BlockChainStream* This)
4431 HeapFree(GetProcessHeap(), 0, This);
4434 /******************************************************************************
4435 * BlockChainStream_GetHeadOfChain
4437 * Returns the head of this stream chain.
4438 * Some special chains don't have properties, their heads are kept in
4439 * This->headOfStreamPlaceHolder.
4442 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4444 StgProperty chainProperty;
4445 BOOL readSuccessful;
4447 if (This->headOfStreamPlaceHolder != 0)
4448 return *(This->headOfStreamPlaceHolder);
4450 if (This->ownerPropertyIndex != PROPERTY_NULL)
4452 readSuccessful = StorageImpl_ReadProperty(
4453 This->parentStorage,
4454 This->ownerPropertyIndex,
4459 return chainProperty.startingBlock;
4463 return BLOCK_END_OF_CHAIN;
4466 /******************************************************************************
4467 * BlockChainStream_GetCount
4469 * Returns the number of blocks that comprises this chain.
4470 * This is not the size of the stream as the last block may not be full!
4473 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
4478 blockIndex = BlockChainStream_GetHeadOfChain(This);
4480 while (blockIndex != BLOCK_END_OF_CHAIN)
4484 if(FAILED(StorageImpl_GetNextBlockInChain(
4485 This->parentStorage,
4494 /******************************************************************************
4495 * BlockChainStream_ReadAt
4497 * Reads a specified number of bytes from this chain at the specified offset.
4498 * bytesRead may be NULL.
4499 * Failure will be returned if the specified number of bytes has not been read.
4501 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
4502 ULARGE_INTEGER offset,
4507 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4508 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4509 ULONG bytesToReadInBuffer;
4513 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
4516 * Find the first block in the stream that contains part of the buffer.
4518 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4519 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4520 (blockNoInSequence < This->lastBlockNoInSequence) )
4522 blockIndex = BlockChainStream_GetHeadOfChain(This);
4523 This->lastBlockNoInSequence = blockNoInSequence;
4527 ULONG temp = blockNoInSequence;
4529 blockIndex = This->lastBlockNoInSequenceIndex;
4530 blockNoInSequence -= This->lastBlockNoInSequence;
4531 This->lastBlockNoInSequence = temp;
4534 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4536 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4537 return STG_E_DOCFILECORRUPT;
4538 blockNoInSequence--;
4541 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4542 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
4544 This->lastBlockNoInSequenceIndex = blockIndex;
4547 * Start reading the buffer.
4550 bufferWalker = buffer;
4552 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4554 ULARGE_INTEGER ulOffset;
4557 * Calculate how many bytes we can copy from this big block.
4559 bytesToReadInBuffer =
4560 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4562 TRACE("block %i\n",blockIndex);
4563 ulOffset.u.HighPart = 0;
4564 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4567 StorageImpl_ReadAt(This->parentStorage,
4570 bytesToReadInBuffer,
4573 * Step to the next big block.
4575 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4576 return STG_E_DOCFILECORRUPT;
4578 bufferWalker += bytesReadAt;
4579 size -= bytesReadAt;
4580 *bytesRead += bytesReadAt;
4581 offsetInBlock = 0; /* There is no offset on the next block */
4583 if (bytesToReadInBuffer != bytesReadAt)
4587 return (size == 0) ? S_OK : STG_E_READFAULT;
4590 /******************************************************************************
4591 * BlockChainStream_WriteAt
4593 * Writes the specified number of bytes to this chain at the specified offset.
4594 * bytesWritten may be NULL.
4595 * Will fail if not all specified number of bytes have been written.
4597 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
4598 ULARGE_INTEGER offset,
4601 ULONG* bytesWritten)
4603 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4604 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4607 const BYTE* bufferWalker;
4610 * Find the first block in the stream that contains part of the buffer.
4612 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4613 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4614 (blockNoInSequence < This->lastBlockNoInSequence) )
4616 blockIndex = BlockChainStream_GetHeadOfChain(This);
4617 This->lastBlockNoInSequence = blockNoInSequence;
4621 ULONG temp = blockNoInSequence;
4623 blockIndex = This->lastBlockNoInSequenceIndex;
4624 blockNoInSequence -= This->lastBlockNoInSequence;
4625 This->lastBlockNoInSequence = temp;
4628 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4630 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4632 return STG_E_DOCFILECORRUPT;
4633 blockNoInSequence--;
4636 This->lastBlockNoInSequenceIndex = blockIndex;
4638 /* BlockChainStream_SetSize should have already been called to ensure we have
4639 * enough blocks in the chain to write into */
4640 if (blockIndex == BLOCK_END_OF_CHAIN)
4642 ERR("not enough blocks in chain to write data\n");
4643 return STG_E_DOCFILECORRUPT;
4647 bufferWalker = (const BYTE*)buffer;
4649 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4651 ULARGE_INTEGER ulOffset;
4652 DWORD bytesWrittenAt;
4654 * Calculate how many bytes we can copy from this big block.
4657 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4659 TRACE("block %i\n",blockIndex);
4660 ulOffset.u.HighPart = 0;
4661 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4664 StorageImpl_WriteAt(This->parentStorage,
4671 * Step to the next big block.
4673 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4675 return STG_E_DOCFILECORRUPT;
4677 bufferWalker += bytesWrittenAt;
4678 size -= bytesWrittenAt;
4679 *bytesWritten += bytesWrittenAt;
4680 offsetInBlock = 0; /* There is no offset on the next block */
4682 if (bytesWrittenAt != bytesToWrite)
4686 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
4689 /******************************************************************************
4690 * BlockChainStream_Shrink
4692 * Shrinks this chain in the big block depot.
4694 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
4695 ULARGE_INTEGER newSize)
4697 ULONG blockIndex, extraBlock;
4702 * Reset the last accessed block cache.
4704 This->lastBlockNoInSequence = 0xFFFFFFFF;
4705 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4708 * Figure out how many blocks are needed to contain the new size
4710 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4712 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4715 blockIndex = BlockChainStream_GetHeadOfChain(This);
4718 * Go to the new end of chain
4720 while (count < numBlocks)
4722 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4728 /* Get the next block before marking the new end */
4729 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4733 /* Mark the new end of chain */
4734 StorageImpl_SetNextBlockInChain(
4735 This->parentStorage,
4737 BLOCK_END_OF_CHAIN);
4739 This->tailIndex = blockIndex;
4740 This->numBlocks = numBlocks;
4743 * Mark the extra blocks as free
4745 while (extraBlock != BLOCK_END_OF_CHAIN)
4747 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4750 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4751 extraBlock = blockIndex;
4757 /******************************************************************************
4758 * BlockChainStream_Enlarge
4760 * Grows this chain in the big block depot.
4762 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4763 ULARGE_INTEGER newSize)
4765 ULONG blockIndex, currentBlock;
4767 ULONG oldNumBlocks = 0;
4769 blockIndex = BlockChainStream_GetHeadOfChain(This);
4772 * Empty chain. Create the head.
4774 if (blockIndex == BLOCK_END_OF_CHAIN)
4776 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4777 StorageImpl_SetNextBlockInChain(This->parentStorage,
4779 BLOCK_END_OF_CHAIN);
4781 if (This->headOfStreamPlaceHolder != 0)
4783 *(This->headOfStreamPlaceHolder) = blockIndex;
4787 StgProperty chainProp;
4788 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4790 StorageImpl_ReadProperty(
4791 This->parentStorage,
4792 This->ownerPropertyIndex,
4795 chainProp.startingBlock = blockIndex;
4797 StorageImpl_WriteProperty(
4798 This->parentStorage,
4799 This->ownerPropertyIndex,
4803 This->tailIndex = blockIndex;
4804 This->numBlocks = 1;
4808 * Figure out how many blocks are needed to contain this stream
4810 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4812 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4816 * Go to the current end of chain
4818 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4820 currentBlock = blockIndex;
4822 while (blockIndex != BLOCK_END_OF_CHAIN)
4825 currentBlock = blockIndex;
4827 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4832 This->tailIndex = currentBlock;
4835 currentBlock = This->tailIndex;
4836 oldNumBlocks = This->numBlocks;
4839 * Add new blocks to the chain
4841 if (oldNumBlocks < newNumBlocks)
4843 while (oldNumBlocks < newNumBlocks)
4845 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4847 StorageImpl_SetNextBlockInChain(
4848 This->parentStorage,
4852 StorageImpl_SetNextBlockInChain(
4853 This->parentStorage,
4855 BLOCK_END_OF_CHAIN);
4857 currentBlock = blockIndex;
4861 This->tailIndex = blockIndex;
4862 This->numBlocks = newNumBlocks;
4868 /******************************************************************************
4869 * BlockChainStream_SetSize
4871 * Sets the size of this stream. The big block depot will be updated.
4872 * The file will grow if we grow the chain.
4874 * TODO: Free the actual blocks in the file when we shrink the chain.
4875 * Currently, the blocks are still in the file. So the file size
4876 * doesn't shrink even if we shrink streams.
4878 BOOL BlockChainStream_SetSize(
4879 BlockChainStream* This,
4880 ULARGE_INTEGER newSize)
4882 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4884 if (newSize.u.LowPart == size.u.LowPart)
4887 if (newSize.u.LowPart < size.u.LowPart)
4889 BlockChainStream_Shrink(This, newSize);
4893 BlockChainStream_Enlarge(This, newSize);
4899 /******************************************************************************
4900 * BlockChainStream_GetSize
4902 * Returns the size of this chain.
4903 * Will return the block count if this chain doesn't have a property.
4905 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4907 StgProperty chainProperty;
4909 if(This->headOfStreamPlaceHolder == NULL)
4912 * This chain is a data stream read the property and return
4913 * the appropriate size
4915 StorageImpl_ReadProperty(
4916 This->parentStorage,
4917 This->ownerPropertyIndex,
4920 return chainProperty.size;
4925 * this chain is a chain that does not have a property, figure out the
4926 * size by making the product number of used blocks times the
4929 ULARGE_INTEGER result;
4930 result.u.HighPart = 0;
4933 BlockChainStream_GetCount(This) *
4934 This->parentStorage->bigBlockSize;
4940 /******************************************************************************
4941 ** SmallBlockChainStream implementation
4944 SmallBlockChainStream* SmallBlockChainStream_Construct(
4945 StorageImpl* parentStorage,
4946 ULONG propertyIndex)
4948 SmallBlockChainStream* newStream;
4950 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4952 newStream->parentStorage = parentStorage;
4953 newStream->ownerPropertyIndex = propertyIndex;
4958 void SmallBlockChainStream_Destroy(
4959 SmallBlockChainStream* This)
4961 HeapFree(GetProcessHeap(), 0, This);
4964 /******************************************************************************
4965 * SmallBlockChainStream_GetHeadOfChain
4967 * Returns the head of this chain of small blocks.
4969 static ULONG SmallBlockChainStream_GetHeadOfChain(
4970 SmallBlockChainStream* This)
4972 StgProperty chainProperty;
4973 BOOL readSuccessful;
4975 if (This->ownerPropertyIndex)
4977 readSuccessful = StorageImpl_ReadProperty(
4978 This->parentStorage,
4979 This->ownerPropertyIndex,
4984 return chainProperty.startingBlock;
4989 return BLOCK_END_OF_CHAIN;
4992 /******************************************************************************
4993 * SmallBlockChainStream_GetNextBlockInChain
4995 * Returns the index of the next small block in this chain.
4998 * - BLOCK_END_OF_CHAIN: end of this chain
4999 * - BLOCK_UNUSED: small block 'blockIndex' is free
5001 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
5002 SmallBlockChainStream* This,
5004 ULONG* nextBlockInChain)
5006 ULARGE_INTEGER offsetOfBlockInDepot;
5011 *nextBlockInChain = BLOCK_END_OF_CHAIN;
5013 offsetOfBlockInDepot.u.HighPart = 0;
5014 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5017 * Read those bytes in the buffer from the small block file.
5019 res = BlockChainStream_ReadAt(
5020 This->parentStorage->smallBlockDepotChain,
5021 offsetOfBlockInDepot,
5028 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
5035 /******************************************************************************
5036 * SmallBlockChainStream_SetNextBlockInChain
5038 * Writes the index of the next block of the specified block in the small
5040 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5041 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5043 static void SmallBlockChainStream_SetNextBlockInChain(
5044 SmallBlockChainStream* This,
5048 ULARGE_INTEGER offsetOfBlockInDepot;
5052 offsetOfBlockInDepot.u.HighPart = 0;
5053 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5055 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
5058 * Read those bytes in the buffer from the small block file.
5060 BlockChainStream_WriteAt(
5061 This->parentStorage->smallBlockDepotChain,
5062 offsetOfBlockInDepot,
5068 /******************************************************************************
5069 * SmallBlockChainStream_FreeBlock
5071 * Flag small block 'blockIndex' as free in the small block depot.
5073 static void SmallBlockChainStream_FreeBlock(
5074 SmallBlockChainStream* This,
5077 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5080 /******************************************************************************
5081 * SmallBlockChainStream_GetNextFreeBlock
5083 * Returns the index of a free small block. The small block depot will be
5084 * enlarged if necessary. The small block chain will also be enlarged if
5087 static ULONG SmallBlockChainStream_GetNextFreeBlock(
5088 SmallBlockChainStream* This)
5090 ULARGE_INTEGER offsetOfBlockInDepot;
5093 ULONG blockIndex = 0;
5094 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5096 ULONG smallBlocksPerBigBlock;
5098 offsetOfBlockInDepot.u.HighPart = 0;
5101 * Scan the small block depot for a free block
5103 while (nextBlockIndex != BLOCK_UNUSED)
5105 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5107 res = BlockChainStream_ReadAt(
5108 This->parentStorage->smallBlockDepotChain,
5109 offsetOfBlockInDepot,
5115 * If we run out of space for the small block depot, enlarge it
5119 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5121 if (nextBlockIndex != BLOCK_UNUSED)
5127 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5129 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5130 ULONG nextBlock, newsbdIndex;
5131 BYTE smallBlockDepot[BIG_BLOCK_SIZE];
5133 nextBlock = sbdIndex;
5134 while (nextBlock != BLOCK_END_OF_CHAIN)
5136 sbdIndex = nextBlock;
5137 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5140 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5141 if (sbdIndex != BLOCK_END_OF_CHAIN)
5142 StorageImpl_SetNextBlockInChain(
5143 This->parentStorage,
5147 StorageImpl_SetNextBlockInChain(
5148 This->parentStorage,
5150 BLOCK_END_OF_CHAIN);
5153 * Initialize all the small blocks to free
5155 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5156 StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
5161 * We have just created the small block depot.
5163 StgProperty rootProp;
5167 * Save it in the header
5169 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5170 StorageImpl_SaveFileHeader(This->parentStorage);
5173 * And allocate the first big block that will contain small blocks
5176 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5178 StorageImpl_SetNextBlockInChain(
5179 This->parentStorage,
5181 BLOCK_END_OF_CHAIN);
5183 StorageImpl_ReadProperty(
5184 This->parentStorage,
5185 This->parentStorage->base.rootPropertySetIndex,
5188 rootProp.startingBlock = sbStartIndex;
5189 rootProp.size.u.HighPart = 0;
5190 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
5192 StorageImpl_WriteProperty(
5193 This->parentStorage,
5194 This->parentStorage->base.rootPropertySetIndex,
5200 smallBlocksPerBigBlock =
5201 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5204 * Verify if we have to allocate big blocks to contain small blocks
5206 if (blockIndex % smallBlocksPerBigBlock == 0)
5208 StgProperty rootProp;
5209 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5211 StorageImpl_ReadProperty(
5212 This->parentStorage,
5213 This->parentStorage->base.rootPropertySetIndex,
5216 if (rootProp.size.u.LowPart <
5217 (blocksRequired * This->parentStorage->bigBlockSize))
5219 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5221 BlockChainStream_SetSize(
5222 This->parentStorage->smallBlockRootChain,
5225 StorageImpl_WriteProperty(
5226 This->parentStorage,
5227 This->parentStorage->base.rootPropertySetIndex,
5235 /******************************************************************************
5236 * SmallBlockChainStream_ReadAt
5238 * Reads a specified number of bytes from this chain at the specified offset.
5239 * bytesRead may be NULL.
5240 * Failure will be returned if the specified number of bytes has not been read.
5242 HRESULT SmallBlockChainStream_ReadAt(
5243 SmallBlockChainStream* This,
5244 ULARGE_INTEGER offset,
5250 ULARGE_INTEGER offsetInBigBlockFile;
5251 ULONG blockNoInSequence =
5252 offset.u.LowPart / This->parentStorage->smallBlockSize;
5254 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5255 ULONG bytesToReadInBuffer;
5257 ULONG bytesReadFromBigBlockFile;
5261 * This should never happen on a small block file.
5263 assert(offset.u.HighPart==0);
5266 * Find the first block in the stream that contains part of the buffer.
5268 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5270 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5272 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5275 blockNoInSequence--;
5279 * Start reading the buffer.
5282 bufferWalker = buffer;
5284 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5287 * Calculate how many bytes we can copy from this small block.
5289 bytesToReadInBuffer =
5290 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5293 * Calculate the offset of the small block in the small block file.
5295 offsetInBigBlockFile.u.HighPart = 0;
5296 offsetInBigBlockFile.u.LowPart =
5297 blockIndex * This->parentStorage->smallBlockSize;
5299 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5302 * Read those bytes in the buffer from the small block file.
5303 * The small block has already been identified so it shouldn't fail
5304 * unless the file is corrupt.
5306 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5307 offsetInBigBlockFile,
5308 bytesToReadInBuffer,
5310 &bytesReadFromBigBlockFile);
5316 * Step to the next big block.
5318 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5320 return STG_E_DOCFILECORRUPT;
5322 bufferWalker += bytesReadFromBigBlockFile;
5323 size -= bytesReadFromBigBlockFile;
5324 *bytesRead += bytesReadFromBigBlockFile;
5325 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5328 return (size == 0) ? S_OK : STG_E_READFAULT;
5331 /******************************************************************************
5332 * SmallBlockChainStream_WriteAt
5334 * Writes the specified number of bytes to this chain at the specified offset.
5335 * bytesWritten may be NULL.
5336 * Will fail if not all specified number of bytes have been written.
5338 HRESULT SmallBlockChainStream_WriteAt(
5339 SmallBlockChainStream* This,
5340 ULARGE_INTEGER offset,
5343 ULONG* bytesWritten)
5345 ULARGE_INTEGER offsetInBigBlockFile;
5346 ULONG blockNoInSequence =
5347 offset.u.LowPart / This->parentStorage->smallBlockSize;
5349 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5350 ULONG bytesToWriteInBuffer;
5352 ULONG bytesWrittenToBigBlockFile;
5353 const BYTE* bufferWalker;
5357 * This should never happen on a small block file.
5359 assert(offset.u.HighPart==0);
5362 * Find the first block in the stream that contains part of the buffer.
5364 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5366 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5368 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5369 return STG_E_DOCFILECORRUPT;
5370 blockNoInSequence--;
5374 * Start writing the buffer.
5376 * Here, I'm casting away the constness on the buffer variable
5377 * This is OK since we don't intend to modify that buffer.
5380 bufferWalker = (const BYTE*)buffer;
5381 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5384 * Calculate how many bytes we can copy to this small block.
5386 bytesToWriteInBuffer =
5387 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5390 * Calculate the offset of the small block in the small block file.
5392 offsetInBigBlockFile.u.HighPart = 0;
5393 offsetInBigBlockFile.u.LowPart =
5394 blockIndex * This->parentStorage->smallBlockSize;
5396 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5399 * Write those bytes in the buffer to the small block file.
5401 res = BlockChainStream_WriteAt(
5402 This->parentStorage->smallBlockRootChain,
5403 offsetInBigBlockFile,
5404 bytesToWriteInBuffer,
5406 &bytesWrittenToBigBlockFile);
5411 * Step to the next big block.
5413 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5416 bufferWalker += bytesWrittenToBigBlockFile;
5417 size -= bytesWrittenToBigBlockFile;
5418 *bytesWritten += bytesWrittenToBigBlockFile;
5419 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
5422 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5425 /******************************************************************************
5426 * SmallBlockChainStream_Shrink
5428 * Shrinks this chain in the small block depot.
5430 static BOOL SmallBlockChainStream_Shrink(
5431 SmallBlockChainStream* This,
5432 ULARGE_INTEGER newSize)
5434 ULONG blockIndex, extraBlock;
5438 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5440 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5443 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5446 * Go to the new end of chain
5448 while (count < numBlocks)
5450 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5457 * If the count is 0, we have a special case, the head of the chain was
5462 StgProperty chainProp;
5464 StorageImpl_ReadProperty(This->parentStorage,
5465 This->ownerPropertyIndex,
5468 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5470 StorageImpl_WriteProperty(This->parentStorage,
5471 This->ownerPropertyIndex,
5475 * We start freeing the chain at the head block.
5477 extraBlock = blockIndex;
5481 /* Get the next block before marking the new end */
5482 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5486 /* Mark the new end of chain */
5487 SmallBlockChainStream_SetNextBlockInChain(
5490 BLOCK_END_OF_CHAIN);
5494 * Mark the extra blocks as free
5496 while (extraBlock != BLOCK_END_OF_CHAIN)
5498 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5501 SmallBlockChainStream_FreeBlock(This, extraBlock);
5502 extraBlock = blockIndex;
5508 /******************************************************************************
5509 * SmallBlockChainStream_Enlarge
5511 * Grows this chain in the small block depot.
5513 static BOOL SmallBlockChainStream_Enlarge(
5514 SmallBlockChainStream* This,
5515 ULARGE_INTEGER newSize)
5517 ULONG blockIndex, currentBlock;
5519 ULONG oldNumBlocks = 0;
5521 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5526 if (blockIndex == BLOCK_END_OF_CHAIN)
5529 StgProperty chainProp;
5531 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5534 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5536 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5539 blockIndex = chainProp.startingBlock;
5540 SmallBlockChainStream_SetNextBlockInChain(
5543 BLOCK_END_OF_CHAIN);
5546 currentBlock = blockIndex;
5549 * Figure out how many blocks are needed to contain this stream
5551 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5553 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5557 * Go to the current end of chain
5559 while (blockIndex != BLOCK_END_OF_CHAIN)
5562 currentBlock = blockIndex;
5563 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5568 * Add new blocks to the chain
5570 while (oldNumBlocks < newNumBlocks)
5572 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5573 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5575 SmallBlockChainStream_SetNextBlockInChain(
5578 BLOCK_END_OF_CHAIN);
5580 currentBlock = blockIndex;
5587 /******************************************************************************
5588 * SmallBlockChainStream_SetSize
5590 * Sets the size of this stream.
5591 * The file will grow if we grow the chain.
5593 * TODO: Free the actual blocks in the file when we shrink the chain.
5594 * Currently, the blocks are still in the file. So the file size
5595 * doesn't shrink even if we shrink streams.
5597 BOOL SmallBlockChainStream_SetSize(
5598 SmallBlockChainStream* This,
5599 ULARGE_INTEGER newSize)
5601 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5603 if (newSize.u.LowPart == size.u.LowPart)
5606 if (newSize.u.LowPart < size.u.LowPart)
5608 SmallBlockChainStream_Shrink(This, newSize);
5612 SmallBlockChainStream_Enlarge(This, newSize);
5618 /******************************************************************************
5619 * SmallBlockChainStream_GetSize
5621 * Returns the size of this chain.
5623 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5625 StgProperty chainProperty;
5627 StorageImpl_ReadProperty(
5628 This->parentStorage,
5629 This->ownerPropertyIndex,
5632 return chainProperty.size;
5635 /******************************************************************************
5636 * StgCreateDocfile [OLE32.@]
5637 * Creates a new compound file storage object
5640 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5641 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5642 * reserved [ ?] unused?, usually 0
5643 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5646 * S_OK if the file was successfully created
5647 * some STG_E_ value if error
5649 * if pwcsName is NULL, create file with new unique name
5650 * the function can returns
5651 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5654 HRESULT WINAPI StgCreateDocfile(
5658 IStorage **ppstgOpen)
5660 StorageImpl* newStorage = 0;
5661 HANDLE hFile = INVALID_HANDLE_VALUE;
5662 HRESULT hr = STG_E_INVALIDFLAG;
5666 DWORD fileAttributes;
5667 WCHAR tempFileName[MAX_PATH];
5669 TRACE("(%s, %x, %d, %p)\n",
5670 debugstr_w(pwcsName), grfMode,
5671 reserved, ppstgOpen);
5674 * Validate the parameters
5677 return STG_E_INVALIDPOINTER;
5679 return STG_E_INVALIDPARAMETER;
5681 /* if no share mode given then DENY_NONE is the default */
5682 if (STGM_SHARE_MODE(grfMode) == 0)
5683 grfMode |= STGM_SHARE_DENY_NONE;
5686 * Validate the STGM flags
5688 if ( FAILED( validateSTGM(grfMode) ))
5691 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5692 switch(STGM_ACCESS_MODE(grfMode))
5695 case STGM_READWRITE:
5701 /* in direct mode, can only use SHARE_EXCLUSIVE */
5702 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5705 /* but in transacted mode, any share mode is valid */
5708 * Generate a unique name.
5712 WCHAR tempPath[MAX_PATH];
5713 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5715 memset(tempPath, 0, sizeof(tempPath));
5716 memset(tempFileName, 0, sizeof(tempFileName));
5718 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5721 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5722 pwcsName = tempFileName;
5725 hr = STG_E_INSUFFICIENTMEMORY;
5729 creationMode = TRUNCATE_EXISTING;
5733 creationMode = GetCreationModeFromSTGM(grfMode);
5737 * Interpret the STGM value grfMode
5739 shareMode = GetShareModeFromSTGM(grfMode);
5740 accessMode = GetAccessModeFromSTGM(grfMode);
5742 if (grfMode & STGM_DELETEONRELEASE)
5743 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5745 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5747 if (grfMode & STGM_TRANSACTED)
5748 FIXME("Transacted mode not implemented.\n");
5751 * Initialize the "out" parameter.
5755 hFile = CreateFileW(pwcsName,
5763 if (hFile == INVALID_HANDLE_VALUE)
5765 if(GetLastError() == ERROR_FILE_EXISTS)
5766 hr = STG_E_FILEALREADYEXISTS;
5773 * Allocate and initialize the new IStorage32object.
5775 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5777 if (newStorage == 0)
5779 hr = STG_E_INSUFFICIENTMEMORY;
5783 hr = StorageImpl_Construct(
5794 HeapFree(GetProcessHeap(), 0, newStorage);
5799 * Get an "out" pointer for the caller.
5801 hr = StorageBaseImpl_QueryInterface(
5802 (IStorage*)newStorage,
5803 (REFIID)&IID_IStorage,
5806 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
5811 /******************************************************************************
5812 * StgCreateStorageEx [OLE32.@]
5814 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5816 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5817 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5819 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5821 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5822 return STG_E_INVALIDPARAMETER;
5825 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5827 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5828 return STG_E_INVALIDPARAMETER;
5831 if (stgfmt == STGFMT_FILE)
5833 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5834 return STG_E_INVALIDPARAMETER;
5837 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5839 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5840 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5843 ERR("Invalid stgfmt argument\n");
5844 return STG_E_INVALIDPARAMETER;
5847 /******************************************************************************
5848 * StgCreatePropSetStg [OLE32.@]
5850 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5851 IPropertySetStorage **ppPropSetStg)
5855 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
5857 hr = STG_E_INVALIDPARAMETER;
5859 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5860 (void**)ppPropSetStg);
5864 /******************************************************************************
5865 * StgOpenStorageEx [OLE32.@]
5867 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5869 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5870 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5872 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5874 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5875 return STG_E_INVALIDPARAMETER;
5881 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5882 return STG_E_INVALIDPARAMETER;
5884 case STGFMT_STORAGE:
5887 case STGFMT_DOCFILE:
5888 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
5890 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5891 return STG_E_INVALIDPARAMETER;
5893 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5897 WARN("STGFMT_ANY assuming storage\n");
5901 return STG_E_INVALIDPARAMETER;
5904 return StgOpenStorage(pwcsName, NULL, grfMode, (SNB)NULL, 0, (IStorage **)ppObjectOpen);
5908 /******************************************************************************
5909 * StgOpenStorage [OLE32.@]
5911 HRESULT WINAPI StgOpenStorage(
5912 const OLECHAR *pwcsName,
5913 IStorage *pstgPriority,
5917 IStorage **ppstgOpen)
5919 StorageImpl* newStorage = 0;
5924 WCHAR fullname[MAX_PATH];
5926 TRACE("(%s, %p, %x, %p, %d, %p)\n",
5927 debugstr_w(pwcsName), pstgPriority, grfMode,
5928 snbExclude, reserved, ppstgOpen);
5931 * Perform sanity checks
5935 hr = STG_E_INVALIDNAME;
5941 hr = STG_E_INVALIDPOINTER;
5947 hr = STG_E_INVALIDPARAMETER;
5951 if (grfMode & STGM_PRIORITY)
5953 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
5954 return STG_E_INVALIDFLAG;
5955 if (grfMode & STGM_DELETEONRELEASE)
5956 return STG_E_INVALIDFUNCTION;
5957 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
5958 return STG_E_INVALIDFLAG;
5959 grfMode &= ~0xf0; /* remove the existing sharing mode */
5960 grfMode |= STGM_SHARE_DENY_NONE;
5962 /* STGM_PRIORITY stops other IStorage objects on the same file from
5963 * committing until the STGM_PRIORITY IStorage is closed. it also
5964 * stops non-transacted mode StgOpenStorage calls with write access from
5965 * succeeding. obviously, both of these cannot be achieved through just
5966 * file share flags */
5967 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5971 * Validate the sharing mode
5973 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
5974 switch(STGM_SHARE_MODE(grfMode))
5976 case STGM_SHARE_EXCLUSIVE:
5977 case STGM_SHARE_DENY_WRITE:
5980 hr = STG_E_INVALIDFLAG;
5985 * Validate the STGM flags
5987 if ( FAILED( validateSTGM(grfMode) ) ||
5988 (grfMode&STGM_CREATE))
5990 hr = STG_E_INVALIDFLAG;
5994 /* shared reading requires transacted mode */
5995 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5996 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
5997 !(grfMode&STGM_TRANSACTED) )
5999 hr = STG_E_INVALIDFLAG;
6004 * Interpret the STGM value grfMode
6006 shareMode = GetShareModeFromSTGM(grfMode);
6007 accessMode = GetAccessModeFromSTGM(grfMode);
6010 * Initialize the "out" parameter.
6014 hFile = CreateFileW( pwcsName,
6019 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
6022 if (hFile==INVALID_HANDLE_VALUE)
6024 DWORD last_error = GetLastError();
6030 case ERROR_FILE_NOT_FOUND:
6031 hr = STG_E_FILENOTFOUND;
6034 case ERROR_PATH_NOT_FOUND:
6035 hr = STG_E_PATHNOTFOUND;
6038 case ERROR_ACCESS_DENIED:
6039 case ERROR_WRITE_PROTECT:
6040 hr = STG_E_ACCESSDENIED;
6043 case ERROR_SHARING_VIOLATION:
6044 hr = STG_E_SHAREVIOLATION;
6055 * Refuse to open the file if it's too small to be a structured storage file
6056 * FIXME: verify the file when reading instead of here
6058 if (GetFileSize(hFile, NULL) < 0x100)
6061 hr = STG_E_FILEALREADYEXISTS;
6066 * Allocate and initialize the new IStorage32object.
6068 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6070 if (newStorage == 0)
6072 hr = STG_E_INSUFFICIENTMEMORY;
6076 /* Initialize the storage */
6077 hr = StorageImpl_Construct(
6088 HeapFree(GetProcessHeap(), 0, newStorage);
6090 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6092 if(hr == STG_E_INVALIDHEADER)
6093 hr = STG_E_FILEALREADYEXISTS;
6097 /* prepare the file name string given in lieu of the root property name */
6098 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
6099 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
6100 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
6103 * Get an "out" pointer for the caller.
6105 hr = StorageBaseImpl_QueryInterface(
6106 (IStorage*)newStorage,
6107 (REFIID)&IID_IStorage,
6111 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6115 /******************************************************************************
6116 * StgCreateDocfileOnILockBytes [OLE32.@]
6118 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6122 IStorage** ppstgOpen)
6124 StorageImpl* newStorage = 0;
6128 * Validate the parameters
6130 if ((ppstgOpen == 0) || (plkbyt == 0))
6131 return STG_E_INVALIDPOINTER;
6134 * Allocate and initialize the new IStorage object.
6136 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6138 if (newStorage == 0)
6139 return STG_E_INSUFFICIENTMEMORY;
6141 hr = StorageImpl_Construct(
6152 HeapFree(GetProcessHeap(), 0, newStorage);
6157 * Get an "out" pointer for the caller.
6159 hr = StorageBaseImpl_QueryInterface(
6160 (IStorage*)newStorage,
6161 (REFIID)&IID_IStorage,
6167 /******************************************************************************
6168 * StgOpenStorageOnILockBytes [OLE32.@]
6170 HRESULT WINAPI StgOpenStorageOnILockBytes(
6172 IStorage *pstgPriority,
6176 IStorage **ppstgOpen)
6178 StorageImpl* newStorage = 0;
6182 * Perform a sanity check
6184 if ((plkbyt == 0) || (ppstgOpen == 0))
6185 return STG_E_INVALIDPOINTER;
6188 * Validate the STGM flags
6190 if ( FAILED( validateSTGM(grfMode) ))
6191 return STG_E_INVALIDFLAG;
6194 * Initialize the "out" parameter.
6199 * Allocate and initialize the new IStorage object.
6201 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6203 if (newStorage == 0)
6204 return STG_E_INSUFFICIENTMEMORY;
6206 hr = StorageImpl_Construct(
6217 HeapFree(GetProcessHeap(), 0, newStorage);
6222 * Get an "out" pointer for the caller.
6224 hr = StorageBaseImpl_QueryInterface(
6225 (IStorage*)newStorage,
6226 (REFIID)&IID_IStorage,
6232 /******************************************************************************
6233 * StgSetTimes [ole32.@]
6234 * StgSetTimes [OLE32.@]
6238 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6239 FILETIME const *patime, FILETIME const *pmtime)
6241 IStorage *stg = NULL;
6244 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6246 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6250 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6251 IStorage_Release(stg);
6257 /******************************************************************************
6258 * StgIsStorageILockBytes [OLE32.@]
6260 * Determines if the ILockBytes contains a storage object.
6262 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6265 ULARGE_INTEGER offset;
6267 offset.u.HighPart = 0;
6268 offset.u.LowPart = 0;
6270 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6272 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6278 /******************************************************************************
6279 * WriteClassStg [OLE32.@]
6281 * This method will store the specified CLSID in the specified storage object
6283 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6288 return E_INVALIDARG;
6290 hRes = IStorage_SetClass(pStg, rclsid);
6295 /***********************************************************************
6296 * ReadClassStg (OLE32.@)
6298 * This method reads the CLSID previously written to a storage object with
6299 * the WriteClassStg.
6302 * pstg [I] IStorage pointer
6303 * pclsid [O] Pointer to where the CLSID is written
6307 * Failure: HRESULT code.
6309 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6314 TRACE("(%p, %p)\n", pstg, pclsid);
6316 if(!pstg || !pclsid)
6317 return E_INVALIDARG;
6320 * read a STATSTG structure (contains the clsid) from the storage
6322 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
6325 *pclsid=pstatstg.clsid;
6330 /***********************************************************************
6331 * OleLoadFromStream (OLE32.@)
6333 * This function loads an object from stream
6335 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6339 LPPERSISTSTREAM xstm;
6341 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6343 res=ReadClassStm(pStm,&clsid);
6344 if (!SUCCEEDED(res))
6346 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6347 if (!SUCCEEDED(res))
6349 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6350 if (!SUCCEEDED(res)) {
6351 IUnknown_Release((IUnknown*)*ppvObj);
6354 res=IPersistStream_Load(xstm,pStm);
6355 IPersistStream_Release(xstm);
6356 /* FIXME: all refcounts ok at this point? I think they should be:
6359 * xstm : 0 (released)
6364 /***********************************************************************
6365 * OleSaveToStream (OLE32.@)
6367 * This function saves an object with the IPersistStream interface on it
6368 * to the specified stream.
6370 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6376 TRACE("(%p,%p)\n",pPStm,pStm);
6378 res=IPersistStream_GetClassID(pPStm,&clsid);
6380 if (SUCCEEDED(res)){
6382 res=WriteClassStm(pStm,&clsid);
6386 res=IPersistStream_Save(pPStm,pStm,TRUE);
6389 TRACE("Finished Save\n");
6393 /****************************************************************************
6394 * This method validate a STGM parameter that can contain the values below
6396 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6397 * The stgm values contained in 0xffff0000 are bitmasks.
6399 * STGM_DIRECT 0x00000000
6400 * STGM_TRANSACTED 0x00010000
6401 * STGM_SIMPLE 0x08000000
6403 * STGM_READ 0x00000000
6404 * STGM_WRITE 0x00000001
6405 * STGM_READWRITE 0x00000002
6407 * STGM_SHARE_DENY_NONE 0x00000040
6408 * STGM_SHARE_DENY_READ 0x00000030
6409 * STGM_SHARE_DENY_WRITE 0x00000020
6410 * STGM_SHARE_EXCLUSIVE 0x00000010
6412 * STGM_PRIORITY 0x00040000
6413 * STGM_DELETEONRELEASE 0x04000000
6415 * STGM_CREATE 0x00001000
6416 * STGM_CONVERT 0x00020000
6417 * STGM_FAILIFTHERE 0x00000000
6419 * STGM_NOSCRATCH 0x00100000
6420 * STGM_NOSNAPSHOT 0x00200000
6422 static HRESULT validateSTGM(DWORD stgm)
6424 DWORD access = STGM_ACCESS_MODE(stgm);
6425 DWORD share = STGM_SHARE_MODE(stgm);
6426 DWORD create = STGM_CREATE_MODE(stgm);
6428 if (stgm&~STGM_KNOWN_FLAGS)
6430 ERR("unknown flags %08x\n", stgm);
6438 case STGM_READWRITE:
6446 case STGM_SHARE_DENY_NONE:
6447 case STGM_SHARE_DENY_READ:
6448 case STGM_SHARE_DENY_WRITE:
6449 case STGM_SHARE_EXCLUSIVE:
6458 case STGM_FAILIFTHERE:
6465 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6467 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6471 * STGM_CREATE | STGM_CONVERT
6472 * if both are false, STGM_FAILIFTHERE is set to TRUE
6474 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6478 * STGM_NOSCRATCH requires STGM_TRANSACTED
6480 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6484 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6485 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6487 if ( (stgm & STGM_NOSNAPSHOT) &&
6488 (!(stgm & STGM_TRANSACTED) ||
6489 share == STGM_SHARE_EXCLUSIVE ||
6490 share == STGM_SHARE_DENY_WRITE) )
6496 /****************************************************************************
6497 * GetShareModeFromSTGM
6499 * This method will return a share mode flag from a STGM value.
6500 * The STGM value is assumed valid.
6502 static DWORD GetShareModeFromSTGM(DWORD stgm)
6504 switch (STGM_SHARE_MODE(stgm))
6506 case STGM_SHARE_DENY_NONE:
6507 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6508 case STGM_SHARE_DENY_READ:
6509 return FILE_SHARE_WRITE;
6510 case STGM_SHARE_DENY_WRITE:
6511 return FILE_SHARE_READ;
6512 case STGM_SHARE_EXCLUSIVE:
6515 ERR("Invalid share mode!\n");
6520 /****************************************************************************
6521 * GetAccessModeFromSTGM
6523 * This method will return an access mode flag from a STGM value.
6524 * The STGM value is assumed valid.
6526 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6528 switch (STGM_ACCESS_MODE(stgm))
6531 return GENERIC_READ;
6533 case STGM_READWRITE:
6534 return GENERIC_READ | GENERIC_WRITE;
6536 ERR("Invalid access mode!\n");
6541 /****************************************************************************
6542 * GetCreationModeFromSTGM
6544 * This method will return a creation mode flag from a STGM value.
6545 * The STGM value is assumed valid.
6547 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6549 switch(STGM_CREATE_MODE(stgm))
6552 return CREATE_ALWAYS;
6554 FIXME("STGM_CONVERT not implemented!\n");
6556 case STGM_FAILIFTHERE:
6559 ERR("Invalid create mode!\n");
6565 /*************************************************************************
6566 * OLECONVERT_LoadOLE10 [Internal]
6568 * Loads the OLE10 STREAM to memory
6571 * pOleStream [I] The OLESTREAM
6572 * pData [I] Data Structure for the OLESTREAM Data
6576 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6577 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6580 * This function is used by OleConvertOLESTREAMToIStorage only.
6582 * Memory allocated for pData must be freed by the caller
6584 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6587 HRESULT hRes = S_OK;
6591 pData->pData = NULL;
6592 pData->pstrOleObjFileName = (CHAR *) NULL;
6594 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6597 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6598 if(dwSize != sizeof(pData->dwOleID))
6600 hRes = CONVERT10_E_OLESTREAM_GET;
6602 else if(pData->dwOleID != OLESTREAM_ID)
6604 hRes = CONVERT10_E_OLESTREAM_FMT;
6615 /* Get the TypeID...more info needed for this field */
6616 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6617 if(dwSize != sizeof(pData->dwTypeID))
6619 hRes = CONVERT10_E_OLESTREAM_GET;
6624 if(pData->dwTypeID != 0)
6626 /* Get the length of the OleTypeName */
6627 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6628 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6630 hRes = CONVERT10_E_OLESTREAM_GET;
6635 if(pData->dwOleTypeNameLength > 0)
6637 /* Get the OleTypeName */
6638 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6639 if(dwSize != pData->dwOleTypeNameLength)
6641 hRes = CONVERT10_E_OLESTREAM_GET;
6647 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6648 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6650 hRes = CONVERT10_E_OLESTREAM_GET;
6654 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6655 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6656 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6657 if(pData->pstrOleObjFileName)
6659 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6660 if(dwSize != pData->dwOleObjFileNameLength)
6662 hRes = CONVERT10_E_OLESTREAM_GET;
6666 hRes = CONVERT10_E_OLESTREAM_GET;
6671 /* Get the Width of the Metafile */
6672 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6673 if(dwSize != sizeof(pData->dwMetaFileWidth))
6675 hRes = CONVERT10_E_OLESTREAM_GET;
6679 /* Get the Height of the Metafile */
6680 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6681 if(dwSize != sizeof(pData->dwMetaFileHeight))
6683 hRes = CONVERT10_E_OLESTREAM_GET;
6689 /* Get the Length of the Data */
6690 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6691 if(dwSize != sizeof(pData->dwDataLength))
6693 hRes = CONVERT10_E_OLESTREAM_GET;
6697 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6699 if(!bStrem1) /* if it is a second OLE stream data */
6701 pData->dwDataLength -= 8;
6702 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6703 if(dwSize != sizeof(pData->strUnknown))
6705 hRes = CONVERT10_E_OLESTREAM_GET;
6711 if(pData->dwDataLength > 0)
6713 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6715 /* Get Data (ex. IStorage, Metafile, or BMP) */
6718 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6719 if(dwSize != pData->dwDataLength)
6721 hRes = CONVERT10_E_OLESTREAM_GET;
6726 hRes = CONVERT10_E_OLESTREAM_GET;
6735 /*************************************************************************
6736 * OLECONVERT_SaveOLE10 [Internal]
6738 * Saves the OLE10 STREAM From memory
6741 * pData [I] Data Structure for the OLESTREAM Data
6742 * pOleStream [I] The OLESTREAM to save
6746 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6749 * This function is used by OleConvertIStorageToOLESTREAM only.
6752 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6755 HRESULT hRes = S_OK;
6759 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6760 if(dwSize != sizeof(pData->dwOleID))
6762 hRes = CONVERT10_E_OLESTREAM_PUT;
6767 /* Set the TypeID */
6768 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6769 if(dwSize != sizeof(pData->dwTypeID))
6771 hRes = CONVERT10_E_OLESTREAM_PUT;
6775 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6777 /* Set the Length of the OleTypeName */
6778 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6779 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6781 hRes = CONVERT10_E_OLESTREAM_PUT;
6786 if(pData->dwOleTypeNameLength > 0)
6788 /* Set the OleTypeName */
6789 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6790 if(dwSize != pData->dwOleTypeNameLength)
6792 hRes = CONVERT10_E_OLESTREAM_PUT;
6799 /* Set the width of the Metafile */
6800 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6801 if(dwSize != sizeof(pData->dwMetaFileWidth))
6803 hRes = CONVERT10_E_OLESTREAM_PUT;
6809 /* Set the height of the Metafile */
6810 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6811 if(dwSize != sizeof(pData->dwMetaFileHeight))
6813 hRes = CONVERT10_E_OLESTREAM_PUT;
6819 /* Set the length of the Data */
6820 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6821 if(dwSize != sizeof(pData->dwDataLength))
6823 hRes = CONVERT10_E_OLESTREAM_PUT;
6829 if(pData->dwDataLength > 0)
6831 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6832 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6833 if(dwSize != pData->dwDataLength)
6835 hRes = CONVERT10_E_OLESTREAM_PUT;
6843 /*************************************************************************
6844 * OLECONVERT_GetOLE20FromOLE10[Internal]
6846 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6847 * opens it, and copies the content to the dest IStorage for
6848 * OleConvertOLESTREAMToIStorage
6852 * pDestStorage [I] The IStorage to copy the data to
6853 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6854 * nBufferLength [I] The size of the buffer
6863 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
6867 IStorage *pTempStorage;
6868 DWORD dwNumOfBytesWritten;
6869 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6870 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6872 /* Create a temp File */
6873 GetTempPathW(MAX_PATH, wstrTempDir);
6874 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6875 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6877 if(hFile != INVALID_HANDLE_VALUE)
6879 /* Write IStorage Data to File */
6880 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6883 /* Open and copy temp storage to the Dest Storage */
6884 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6887 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6888 StorageBaseImpl_Release(pTempStorage);
6890 DeleteFileW(wstrTempFile);
6895 /*************************************************************************
6896 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6898 * Saves the OLE10 STREAM From memory
6901 * pStorage [I] The Src IStorage to copy
6902 * pData [I] The Dest Memory to write to.
6905 * The size in bytes allocated for pData
6908 * Memory allocated for pData must be freed by the caller
6910 * Used by OleConvertIStorageToOLESTREAM only.
6913 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6917 DWORD nDataLength = 0;
6918 IStorage *pTempStorage;
6919 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6920 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6924 /* Create temp Storage */
6925 GetTempPathW(MAX_PATH, wstrTempDir);
6926 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6927 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6931 /* Copy Src Storage to the Temp Storage */
6932 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6933 StorageBaseImpl_Release(pTempStorage);
6935 /* Open Temp Storage as a file and copy to memory */
6936 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6937 if(hFile != INVALID_HANDLE_VALUE)
6939 nDataLength = GetFileSize(hFile, NULL);
6940 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6941 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6944 DeleteFileW(wstrTempFile);
6949 /*************************************************************************
6950 * OLECONVERT_CreateOleStream [Internal]
6952 * Creates the "\001OLE" stream in the IStorage if necessary.
6955 * pStorage [I] Dest storage to create the stream in
6961 * This function is used by OleConvertOLESTREAMToIStorage only.
6963 * This stream is still unknown, MS Word seems to have extra data
6964 * but since the data is stored in the OLESTREAM there should be
6965 * no need to recreate the stream. If the stream is manually
6966 * deleted it will create it with this default data.
6969 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6973 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6974 BYTE pOleStreamHeader [] =
6976 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6977 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6978 0x00, 0x00, 0x00, 0x00
6981 /* Create stream if not present */
6982 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6983 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6987 /* Write default Data */
6988 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6989 IStream_Release(pStream);
6993 /* write a string to a stream, preceded by its length */
6994 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
7001 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
7002 r = IStream_Write( stm, &len, sizeof(len), NULL);
7007 str = CoTaskMemAlloc( len );
7008 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
7009 r = IStream_Write( stm, str, len, NULL);
7010 CoTaskMemFree( str );
7014 /* read a string preceded by its length from a stream */
7015 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
7018 DWORD len, count = 0;
7022 r = IStream_Read( stm, &len, sizeof(len), &count );
7025 if( count != sizeof(len) )
7026 return E_OUTOFMEMORY;
7028 TRACE("%d bytes\n",len);
7030 str = CoTaskMemAlloc( len );
7032 return E_OUTOFMEMORY;
7034 r = IStream_Read( stm, str, len, &count );
7039 CoTaskMemFree( str );
7040 return E_OUTOFMEMORY;
7043 TRACE("Read string %s\n",debugstr_an(str,len));
7045 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7046 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7048 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7049 CoTaskMemFree( str );
7057 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7058 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7062 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7064 static const BYTE unknown1[12] =
7065 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7066 0xFF, 0xFF, 0xFF, 0xFF};
7067 static const BYTE unknown2[16] =
7068 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7069 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7071 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7072 debugstr_w(lpszUserType), debugstr_w(szClipName),
7073 debugstr_w(szProgIDName));
7075 /* Create a CompObj stream if it doesn't exist */
7076 r = IStorage_CreateStream(pstg, szwStreamName,
7077 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7081 /* Write CompObj Structure to stream */
7082 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7084 if( SUCCEEDED( r ) )
7085 r = WriteClassStm( pstm, clsid );
7087 if( SUCCEEDED( r ) )
7088 r = STREAM_WriteString( pstm, lpszUserType );
7089 if( SUCCEEDED( r ) )
7090 r = STREAM_WriteString( pstm, szClipName );
7091 if( SUCCEEDED( r ) )
7092 r = STREAM_WriteString( pstm, szProgIDName );
7093 if( SUCCEEDED( r ) )
7094 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7096 IStream_Release( pstm );
7101 /***********************************************************************
7102 * WriteFmtUserTypeStg (OLE32.@)
7104 HRESULT WINAPI WriteFmtUserTypeStg(
7105 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7108 WCHAR szwClipName[0x40];
7109 CLSID clsid = CLSID_NULL;
7110 LPWSTR wstrProgID = NULL;
7113 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7115 /* get the clipboard format name */
7116 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
7119 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7121 /* FIXME: There's room to save a CLSID and its ProgID, but
7122 the CLSID is not looked up in the registry and in all the
7123 tests I wrote it was CLSID_NULL. Where does it come from?
7126 /* get the real program ID. This may fail, but that's fine */
7127 ProgIDFromCLSID(&clsid, &wstrProgID);
7129 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7131 r = STORAGE_WriteCompObj( pstg, &clsid,
7132 lpszUserType, szwClipName, wstrProgID );
7134 CoTaskMemFree(wstrProgID);
7140 /******************************************************************************
7141 * ReadFmtUserTypeStg [OLE32.@]
7143 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7147 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7148 unsigned char unknown1[12];
7149 unsigned char unknown2[16];
7151 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7154 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7156 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7157 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7160 WARN("Failed to open stream r = %08x\n", r);
7164 /* read the various parts of the structure */
7165 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7166 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7168 r = ReadClassStm( stm, &clsid );
7172 r = STREAM_ReadString( stm, &szCLSIDName );
7176 r = STREAM_ReadString( stm, &szOleTypeName );
7180 r = STREAM_ReadString( stm, &szProgIDName );
7184 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7185 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7188 /* ok, success... now we just need to store what we found */
7190 *pcf = RegisterClipboardFormatW( szOleTypeName );
7191 CoTaskMemFree( szOleTypeName );
7193 if( lplpszUserType )
7194 *lplpszUserType = szCLSIDName;
7195 CoTaskMemFree( szProgIDName );
7198 IStream_Release( stm );
7204 /*************************************************************************
7205 * OLECONVERT_CreateCompObjStream [Internal]
7207 * Creates a "\001CompObj" is the destination IStorage if necessary.
7210 * pStorage [I] The dest IStorage to create the CompObj Stream
7212 * strOleTypeName [I] The ProgID
7216 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7219 * This function is used by OleConvertOLESTREAMToIStorage only.
7221 * The stream data is stored in the OLESTREAM and there should be
7222 * no need to recreate the stream. If the stream is manually
7223 * deleted it will attempt to create it by querying the registry.
7227 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7230 HRESULT hStorageRes, hRes = S_OK;
7231 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7232 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7233 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7235 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7236 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7238 /* Initialize the CompObj structure */
7239 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7240 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
7241 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
7244 /* Create a CompObj stream if it doesn't exist */
7245 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7246 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7247 if(hStorageRes == S_OK)
7249 /* copy the OleTypeName to the compobj struct */
7250 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7251 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7253 /* copy the OleTypeName to the compobj struct */
7254 /* Note: in the test made, these were Identical */
7255 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7256 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7259 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7260 bufferW, OLESTREAM_MAX_STR_LEN );
7261 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7267 /* Get the CLSID Default Name from the Registry */
7268 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7269 if(hErr == ERROR_SUCCESS)
7271 char strTemp[OLESTREAM_MAX_STR_LEN];
7272 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7273 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7274 if(hErr == ERROR_SUCCESS)
7276 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7282 /* Write CompObj Structure to stream */
7283 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7285 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7287 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7288 if(IStorageCompObj.dwCLSIDNameLength > 0)
7290 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7292 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7293 if(IStorageCompObj.dwOleTypeNameLength > 0)
7295 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7297 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7298 if(IStorageCompObj.dwProgIDNameLength > 0)
7300 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7302 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7303 IStream_Release(pStream);
7309 /*************************************************************************
7310 * OLECONVERT_CreateOlePresStream[Internal]
7312 * Creates the "\002OlePres000" Stream with the Metafile data
7315 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7316 * dwExtentX [I] Width of the Metafile
7317 * dwExtentY [I] Height of the Metafile
7318 * pData [I] Metafile data
7319 * dwDataLength [I] Size of the Metafile data
7323 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7326 * This function is used by OleConvertOLESTREAMToIStorage only.
7329 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7333 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7334 BYTE pOlePresStreamHeader [] =
7336 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7337 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7338 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7339 0x00, 0x00, 0x00, 0x00
7342 BYTE pOlePresStreamHeaderEmpty [] =
7344 0x00, 0x00, 0x00, 0x00,
7345 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7346 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7347 0x00, 0x00, 0x00, 0x00
7350 /* Create the OlePres000 Stream */
7351 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7352 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7357 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7359 memset(&OlePres, 0, sizeof(OlePres));
7360 /* Do we have any metafile data to save */
7361 if(dwDataLength > 0)
7363 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7364 nHeaderSize = sizeof(pOlePresStreamHeader);
7368 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7369 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7371 /* Set width and height of the metafile */
7372 OlePres.dwExtentX = dwExtentX;
7373 OlePres.dwExtentY = -dwExtentY;
7375 /* Set Data and Length */
7376 if(dwDataLength > sizeof(METAFILEPICT16))
7378 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7379 OlePres.pData = &(pData[8]);
7381 /* Save OlePres000 Data to Stream */
7382 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7383 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7384 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7385 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7386 if(OlePres.dwSize > 0)
7388 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7390 IStream_Release(pStream);
7394 /*************************************************************************
7395 * OLECONVERT_CreateOle10NativeStream [Internal]
7397 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7400 * pStorage [I] Dest storage to create the stream in
7401 * pData [I] Ole10 Native Data (ex. bmp)
7402 * dwDataLength [I] Size of the Ole10 Native Data
7408 * This function is used by OleConvertOLESTREAMToIStorage only.
7410 * Might need to verify the data and return appropriate error message
7413 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
7417 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7419 /* Create the Ole10Native Stream */
7420 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7421 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7425 /* Write info to stream */
7426 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7427 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7428 IStream_Release(pStream);
7433 /*************************************************************************
7434 * OLECONVERT_GetOLE10ProgID [Internal]
7436 * Finds the ProgID (or OleTypeID) from the IStorage
7439 * pStorage [I] The Src IStorage to get the ProgID
7440 * strProgID [I] the ProgID string to get
7441 * dwSize [I] the size of the string
7445 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7448 * This function is used by OleConvertIStorageToOLESTREAM only.
7452 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7456 LARGE_INTEGER iSeekPos;
7457 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7458 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7460 /* Open the CompObj Stream */
7461 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7462 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7466 /*Get the OleType from the CompObj Stream */
7467 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7468 iSeekPos.u.HighPart = 0;
7470 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7471 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7472 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7473 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7474 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7475 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7476 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7478 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7481 IStream_Read(pStream, strProgID, *dwSize, NULL);
7483 IStream_Release(pStream);
7488 LPOLESTR wstrProgID;
7490 /* Get the OleType from the registry */
7491 REFCLSID clsid = &(stat.clsid);
7492 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7493 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7496 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7503 /*************************************************************************
7504 * OLECONVERT_GetOle10PresData [Internal]
7506 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7509 * pStorage [I] Src IStroage
7510 * pOleStream [I] Dest OleStream Mem Struct
7516 * This function is used by OleConvertIStorageToOLESTREAM only.
7518 * Memory allocated for pData must be freed by the caller
7522 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7527 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7529 /* Initialize Default data for OLESTREAM */
7530 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7531 pOleStreamData[0].dwTypeID = 2;
7532 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7533 pOleStreamData[1].dwTypeID = 0;
7534 pOleStreamData[0].dwMetaFileWidth = 0;
7535 pOleStreamData[0].dwMetaFileHeight = 0;
7536 pOleStreamData[0].pData = NULL;
7537 pOleStreamData[1].pData = NULL;
7539 /* Open Ole10Native Stream */
7540 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7541 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7545 /* Read Size and Data */
7546 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7547 if(pOleStreamData->dwDataLength > 0)
7549 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7550 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7552 IStream_Release(pStream);
7558 /*************************************************************************
7559 * OLECONVERT_GetOle20PresData[Internal]
7561 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7564 * pStorage [I] Src IStroage
7565 * pOleStreamData [I] Dest OleStream Mem Struct
7571 * This function is used by OleConvertIStorageToOLESTREAM only.
7573 * Memory allocated for pData must be freed by the caller
7575 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7579 OLECONVERT_ISTORAGE_OLEPRES olePress;
7580 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7582 /* Initialize Default data for OLESTREAM */
7583 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7584 pOleStreamData[0].dwTypeID = 2;
7585 pOleStreamData[0].dwMetaFileWidth = 0;
7586 pOleStreamData[0].dwMetaFileHeight = 0;
7587 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7588 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7589 pOleStreamData[1].dwTypeID = 0;
7590 pOleStreamData[1].dwOleTypeNameLength = 0;
7591 pOleStreamData[1].strOleTypeName[0] = 0;
7592 pOleStreamData[1].dwMetaFileWidth = 0;
7593 pOleStreamData[1].dwMetaFileHeight = 0;
7594 pOleStreamData[1].pData = NULL;
7595 pOleStreamData[1].dwDataLength = 0;
7598 /* Open OlePress000 stream */
7599 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7600 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7603 LARGE_INTEGER iSeekPos;
7604 METAFILEPICT16 MetaFilePict;
7605 static const char strMetafilePictName[] = "METAFILEPICT";
7607 /* Set the TypeID for a Metafile */
7608 pOleStreamData[1].dwTypeID = 5;
7610 /* Set the OleTypeName to Metafile */
7611 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7612 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7614 iSeekPos.u.HighPart = 0;
7615 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7617 /* Get Presentation Data */
7618 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7619 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7620 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7621 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7623 /*Set width and Height */
7624 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7625 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7626 if(olePress.dwSize > 0)
7629 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7631 /* Set MetaFilePict struct */
7632 MetaFilePict.mm = 8;
7633 MetaFilePict.xExt = olePress.dwExtentX;
7634 MetaFilePict.yExt = olePress.dwExtentY;
7635 MetaFilePict.hMF = 0;
7637 /* Get Metafile Data */
7638 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7639 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7640 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7642 IStream_Release(pStream);
7646 /*************************************************************************
7647 * OleConvertOLESTREAMToIStorage [OLE32.@]
7652 * DVTARGETDEVICE paramenter is not handled
7653 * Still unsure of some mem fields for OLE 10 Stream
7654 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7655 * and "\001OLE" streams
7658 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7659 LPOLESTREAM pOleStream,
7661 const DVTARGETDEVICE* ptd)
7665 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7667 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
7669 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7673 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7676 if(pstg == NULL || pOleStream == NULL)
7678 hRes = E_INVALIDARG;
7683 /* Load the OLESTREAM to Memory */
7684 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7689 /* Load the OLESTREAM to Memory (part 2)*/
7690 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7696 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7698 /* Do we have the IStorage Data in the OLESTREAM */
7699 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7701 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7702 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7706 /* It must be an original OLE 1.0 source */
7707 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7712 /* It must be an original OLE 1.0 source */
7713 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7716 /* Create CompObj Stream if necessary */
7717 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7720 /*Create the Ole Stream if necessary */
7721 OLECONVERT_CreateOleStream(pstg);
7726 /* Free allocated memory */
7727 for(i=0; i < 2; i++)
7729 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7730 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7731 pOleStreamData[i].pstrOleObjFileName = NULL;
7736 /*************************************************************************
7737 * OleConvertIStorageToOLESTREAM [OLE32.@]
7744 * Still unsure of some mem fields for OLE 10 Stream
7745 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7746 * and "\001OLE" streams.
7749 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7751 LPOLESTREAM pOleStream)
7754 HRESULT hRes = S_OK;
7756 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7757 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7759 TRACE("%p %p\n", pstg, pOleStream);
7761 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7763 if(pstg == NULL || pOleStream == NULL)
7765 hRes = E_INVALIDARG;
7769 /* Get the ProgID */
7770 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7771 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7775 /* Was it originally Ole10 */
7776 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7779 IStream_Release(pStream);
7780 /* Get Presentation Data for Ole10Native */
7781 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7785 /* Get Presentation Data (OLE20) */
7786 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7789 /* Save OLESTREAM */
7790 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7793 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7798 /* Free allocated memory */
7799 for(i=0; i < 2; i++)
7801 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7807 /***********************************************************************
7808 * GetConvertStg (OLE32.@)
7810 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7811 FIXME("unimplemented stub!\n");
7815 /******************************************************************************
7816 * StgIsStorageFile [OLE32.@]
7817 * Verify if the file contains a storage object
7823 * S_OK if file has magic bytes as a storage object
7824 * S_FALSE if file is not storage
7827 StgIsStorageFile(LPCOLESTR fn)
7833 TRACE("%s\n", debugstr_w(fn));
7834 hf = CreateFileW(fn, GENERIC_READ,
7835 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7836 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7838 if (hf == INVALID_HANDLE_VALUE)
7839 return STG_E_FILENOTFOUND;
7841 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7843 WARN(" unable to read file\n");
7850 if (bytes_read != 8) {
7851 WARN(" too short\n");
7855 if (!memcmp(magic,STORAGE_magic,8)) {
7860 WARN(" -> Invalid header.\n");
7864 /***********************************************************************
7865 * WriteClassStm (OLE32.@)
7867 * Writes a CLSID to a stream.
7870 * pStm [I] Stream to write to.
7871 * rclsid [I] CLSID to write.
7875 * Failure: HRESULT code.
7877 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
7879 TRACE("(%p,%p)\n",pStm,rclsid);
7881 if (!pStm || !rclsid)
7882 return E_INVALIDARG;
7884 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
7887 /***********************************************************************
7888 * ReadClassStm (OLE32.@)
7890 * Reads a CLSID from a stream.
7893 * pStm [I] Stream to read from.
7894 * rclsid [O] CLSID to read.
7898 * Failure: HRESULT code.
7900 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
7905 TRACE("(%p,%p)\n",pStm,pclsid);
7907 if (!pStm || !pclsid)
7908 return E_INVALIDARG;
7910 /* clear the output args */
7911 memcpy(pclsid, &CLSID_NULL, sizeof(*pclsid));
7913 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
7918 if (nbByte != sizeof(CLSID))
7919 return STG_E_READFAULT;