2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
47 #include "wine/unicode.h"
48 #include "wine/debug.h"
50 #include "storage32.h"
51 #include "ole2.h" /* For Write/ReadClassStm */
54 #include "wine/wingdi16.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(storage);
58 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
59 #define OLESTREAM_ID 0x501
60 #define OLESTREAM_MAX_STR_LEN 255
63 * These are signatures to detect the type of Document file.
65 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
66 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
68 static const char rootPropertyName[] = "Root Entry";
70 /****************************************************************************
71 * Storage32InternalImpl definitions.
73 * Definition of the implementation structure for the IStorage32 interface.
74 * This one implements the IStorage32 interface for storage that are
75 * inside another storage.
77 struct StorageInternalImpl
79 struct StorageBaseImpl base;
81 * There is no specific data for this class.
84 typedef struct StorageInternalImpl StorageInternalImpl;
86 /* Method definitions for the Storage32InternalImpl class. */
87 static StorageInternalImpl* StorageInternalImpl_Construct(StorageImpl* ancestorStorage,
88 DWORD openFlags, ULONG rootTropertyIndex);
89 static void StorageImpl_Destroy(StorageBaseImpl* iface);
90 static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
91 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const void* buffer);
92 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
93 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
94 static void StorageImpl_SaveFileHeader(StorageImpl* This);
96 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
97 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
98 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
99 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
100 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
102 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
103 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
104 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
106 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
107 static ULONG SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream* This);
108 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
109 ULONG blockIndex, ULONG offset, DWORD value);
110 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This,
111 ULONG blockIndex, ULONG offset, DWORD* value);
113 /* OLESTREAM memory structure to use for Get and Put Routines */
114 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
119 DWORD dwOleTypeNameLength;
120 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
121 CHAR *pstrOleObjFileName;
122 DWORD dwOleObjFileNameLength;
123 DWORD dwMetaFileWidth;
124 DWORD dwMetaFileHeight;
125 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
128 }OLECONVERT_OLESTREAM_DATA;
130 /* CompObj Stream structure */
131 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
136 DWORD dwCLSIDNameLength;
137 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
138 DWORD dwOleTypeNameLength;
139 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
140 DWORD dwProgIDNameLength;
141 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
143 }OLECONVERT_ISTORAGE_COMPOBJ;
146 /* Ole Presentation Stream structure */
147 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
155 }OLECONVERT_ISTORAGE_OLEPRES;
159 /***********************************************************************
160 * Forward declaration of internal functions used by the method DestroyElement
162 static HRESULT deleteStorageProperty(
163 StorageImpl *parentStorage,
164 ULONG foundPropertyIndexToDelete,
165 StgProperty propertyToDelete);
167 static HRESULT deleteStreamProperty(
168 StorageImpl *parentStorage,
169 ULONG foundPropertyIndexToDelete,
170 StgProperty propertyToDelete);
172 static HRESULT findPlaceholder(
173 StorageImpl *storage,
174 ULONG propertyIndexToStore,
175 ULONG storagePropertyIndex,
178 static HRESULT adjustPropertyChain(
180 StgProperty propertyToDelete,
181 StgProperty parentProperty,
182 ULONG parentPropertyId,
185 /***********************************************************************
186 * Declaration of the functions used to manipulate StgProperty
189 static ULONG getFreeProperty(
190 StorageImpl *storage);
192 static void updatePropertyChain(
193 StorageImpl *storage,
194 ULONG newPropertyIndex,
195 StgProperty newProperty);
197 static LONG propertyNameCmp(
198 const OLECHAR *newProperty,
199 const OLECHAR *currentProperty);
202 /***********************************************************************
203 * Declaration of miscellaneous functions...
205 static HRESULT validateSTGM(DWORD stgmValue);
207 static DWORD GetShareModeFromSTGM(DWORD stgm);
208 static DWORD GetAccessModeFromSTGM(DWORD stgm);
209 static DWORD GetCreationModeFromSTGM(DWORD stgm);
211 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
214 /****************************************************************************
215 * IEnumSTATSTGImpl definitions.
217 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
218 * This class allows iterating through the content of a storage and to find
219 * specific items inside it.
221 struct IEnumSTATSTGImpl
223 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
224 * since we want to cast this in an IEnumSTATSTG pointer */
226 LONG ref; /* Reference count */
227 StorageImpl* parentStorage; /* Reference to the parent storage */
228 ULONG firstPropertyNode; /* Index of the root of the storage to enumerate */
231 * The current implementation of the IEnumSTATSTGImpl class uses a stack
232 * to walk the property sets to get the content of a storage. This stack
233 * is implemented by the following 3 data members
239 #define ENUMSTATSGT_SIZE_INCREMENT 10
243 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageImpl* This, ULONG firstPropertyNode);
244 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
245 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl* This, ULONG nodeToPush);
246 static ULONG IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl* This, BOOL remove);
247 static ULONG IEnumSTATSTGImpl_FindProperty(IEnumSTATSTGImpl* This, const OLECHAR* lpszPropName,
248 StgProperty* buffer);
249 static INT IEnumSTATSTGImpl_FindParentProperty(IEnumSTATSTGImpl *This, ULONG childProperty,
250 StgProperty *currentProperty, ULONG *propertyId);
252 /************************************************************************
256 static ULONG BLOCK_GetBigBlockOffset(ULONG index)
258 if (index == 0xffffffff)
263 return index * BIG_BLOCK_SIZE;
266 /************************************************************************
267 ** Storage32BaseImpl implementation
269 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
270 ULARGE_INTEGER offset,
275 return BIGBLOCKFILE_ReadAt(This->bigBlockFile,offset,buffer,size,bytesRead);
278 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
279 ULARGE_INTEGER offset,
284 return BIGBLOCKFILE_WriteAt(This->bigBlockFile,offset,buffer,size,bytesWritten);
287 /************************************************************************
288 * Storage32BaseImpl_QueryInterface (IUnknown)
290 * This method implements the common QueryInterface for all IStorage32
291 * implementations contained in this file.
293 * See Windows documentation for more details on IUnknown methods.
295 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
300 StorageBaseImpl *This = (StorageBaseImpl *)iface;
302 if ( (This==0) || (ppvObject==0) )
307 if (IsEqualGUID(&IID_IUnknown, riid) ||
308 IsEqualGUID(&IID_IStorage, riid))
312 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
314 *ppvObject = &This->pssVtbl;
318 return E_NOINTERFACE;
320 IStorage_AddRef(iface);
325 /************************************************************************
326 * Storage32BaseImpl_AddRef (IUnknown)
328 * This method implements the common AddRef for all IStorage32
329 * implementations contained in this file.
331 * See Windows documentation for more details on IUnknown methods.
333 static ULONG WINAPI StorageBaseImpl_AddRef(
336 StorageBaseImpl *This = (StorageBaseImpl *)iface;
337 ULONG ref = InterlockedIncrement(&This->ref);
339 TRACE("(%p) AddRef to %d\n", This, ref);
344 /************************************************************************
345 * Storage32BaseImpl_Release (IUnknown)
347 * This method implements the common Release for all IStorage32
348 * implementations contained in this file.
350 * See Windows documentation for more details on IUnknown methods.
352 static ULONG WINAPI StorageBaseImpl_Release(
355 StorageBaseImpl *This = (StorageBaseImpl *)iface;
357 ULONG ref = InterlockedDecrement(&This->ref);
359 TRACE("(%p) ReleaseRef to %d\n", This, ref);
364 * Since we are using a system of base-classes, we want to call the
365 * destructor of the appropriate derived class. To do this, we are
366 * using virtual functions to implement the destructor.
368 This->v_destructor(This);
374 /************************************************************************
375 * Storage32BaseImpl_OpenStream (IStorage)
377 * This method will open the specified stream object from the current storage.
379 * See Windows documentation for more details on IStorage methods.
381 static HRESULT WINAPI StorageBaseImpl_OpenStream(
383 const OLECHAR* pwcsName, /* [string][in] */
384 void* reserved1, /* [unique][in] */
385 DWORD grfMode, /* [in] */
386 DWORD reserved2, /* [in] */
387 IStream** ppstm) /* [out] */
389 StorageBaseImpl *This = (StorageBaseImpl *)iface;
390 IEnumSTATSTGImpl* propertyEnumeration;
391 StgStreamImpl* newStream;
392 StgProperty currentProperty;
393 ULONG foundPropertyIndex;
394 HRESULT res = STG_E_UNKNOWN;
396 TRACE("(%p, %s, %p, %x, %d, %p)\n",
397 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
399 if ( (pwcsName==NULL) || (ppstm==0) )
407 if ( FAILED( validateSTGM(grfMode) ) ||
408 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
410 res = STG_E_INVALIDFLAG;
417 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
419 res = STG_E_INVALIDFUNCTION;
424 * Check that we're compatible with the parent's storage mode, but
425 * only if we are not in transacted mode
427 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
428 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
430 res = STG_E_ACCESSDENIED;
436 * Create a property enumeration to search the properties
438 propertyEnumeration = IEnumSTATSTGImpl_Construct(
439 This->ancestorStorage,
440 This->rootPropertySetIndex);
443 * Search the enumeration for the property with the given name
445 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
451 * Delete the property enumeration since we don't need it anymore
453 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
456 * If it was found, construct the stream object and return a pointer to it.
458 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
459 (currentProperty.propertyType==PROPTYPE_STREAM) )
461 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
465 newStream->grfMode = grfMode;
466 *ppstm = (IStream*)newStream;
468 IStream_AddRef(*ppstm);
478 res = STG_E_FILENOTFOUND;
482 TRACE("<-- IStream %p\n", *ppstm);
483 TRACE("<-- %08x\n", res);
487 /************************************************************************
488 * Storage32BaseImpl_OpenStorage (IStorage)
490 * This method will open a new storage object from the current storage.
492 * See Windows documentation for more details on IStorage methods.
494 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
496 const OLECHAR* pwcsName, /* [string][unique][in] */
497 IStorage* pstgPriority, /* [unique][in] */
498 DWORD grfMode, /* [in] */
499 SNB snbExclude, /* [unique][in] */
500 DWORD reserved, /* [in] */
501 IStorage** ppstg) /* [out] */
503 StorageBaseImpl *This = (StorageBaseImpl *)iface;
504 StorageInternalImpl* newStorage;
505 IEnumSTATSTGImpl* propertyEnumeration;
506 StgProperty currentProperty;
507 ULONG foundPropertyIndex;
508 HRESULT res = STG_E_UNKNOWN;
510 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
511 iface, debugstr_w(pwcsName), pstgPriority,
512 grfMode, snbExclude, reserved, ppstg);
514 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
521 if (snbExclude != NULL)
523 res = STG_E_INVALIDPARAMETER;
527 if ( FAILED( validateSTGM(grfMode) ))
529 res = STG_E_INVALIDFLAG;
536 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
537 (grfMode & STGM_DELETEONRELEASE) ||
538 (grfMode & STGM_PRIORITY) )
540 res = STG_E_INVALIDFUNCTION;
545 * Check that we're compatible with the parent's storage mode,
546 * but only if we are not transacted
548 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
549 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
551 res = STG_E_ACCESSDENIED;
558 propertyEnumeration = IEnumSTATSTGImpl_Construct(
559 This->ancestorStorage,
560 This->rootPropertySetIndex);
562 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
567 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
569 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
570 (currentProperty.propertyType==PROPTYPE_STORAGE) )
572 newStorage = StorageInternalImpl_Construct(
573 This->ancestorStorage,
579 *ppstg = (IStorage*)newStorage;
581 StorageBaseImpl_AddRef(*ppstg);
587 res = STG_E_INSUFFICIENTMEMORY;
591 res = STG_E_FILENOTFOUND;
594 TRACE("<-- %08x\n", res);
598 /************************************************************************
599 * Storage32BaseImpl_EnumElements (IStorage)
601 * This method will create an enumerator object that can be used to
602 * retrieve information about all the properties in the storage object.
604 * See Windows documentation for more details on IStorage methods.
606 static HRESULT WINAPI StorageBaseImpl_EnumElements(
608 DWORD reserved1, /* [in] */
609 void* reserved2, /* [size_is][unique][in] */
610 DWORD reserved3, /* [in] */
611 IEnumSTATSTG** ppenum) /* [out] */
613 StorageBaseImpl *This = (StorageBaseImpl *)iface;
614 IEnumSTATSTGImpl* newEnum;
616 TRACE("(%p, %d, %p, %d, %p)\n",
617 iface, reserved1, reserved2, reserved3, ppenum);
619 if ( (This==0) || (ppenum==0))
622 newEnum = IEnumSTATSTGImpl_Construct(
623 This->ancestorStorage,
624 This->rootPropertySetIndex);
628 *ppenum = (IEnumSTATSTG*)newEnum;
630 IEnumSTATSTG_AddRef(*ppenum);
635 return E_OUTOFMEMORY;
638 /************************************************************************
639 * Storage32BaseImpl_Stat (IStorage)
641 * This method will retrieve information about this storage object.
643 * See Windows documentation for more details on IStorage methods.
645 static HRESULT WINAPI StorageBaseImpl_Stat(
647 STATSTG* pstatstg, /* [out] */
648 DWORD grfStatFlag) /* [in] */
650 StorageBaseImpl *This = (StorageBaseImpl *)iface;
651 StgProperty curProperty;
653 HRESULT res = STG_E_UNKNOWN;
655 TRACE("(%p, %p, %x)\n",
656 iface, pstatstg, grfStatFlag);
658 if ( (This==0) || (pstatstg==0))
664 readSuccessful = StorageImpl_ReadProperty(
665 This->ancestorStorage,
666 This->rootPropertySetIndex,
671 StorageUtl_CopyPropertyToSTATSTG(
676 pstatstg->grfMode = This->openFlags;
677 pstatstg->grfStateBits = This->stateBits;
688 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);
690 TRACE("<-- %08x\n", res);
694 /************************************************************************
695 * Storage32BaseImpl_RenameElement (IStorage)
697 * This method will rename the specified element.
699 * See Windows documentation for more details on IStorage methods.
701 * Implementation notes: The method used to rename consists of creating a clone
702 * of the deleted StgProperty object setting it with the new name and to
703 * perform a DestroyElement of the old StgProperty.
705 static HRESULT WINAPI StorageBaseImpl_RenameElement(
707 const OLECHAR* pwcsOldName, /* [in] */
708 const OLECHAR* pwcsNewName) /* [in] */
710 StorageBaseImpl *This = (StorageBaseImpl *)iface;
711 IEnumSTATSTGImpl* propertyEnumeration;
712 StgProperty currentProperty;
713 ULONG foundPropertyIndex;
715 TRACE("(%p, %s, %s)\n",
716 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
718 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
719 This->rootPropertySetIndex);
721 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
725 if (foundPropertyIndex != PROPERTY_NULL)
728 * There is already a property with the new name
730 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
731 return STG_E_FILEALREADYEXISTS;
734 IEnumSTATSTG_Reset((IEnumSTATSTG*)propertyEnumeration);
737 * Search the enumeration for the old property name
739 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
743 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
745 if (foundPropertyIndex != PROPERTY_NULL)
747 StgProperty renamedProperty;
748 ULONG renamedPropertyIndex;
751 * Setup a new property for the renamed property
753 renamedProperty.sizeOfNameString =
754 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
756 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
757 return STG_E_INVALIDNAME;
759 strcpyW(renamedProperty.name, pwcsNewName);
761 renamedProperty.propertyType = currentProperty.propertyType;
762 renamedProperty.startingBlock = currentProperty.startingBlock;
763 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
764 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
766 renamedProperty.previousProperty = PROPERTY_NULL;
767 renamedProperty.nextProperty = PROPERTY_NULL;
770 * Bring the dirProperty link in case it is a storage and in which
771 * case the renamed storage elements don't require to be reorganized.
773 renamedProperty.dirProperty = currentProperty.dirProperty;
775 /* call CoFileTime to get the current time
776 renamedProperty.timeStampS1
777 renamedProperty.timeStampD1
778 renamedProperty.timeStampS2
779 renamedProperty.timeStampD2
780 renamedProperty.propertyUniqueID
784 * Obtain a free property in the property chain
786 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
789 * Save the new property into the new property spot
791 StorageImpl_WriteProperty(
792 This->ancestorStorage,
793 renamedPropertyIndex,
797 * Find a spot in the property chain for our newly created property.
801 renamedPropertyIndex,
805 * At this point the renamed property has been inserted in the tree,
806 * now, before Destroying the old property we must zero its dirProperty
807 * otherwise the DestroyProperty below will zap it all and we do not want
809 * Also, we fake that the old property is a storage so the DestroyProperty
810 * will not do a SetSize(0) on the stream data.
812 * This means that we need to tweak the StgProperty if it is a stream or a
815 StorageImpl_ReadProperty(This->ancestorStorage,
819 currentProperty.dirProperty = PROPERTY_NULL;
820 currentProperty.propertyType = PROPTYPE_STORAGE;
821 StorageImpl_WriteProperty(
822 This->ancestorStorage,
827 * Invoke Destroy to get rid of the ole property and automatically redo
828 * the linking of its previous and next members...
830 IStorage_DestroyElement(iface, pwcsOldName);
836 * There is no property with the old name
838 return STG_E_FILENOTFOUND;
844 /************************************************************************
845 * Storage32BaseImpl_CreateStream (IStorage)
847 * This method will create a stream object within this storage
849 * See Windows documentation for more details on IStorage methods.
851 static HRESULT WINAPI StorageBaseImpl_CreateStream(
853 const OLECHAR* pwcsName, /* [string][in] */
854 DWORD grfMode, /* [in] */
855 DWORD reserved1, /* [in] */
856 DWORD reserved2, /* [in] */
857 IStream** ppstm) /* [out] */
859 StorageBaseImpl *This = (StorageBaseImpl *)iface;
860 IEnumSTATSTGImpl* propertyEnumeration;
861 StgStreamImpl* newStream;
862 StgProperty currentProperty, newStreamProperty;
863 ULONG foundPropertyIndex, newPropertyIndex;
865 TRACE("(%p, %s, %x, %d, %d, %p)\n",
866 iface, debugstr_w(pwcsName), grfMode,
867 reserved1, reserved2, ppstm);
870 return STG_E_INVALIDPOINTER;
873 return STG_E_INVALIDNAME;
875 if (reserved1 || reserved2)
876 return STG_E_INVALIDPARAMETER;
878 if ( FAILED( validateSTGM(grfMode) ))
879 return STG_E_INVALIDFLAG;
881 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
882 return STG_E_INVALIDFLAG;
887 if ((grfMode & STGM_DELETEONRELEASE) ||
888 (grfMode & STGM_TRANSACTED))
889 return STG_E_INVALIDFUNCTION;
891 /* Can't create a stream on read-only storage */
892 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
893 return STG_E_ACCESSDENIED;
896 * Check that we're compatible with the parent's storage mode
897 * if not in transacted mode
899 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
900 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
901 return STG_E_ACCESSDENIED;
904 if(This->ancestorStorage->base.openFlags & STGM_SIMPLE)
905 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
909 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
910 This->rootPropertySetIndex);
912 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
916 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
918 if (foundPropertyIndex != PROPERTY_NULL)
921 * An element with this name already exists
923 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
927 LIST_FOR_EACH_ENTRY(strm, &This->strmHead, StgStreamImpl, StrmListEntry)
929 if (strm->ownerProperty == foundPropertyIndex)
931 TRACE("Stream deleted %p\n", strm);
932 strm->parentStorage = NULL;
933 list_remove(&strm->StrmListEntry);
936 IStorage_DestroyElement(iface, pwcsName);
939 return STG_E_FILEALREADYEXISTS;
941 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
943 WARN("read-only storage\n");
944 return STG_E_ACCESSDENIED;
948 * memset the empty property
950 memset(&newStreamProperty, 0, sizeof(StgProperty));
952 newStreamProperty.sizeOfNameString =
953 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
955 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
956 return STG_E_INVALIDNAME;
958 strcpyW(newStreamProperty.name, pwcsName);
960 newStreamProperty.propertyType = PROPTYPE_STREAM;
961 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
962 newStreamProperty.size.u.LowPart = 0;
963 newStreamProperty.size.u.HighPart = 0;
965 newStreamProperty.previousProperty = PROPERTY_NULL;
966 newStreamProperty.nextProperty = PROPERTY_NULL;
967 newStreamProperty.dirProperty = PROPERTY_NULL;
969 /* call CoFileTime to get the current time
970 newStreamProperty.timeStampS1
971 newStreamProperty.timeStampD1
972 newStreamProperty.timeStampS2
973 newStreamProperty.timeStampD2
976 /* newStreamProperty.propertyUniqueID */
979 * Get a free property or create a new one
981 newPropertyIndex = getFreeProperty(This->ancestorStorage);
984 * Save the new property into the new property spot
986 StorageImpl_WriteProperty(
987 This->ancestorStorage,
992 * Find a spot in the property chain for our newly created property.
1000 * Open the stream to return it.
1002 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
1006 *ppstm = (IStream*)newStream;
1008 IStream_AddRef(*ppstm);
1012 return STG_E_INSUFFICIENTMEMORY;
1018 /************************************************************************
1019 * Storage32BaseImpl_SetClass (IStorage)
1021 * This method will write the specified CLSID in the property of this
1024 * See Windows documentation for more details on IStorage methods.
1026 static HRESULT WINAPI StorageBaseImpl_SetClass(
1028 REFCLSID clsid) /* [in] */
1030 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1031 HRESULT hRes = E_FAIL;
1032 StgProperty curProperty;
1035 TRACE("(%p, %p)\n", iface, clsid);
1037 success = StorageImpl_ReadProperty(This->ancestorStorage,
1038 This->rootPropertySetIndex,
1042 curProperty.propertyUniqueID = *clsid;
1044 success = StorageImpl_WriteProperty(This->ancestorStorage,
1045 This->rootPropertySetIndex,
1054 /************************************************************************
1055 ** Storage32Impl implementation
1058 /************************************************************************
1059 * Storage32Impl_CreateStorage (IStorage)
1061 * This method will create the storage object within the provided storage.
1063 * See Windows documentation for more details on IStorage methods.
1065 static HRESULT WINAPI StorageImpl_CreateStorage(
1067 const OLECHAR *pwcsName, /* [string][in] */
1068 DWORD grfMode, /* [in] */
1069 DWORD reserved1, /* [in] */
1070 DWORD reserved2, /* [in] */
1071 IStorage **ppstg) /* [out] */
1073 StorageImpl* const This=(StorageImpl*)iface;
1075 IEnumSTATSTGImpl *propertyEnumeration;
1076 StgProperty currentProperty;
1077 StgProperty newProperty;
1078 ULONG foundPropertyIndex;
1079 ULONG newPropertyIndex;
1082 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1083 iface, debugstr_w(pwcsName), grfMode,
1084 reserved1, reserved2, ppstg);
1087 return STG_E_INVALIDPOINTER;
1090 return STG_E_INVALIDNAME;
1094 if ( FAILED( validateSTGM(grfMode) ) ||
1095 (grfMode & STGM_DELETEONRELEASE) )
1097 WARN("bad grfMode: 0x%x\n", grfMode);
1098 return STG_E_INVALIDFLAG;
1102 * Check that we're compatible with the parent's storage mode
1104 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->base.openFlags ) )
1106 WARN("access denied\n");
1107 return STG_E_ACCESSDENIED;
1111 * Create a property enumeration and search the properties
1113 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1114 This->base.rootPropertySetIndex);
1116 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1119 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1121 if (foundPropertyIndex != PROPERTY_NULL)
1124 * An element with this name already exists
1126 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1127 STGM_ACCESS_MODE(This->base.openFlags) != STGM_READ)
1129 hr = IStorage_DestroyElement(iface, pwcsName);
1135 WARN("file already exists\n");
1136 return STG_E_FILEALREADYEXISTS;
1139 else if (STGM_ACCESS_MODE(This->base.openFlags) == STGM_READ)
1141 WARN("read-only storage\n");
1142 return STG_E_ACCESSDENIED;
1146 * memset the empty property
1148 memset(&newProperty, 0, sizeof(StgProperty));
1150 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1152 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1154 FIXME("name too long\n");
1155 return STG_E_INVALIDNAME;
1158 strcpyW(newProperty.name, pwcsName);
1160 newProperty.propertyType = PROPTYPE_STORAGE;
1161 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1162 newProperty.size.u.LowPart = 0;
1163 newProperty.size.u.HighPart = 0;
1165 newProperty.previousProperty = PROPERTY_NULL;
1166 newProperty.nextProperty = PROPERTY_NULL;
1167 newProperty.dirProperty = PROPERTY_NULL;
1169 /* call CoFileTime to get the current time
1170 newProperty.timeStampS1
1171 newProperty.timeStampD1
1172 newProperty.timeStampS2
1173 newProperty.timeStampD2
1176 /* newStorageProperty.propertyUniqueID */
1179 * Obtain a free property in the property chain
1181 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1184 * Save the new property into the new property spot
1186 StorageImpl_WriteProperty(
1187 This->base.ancestorStorage,
1192 * Find a spot in the property chain for our newly created property.
1194 updatePropertyChain(
1200 * Open it to get a pointer to return.
1202 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1204 if( (hr != S_OK) || (*ppstg == NULL))
1214 /***************************************************************************
1218 * Get a free property or create a new one.
1220 static ULONG getFreeProperty(
1221 StorageImpl *storage)
1223 ULONG currentPropertyIndex = 0;
1224 ULONG newPropertyIndex = PROPERTY_NULL;
1225 BOOL readSuccessful = TRUE;
1226 StgProperty currentProperty;
1231 * Start by reading the root property
1233 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1234 currentPropertyIndex,
1238 if (currentProperty.sizeOfNameString == 0)
1241 * The property existis and is available, we found it.
1243 newPropertyIndex = currentPropertyIndex;
1249 * We exhausted the property list, we will create more space below
1251 newPropertyIndex = currentPropertyIndex;
1253 currentPropertyIndex++;
1255 } while (newPropertyIndex == PROPERTY_NULL);
1258 * grow the property chain
1260 if (! readSuccessful)
1262 StgProperty emptyProperty;
1263 ULARGE_INTEGER newSize;
1264 ULONG propertyIndex;
1265 ULONG lastProperty = 0;
1266 ULONG blockCount = 0;
1269 * obtain the new count of property blocks
1271 blockCount = BlockChainStream_GetCount(
1272 storage->base.ancestorStorage->rootBlockChain)+1;
1275 * initialize the size used by the property stream
1277 newSize.u.HighPart = 0;
1278 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1281 * add a property block to the property chain
1283 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1286 * memset the empty property in order to initialize the unused newly
1289 memset(&emptyProperty, 0, sizeof(StgProperty));
1294 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1297 propertyIndex = newPropertyIndex;
1298 propertyIndex < lastProperty;
1301 StorageImpl_WriteProperty(
1302 storage->base.ancestorStorage,
1308 return newPropertyIndex;
1311 /****************************************************************************
1315 * Case insensitive comparison of StgProperty.name by first considering
1318 * Returns <0 when newProperty < currentProperty
1319 * >0 when newProperty > currentProperty
1320 * 0 when newProperty == currentProperty
1322 static LONG propertyNameCmp(
1323 const OLECHAR *newProperty,
1324 const OLECHAR *currentProperty)
1326 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1331 * We compare the string themselves only when they are of the same length
1333 diff = lstrcmpiW( newProperty, currentProperty);
1339 /****************************************************************************
1343 * Properly link this new element in the property chain.
1345 static void updatePropertyChain(
1346 StorageImpl *storage,
1347 ULONG newPropertyIndex,
1348 StgProperty newProperty)
1350 StgProperty currentProperty;
1353 * Read the root property
1355 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1356 storage->base.rootPropertySetIndex,
1359 if (currentProperty.dirProperty != PROPERTY_NULL)
1362 * The root storage contains some element, therefore, start the research
1363 * for the appropriate location.
1366 ULONG current, next, previous, currentPropertyId;
1369 * Keep the StgProperty sequence number of the storage first property
1371 currentPropertyId = currentProperty.dirProperty;
1376 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1377 currentProperty.dirProperty,
1380 previous = currentProperty.previousProperty;
1381 next = currentProperty.nextProperty;
1382 current = currentPropertyId;
1386 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1390 if (previous != PROPERTY_NULL)
1392 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1399 currentProperty.previousProperty = newPropertyIndex;
1400 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1408 if (next != PROPERTY_NULL)
1410 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1417 currentProperty.nextProperty = newPropertyIndex;
1418 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1427 * Trying to insert an item with the same name in the
1428 * subtree structure.
1433 previous = currentProperty.previousProperty;
1434 next = currentProperty.nextProperty;
1440 * The root storage is empty, link the new property to its dir property
1442 currentProperty.dirProperty = newPropertyIndex;
1443 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1444 storage->base.rootPropertySetIndex,
1450 /*************************************************************************
1453 static HRESULT WINAPI StorageImpl_CopyTo(
1455 DWORD ciidExclude, /* [in] */
1456 const IID* rgiidExclude, /* [size_is][unique][in] */
1457 SNB snbExclude, /* [unique][in] */
1458 IStorage* pstgDest) /* [unique][in] */
1460 IEnumSTATSTG *elements = 0;
1461 STATSTG curElement, strStat;
1463 IStorage *pstgTmp, *pstgChild;
1464 IStream *pstrTmp, *pstrChild;
1467 if ((ciidExclude != 0) || (rgiidExclude != NULL))
1468 FIXME("Exclude option not implemented\n");
1470 TRACE("(%p, %d, %p, %p, %p)\n",
1471 iface, ciidExclude, rgiidExclude,
1472 snbExclude, pstgDest);
1474 if ( pstgDest == 0 )
1475 return STG_E_INVALIDPOINTER;
1478 * Enumerate the elements
1480 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1488 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1489 IStorage_SetClass( pstgDest, &curElement.clsid );
1494 * Obtain the next element
1496 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1498 if ( hr == S_FALSE )
1500 hr = S_OK; /* done, every element has been copied */
1506 WCHAR **snb = snbExclude;
1508 while ( *snb != NULL && !skip )
1510 if ( lstrcmpW(curElement.pwcsName, *snb) == 0 )
1519 if (curElement.type == STGTY_STORAGE)
1522 * open child source storage
1524 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1525 STGM_READ|STGM_SHARE_EXCLUSIVE,
1526 NULL, 0, &pstgChild );
1532 * Check if destination storage is not a child of the source
1533 * storage, which will cause an infinite loop
1535 if (pstgChild == pstgDest)
1537 IEnumSTATSTG_Release(elements);
1539 return STG_E_ACCESSDENIED;
1543 * create a new storage in destination storage
1545 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1546 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1550 * if it already exist, don't create a new one use this one
1552 if (hr == STG_E_FILEALREADYEXISTS)
1554 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1555 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1556 NULL, 0, &pstgTmp );
1564 * do the copy recursively
1566 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1569 IStorage_Release( pstgTmp );
1570 IStorage_Release( pstgChild );
1572 else if (curElement.type == STGTY_STREAM)
1575 * create a new stream in destination storage. If the stream already
1576 * exist, it will be deleted and a new one will be created.
1578 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1579 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1586 * open child stream storage
1588 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1589 STGM_READ|STGM_SHARE_EXCLUSIVE,
1596 * Get the size of the source stream
1598 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1601 * Set the size of the destination stream.
1603 IStream_SetSize(pstrTmp, strStat.cbSize);
1608 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1611 IStream_Release( pstrTmp );
1612 IStream_Release( pstrChild );
1616 WARN("unknown element type: %d\n", curElement.type);
1619 } while (hr == S_OK);
1624 IEnumSTATSTG_Release(elements);
1629 /*************************************************************************
1630 * MoveElementTo (IStorage)
1632 static HRESULT WINAPI StorageImpl_MoveElementTo(
1634 const OLECHAR *pwcsName, /* [string][in] */
1635 IStorage *pstgDest, /* [unique][in] */
1636 const OLECHAR *pwcsNewName,/* [string][in] */
1637 DWORD grfFlags) /* [in] */
1639 FIXME("(%p %s %p %s %u): stub\n", iface,
1640 debugstr_w(pwcsName), pstgDest,
1641 debugstr_w(pwcsNewName), grfFlags);
1645 /*************************************************************************
1648 * Ensures that any changes made to a storage object open in transacted mode
1649 * are reflected in the parent storage
1652 * Wine doesn't implement transacted mode, which seems to be a basic
1653 * optimization, so we can ignore this stub for now.
1655 static HRESULT WINAPI StorageImpl_Commit(
1657 DWORD grfCommitFlags)/* [in] */
1659 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1663 /*************************************************************************
1666 * Discard all changes that have been made since the last commit operation
1668 static HRESULT WINAPI StorageImpl_Revert(
1671 FIXME("(%p): stub\n", iface);
1675 /*************************************************************************
1676 * DestroyElement (IStorage)
1678 * Strategy: This implementation is built this way for simplicity not for speed.
1679 * I always delete the topmost element of the enumeration and adjust
1680 * the deleted element pointer all the time. This takes longer to
1681 * do but allow to reinvoke DestroyElement whenever we encounter a
1682 * storage object. The optimisation resides in the usage of another
1683 * enumeration strategy that would give all the leaves of a storage
1684 * first. (postfix order)
1686 static HRESULT WINAPI StorageImpl_DestroyElement(
1688 const OLECHAR *pwcsName)/* [string][in] */
1690 StorageImpl* const This=(StorageImpl*)iface;
1692 IEnumSTATSTGImpl* propertyEnumeration;
1695 StgProperty propertyToDelete;
1696 StgProperty parentProperty;
1697 ULONG foundPropertyIndexToDelete;
1698 ULONG typeOfRelation;
1699 ULONG parentPropertyId = 0;
1702 iface, debugstr_w(pwcsName));
1705 return STG_E_INVALIDPOINTER;
1707 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
1708 return STG_E_ACCESSDENIED;
1710 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1711 This->base.ancestorStorage,
1712 This->base.rootPropertySetIndex);
1714 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1715 propertyEnumeration,
1719 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1721 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1723 return STG_E_FILENOTFOUND;
1727 * Find the parent property of the property to delete (the one that
1728 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1729 * the parent is This. Otherwise, the parent is one of its sibling...
1733 * First, read This's StgProperty..
1735 res = StorageImpl_ReadProperty(
1736 This->base.ancestorStorage,
1737 This->base.rootPropertySetIndex,
1743 * Second, check to see if by any chance the actual storage (This) is not
1744 * the parent of the property to delete... We never know...
1746 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1749 * Set data as it would have been done in the else part...
1751 typeOfRelation = PROPERTY_RELATION_DIR;
1752 parentPropertyId = This->base.rootPropertySetIndex;
1757 * Create a property enumeration to search the parent properties, and
1758 * delete it once done.
1760 IEnumSTATSTGImpl* propertyEnumeration2;
1762 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1763 This->base.ancestorStorage,
1764 This->base.rootPropertySetIndex);
1766 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1767 propertyEnumeration2,
1768 foundPropertyIndexToDelete,
1772 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1775 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1777 hr = deleteStorageProperty(
1779 foundPropertyIndexToDelete,
1782 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1784 hr = deleteStreamProperty(
1786 foundPropertyIndexToDelete,
1794 * Adjust the property chain
1796 hr = adjustPropertyChain(
1807 /************************************************************************
1808 * StorageImpl_Stat (IStorage)
1810 * This method will retrieve information about this storage object.
1812 * See Windows documentation for more details on IStorage methods.
1814 static HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1815 STATSTG* pstatstg, /* [out] */
1816 DWORD grfStatFlag) /* [in] */
1818 StorageImpl* const This = (StorageImpl*)iface;
1819 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1821 if ( SUCCEEDED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1823 CoTaskMemFree(pstatstg->pwcsName);
1824 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1825 strcpyW(pstatstg->pwcsName, This->pwcsName);
1831 /******************************************************************************
1832 * Internal stream list handlers
1835 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1837 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1838 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1841 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1843 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1844 list_remove(&(strm->StrmListEntry));
1847 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1849 struct list *cur, *cur2;
1850 StgStreamImpl *strm=NULL;
1852 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1853 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1854 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1855 strm->parentStorage = NULL;
1861 /*********************************************************************
1865 * Perform the deletion of a complete storage node
1868 static HRESULT deleteStorageProperty(
1869 StorageImpl *parentStorage,
1870 ULONG indexOfPropertyToDelete,
1871 StgProperty propertyToDelete)
1873 IEnumSTATSTG *elements = 0;
1874 IStorage *childStorage = 0;
1875 STATSTG currentElement;
1877 HRESULT destroyHr = S_OK;
1880 * Open the storage and enumerate it
1882 hr = StorageBaseImpl_OpenStorage(
1883 (IStorage*)parentStorage,
1884 propertyToDelete.name,
1886 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1897 * Enumerate the elements
1899 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1904 * Obtain the next element
1906 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
1909 destroyHr = StorageImpl_DestroyElement(childStorage, currentElement.pwcsName);
1911 CoTaskMemFree(currentElement.pwcsName);
1915 * We need to Reset the enumeration every time because we delete elements
1916 * and the enumeration could be invalid
1918 IEnumSTATSTG_Reset(elements);
1920 } while ((hr == S_OK) && (destroyHr == S_OK));
1923 * Invalidate the property by zeroing its name member.
1925 propertyToDelete.sizeOfNameString = 0;
1927 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
1928 indexOfPropertyToDelete,
1931 IStorage_Release(childStorage);
1932 IEnumSTATSTG_Release(elements);
1937 /*********************************************************************
1941 * Perform the deletion of a stream node
1944 static HRESULT deleteStreamProperty(
1945 StorageImpl *parentStorage,
1946 ULONG indexOfPropertyToDelete,
1947 StgProperty propertyToDelete)
1951 ULARGE_INTEGER size;
1953 size.u.HighPart = 0;
1956 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
1957 propertyToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
1967 hr = IStream_SetSize(pis, size);
1975 * Release the stream object.
1977 IStream_Release(pis);
1980 * Invalidate the property by zeroing its name member.
1982 propertyToDelete.sizeOfNameString = 0;
1985 * Here we should re-read the property so we get the updated pointer
1986 * but since we are here to zap it, I don't do it...
1988 StorageImpl_WriteProperty(
1989 parentStorage->base.ancestorStorage,
1990 indexOfPropertyToDelete,
1996 /*********************************************************************
2000 * Finds a placeholder for the StgProperty within the Storage
2003 static HRESULT findPlaceholder(
2004 StorageImpl *storage,
2005 ULONG propertyIndexToStore,
2006 ULONG storePropertyIndex,
2009 StgProperty storeProperty;
2013 * Read the storage property
2015 res = StorageImpl_ReadProperty(
2016 storage->base.ancestorStorage,
2025 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2027 if (storeProperty.previousProperty != PROPERTY_NULL)
2029 return findPlaceholder(
2031 propertyIndexToStore,
2032 storeProperty.previousProperty,
2037 storeProperty.previousProperty = propertyIndexToStore;
2040 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2042 if (storeProperty.nextProperty != PROPERTY_NULL)
2044 return findPlaceholder(
2046 propertyIndexToStore,
2047 storeProperty.nextProperty,
2052 storeProperty.nextProperty = propertyIndexToStore;
2055 else if (typeOfRelation == PROPERTY_RELATION_DIR)
2057 if (storeProperty.dirProperty != PROPERTY_NULL)
2059 return findPlaceholder(
2061 propertyIndexToStore,
2062 storeProperty.dirProperty,
2067 storeProperty.dirProperty = propertyIndexToStore;
2071 res = StorageImpl_WriteProperty(
2072 storage->base.ancestorStorage,
2084 /*************************************************************************
2088 * This method takes the previous and the next property link of a property
2089 * to be deleted and find them a place in the Storage.
2091 static HRESULT adjustPropertyChain(
2093 StgProperty propertyToDelete,
2094 StgProperty parentProperty,
2095 ULONG parentPropertyId,
2098 ULONG newLinkProperty = PROPERTY_NULL;
2099 BOOL needToFindAPlaceholder = FALSE;
2100 ULONG storeNode = PROPERTY_NULL;
2101 ULONG toStoreNode = PROPERTY_NULL;
2102 INT relationType = 0;
2106 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2108 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2111 * Set the parent previous to the property to delete previous
2113 newLinkProperty = propertyToDelete.previousProperty;
2115 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2118 * We also need to find a storage for the other link, setup variables
2119 * to do this at the end...
2121 needToFindAPlaceholder = TRUE;
2122 storeNode = propertyToDelete.previousProperty;
2123 toStoreNode = propertyToDelete.nextProperty;
2124 relationType = PROPERTY_RELATION_NEXT;
2127 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2130 * Set the parent previous to the property to delete next
2132 newLinkProperty = propertyToDelete.nextProperty;
2136 * Link it for real...
2138 parentProperty.previousProperty = newLinkProperty;
2141 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2143 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2146 * Set the parent next to the property to delete next previous
2148 newLinkProperty = propertyToDelete.previousProperty;
2150 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2153 * We also need to find a storage for the other link, setup variables
2154 * to do this at the end...
2156 needToFindAPlaceholder = TRUE;
2157 storeNode = propertyToDelete.previousProperty;
2158 toStoreNode = propertyToDelete.nextProperty;
2159 relationType = PROPERTY_RELATION_NEXT;
2162 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2165 * Set the parent next to the property to delete next
2167 newLinkProperty = propertyToDelete.nextProperty;
2171 * Link it for real...
2173 parentProperty.nextProperty = newLinkProperty;
2175 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2177 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2180 * Set the parent dir to the property to delete previous
2182 newLinkProperty = propertyToDelete.previousProperty;
2184 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2187 * We also need to find a storage for the other link, setup variables
2188 * to do this at the end...
2190 needToFindAPlaceholder = TRUE;
2191 storeNode = propertyToDelete.previousProperty;
2192 toStoreNode = propertyToDelete.nextProperty;
2193 relationType = PROPERTY_RELATION_NEXT;
2196 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2199 * Set the parent dir to the property to delete next
2201 newLinkProperty = propertyToDelete.nextProperty;
2205 * Link it for real...
2207 parentProperty.dirProperty = newLinkProperty;
2211 * Write back the parent property
2213 res = StorageImpl_WriteProperty(
2214 This->base.ancestorStorage,
2223 * If a placeholder is required for the other link, then, find one and
2224 * get out of here...
2226 if (needToFindAPlaceholder)
2228 hr = findPlaceholder(
2239 /******************************************************************************
2240 * SetElementTimes (IStorage)
2242 static HRESULT WINAPI StorageImpl_SetElementTimes(
2244 const OLECHAR *pwcsName,/* [string][in] */
2245 const FILETIME *pctime, /* [in] */
2246 const FILETIME *patime, /* [in] */
2247 const FILETIME *pmtime) /* [in] */
2249 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2253 /******************************************************************************
2254 * SetStateBits (IStorage)
2256 static HRESULT WINAPI StorageImpl_SetStateBits(
2258 DWORD grfStateBits,/* [in] */
2259 DWORD grfMask) /* [in] */
2261 StorageImpl* const This = (StorageImpl*)iface;
2262 This->base.stateBits = (This->base.stateBits & ~grfMask) | (grfStateBits & grfMask);
2267 * Virtual function table for the IStorage32Impl class.
2269 static const IStorageVtbl Storage32Impl_Vtbl =
2271 StorageBaseImpl_QueryInterface,
2272 StorageBaseImpl_AddRef,
2273 StorageBaseImpl_Release,
2274 StorageBaseImpl_CreateStream,
2275 StorageBaseImpl_OpenStream,
2276 StorageImpl_CreateStorage,
2277 StorageBaseImpl_OpenStorage,
2279 StorageImpl_MoveElementTo,
2282 StorageBaseImpl_EnumElements,
2283 StorageImpl_DestroyElement,
2284 StorageBaseImpl_RenameElement,
2285 StorageImpl_SetElementTimes,
2286 StorageBaseImpl_SetClass,
2287 StorageImpl_SetStateBits,
2291 static HRESULT StorageImpl_Construct(
2301 StgProperty currentProperty;
2302 BOOL readSuccessful;
2303 ULONG currentPropertyIndex;
2305 if ( FAILED( validateSTGM(openFlags) ))
2306 return STG_E_INVALIDFLAG;
2308 memset(This, 0, sizeof(StorageImpl));
2310 list_init(&This->base.strmHead);
2312 This->base.lpVtbl = &Storage32Impl_Vtbl;
2313 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2314 This->base.v_destructor = StorageImpl_Destroy;
2315 This->base.openFlags = (openFlags & ~STGM_CREATE);
2316 This->create = create;
2319 * This is the top-level storage so initialize the ancestor pointer
2322 This->base.ancestorStorage = This;
2324 This->hFile = hFile;
2327 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2328 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2329 if (!This->pwcsName)
2330 return STG_E_INSUFFICIENTMEMORY;
2331 strcpyW(This->pwcsName, pwcsName);
2335 * Initialize the big block cache.
2337 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2338 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2339 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2345 if (This->bigBlockFile == 0)
2350 ULARGE_INTEGER size;
2351 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2354 * Initialize all header variables:
2355 * - The big block depot consists of one block and it is at block 0
2356 * - The properties start at block 1
2357 * - There is no small block depot
2359 memset( This->bigBlockDepotStart,
2361 sizeof(This->bigBlockDepotStart));
2363 This->bigBlockDepotCount = 1;
2364 This->bigBlockDepotStart[0] = 0;
2365 This->rootStartBlock = 1;
2366 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2367 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2368 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2369 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2370 This->extBigBlockDepotCount = 0;
2372 StorageImpl_SaveFileHeader(This);
2375 * Add one block for the big block depot and one block for the properties
2377 size.u.HighPart = 0;
2378 size.u.LowPart = This->bigBlockSize * 3;
2379 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2382 * Initialize the big block depot
2384 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2385 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2386 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2387 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2392 * Load the header for the file.
2394 hr = StorageImpl_LoadFileHeader(This);
2398 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2405 * There is no block depot cached yet.
2407 This->indexBlockDepotCached = 0xFFFFFFFF;
2410 * Start searching for free blocks with block 0.
2412 This->prevFreeBlock = 0;
2415 * Create the block chain abstractions.
2417 if(!(This->rootBlockChain =
2418 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2419 return STG_E_READFAULT;
2421 if(!(This->smallBlockDepotChain =
2422 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2424 return STG_E_READFAULT;
2427 * Write the root property (memory only)
2431 StgProperty rootProp;
2433 * Initialize the property chain
2435 memset(&rootProp, 0, sizeof(rootProp));
2436 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2437 sizeof(rootProp.name)/sizeof(WCHAR) );
2438 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2439 rootProp.propertyType = PROPTYPE_ROOT;
2440 rootProp.previousProperty = PROPERTY_NULL;
2441 rootProp.nextProperty = PROPERTY_NULL;
2442 rootProp.dirProperty = PROPERTY_NULL;
2443 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2444 rootProp.size.u.HighPart = 0;
2445 rootProp.size.u.LowPart = 0;
2447 StorageImpl_WriteProperty(This, 0, &rootProp);
2451 * Find the ID of the root in the property sets.
2453 currentPropertyIndex = 0;
2457 readSuccessful = StorageImpl_ReadProperty(
2459 currentPropertyIndex,
2464 if ( (currentProperty.sizeOfNameString != 0 ) &&
2465 (currentProperty.propertyType == PROPTYPE_ROOT) )
2467 This->base.rootPropertySetIndex = currentPropertyIndex;
2471 currentPropertyIndex++;
2473 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2475 if (!readSuccessful)
2478 return STG_E_READFAULT;
2482 * Create the block chain abstraction for the small block root chain.
2484 if(!(This->smallBlockRootChain =
2485 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2486 return STG_E_READFAULT;
2491 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2493 StorageImpl *This = (StorageImpl*) iface;
2494 TRACE("(%p)\n", This);
2496 StorageBaseImpl_DeleteAll(&This->base);
2498 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2500 BlockChainStream_Destroy(This->smallBlockRootChain);
2501 BlockChainStream_Destroy(This->rootBlockChain);
2502 BlockChainStream_Destroy(This->smallBlockDepotChain);
2504 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2505 HeapFree(GetProcessHeap(), 0, This);
2508 /******************************************************************************
2509 * Storage32Impl_GetNextFreeBigBlock
2511 * Returns the index of the next free big block.
2512 * If the big block depot is filled, this method will enlarge it.
2515 static ULONG StorageImpl_GetNextFreeBigBlock(
2518 ULONG depotBlockIndexPos;
2519 BYTE depotBuffer[BIG_BLOCK_SIZE];
2521 ULONG depotBlockOffset;
2522 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2523 ULONG nextBlockIndex = BLOCK_SPECIAL;
2525 ULONG freeBlock = BLOCK_UNUSED;
2527 depotIndex = This->prevFreeBlock / blocksPerDepot;
2528 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2531 * Scan the entire big block depot until we find a block marked free
2533 while (nextBlockIndex != BLOCK_UNUSED)
2535 if (depotIndex < COUNT_BBDEPOTINHEADER)
2537 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2540 * Grow the primary depot.
2542 if (depotBlockIndexPos == BLOCK_UNUSED)
2544 depotBlockIndexPos = depotIndex*blocksPerDepot;
2547 * Add a block depot.
2549 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2550 This->bigBlockDepotCount++;
2551 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2554 * Flag it as a block depot.
2556 StorageImpl_SetNextBlockInChain(This,
2560 /* Save new header information.
2562 StorageImpl_SaveFileHeader(This);
2567 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2569 if (depotBlockIndexPos == BLOCK_UNUSED)
2572 * Grow the extended depot.
2574 ULONG extIndex = BLOCK_UNUSED;
2575 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2576 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2578 if (extBlockOffset == 0)
2580 /* We need an extended block.
2582 extIndex = Storage32Impl_AddExtBlockDepot(This);
2583 This->extBigBlockDepotCount++;
2584 depotBlockIndexPos = extIndex + 1;
2587 depotBlockIndexPos = depotIndex * blocksPerDepot;
2590 * Add a block depot and mark it in the extended block.
2592 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2593 This->bigBlockDepotCount++;
2594 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2596 /* Flag the block depot.
2598 StorageImpl_SetNextBlockInChain(This,
2602 /* If necessary, flag the extended depot block.
2604 if (extIndex != BLOCK_UNUSED)
2605 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2607 /* Save header information.
2609 StorageImpl_SaveFileHeader(This);
2613 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2617 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2618 ( nextBlockIndex != BLOCK_UNUSED))
2620 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2622 if (nextBlockIndex == BLOCK_UNUSED)
2624 freeBlock = (depotIndex * blocksPerDepot) +
2625 (depotBlockOffset/sizeof(ULONG));
2628 depotBlockOffset += sizeof(ULONG);
2633 depotBlockOffset = 0;
2637 * make sure that the block physically exists before using it
2639 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2641 This->prevFreeBlock = freeBlock;
2646 /******************************************************************************
2647 * Storage32Impl_AddBlockDepot
2649 * This will create a depot block, essentially it is a block initialized
2652 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2654 BYTE blockBuffer[BIG_BLOCK_SIZE];
2657 * Initialize blocks as free
2659 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2660 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2663 /******************************************************************************
2664 * Storage32Impl_GetExtDepotBlock
2666 * Returns the index of the block that corresponds to the specified depot
2667 * index. This method is only for depot indexes equal or greater than
2668 * COUNT_BBDEPOTINHEADER.
2670 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2672 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2673 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2674 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2675 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2676 ULONG blockIndex = BLOCK_UNUSED;
2677 ULONG extBlockIndex = This->extBigBlockDepotStart;
2679 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2681 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2682 return BLOCK_UNUSED;
2684 while (extBlockCount > 0)
2686 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2690 if (extBlockIndex != BLOCK_UNUSED)
2691 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
2692 extBlockOffset * sizeof(ULONG), &blockIndex);
2697 /******************************************************************************
2698 * Storage32Impl_SetExtDepotBlock
2700 * Associates the specified block index to the specified depot index.
2701 * This method is only for depot indexes equal or greater than
2702 * COUNT_BBDEPOTINHEADER.
2704 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2706 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2707 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2708 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2709 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2710 ULONG extBlockIndex = This->extBigBlockDepotStart;
2712 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2714 while (extBlockCount > 0)
2716 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2720 if (extBlockIndex != BLOCK_UNUSED)
2722 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
2723 extBlockOffset * sizeof(ULONG),
2728 /******************************************************************************
2729 * Storage32Impl_AddExtBlockDepot
2731 * Creates an extended depot block.
2733 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2735 ULONG numExtBlocks = This->extBigBlockDepotCount;
2736 ULONG nextExtBlock = This->extBigBlockDepotStart;
2737 BYTE depotBuffer[BIG_BLOCK_SIZE];
2738 ULONG index = BLOCK_UNUSED;
2739 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2740 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2741 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2743 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2744 blocksPerDepotBlock;
2746 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2749 * The first extended block.
2751 This->extBigBlockDepotStart = index;
2757 * Follow the chain to the last one.
2759 for (i = 0; i < (numExtBlocks - 1); i++)
2761 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2765 * Add the new extended block to the chain.
2767 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
2772 * Initialize this block.
2774 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2775 StorageImpl_WriteBigBlock(This, index, depotBuffer);
2780 /******************************************************************************
2781 * Storage32Impl_FreeBigBlock
2783 * This method will flag the specified block as free in the big block depot.
2785 static void StorageImpl_FreeBigBlock(
2789 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2791 if (blockIndex < This->prevFreeBlock)
2792 This->prevFreeBlock = blockIndex;
2795 /************************************************************************
2796 * Storage32Impl_GetNextBlockInChain
2798 * This method will retrieve the block index of the next big block in
2801 * Params: This - Pointer to the Storage object.
2802 * blockIndex - Index of the block to retrieve the chain
2804 * nextBlockIndex - receives the return value.
2806 * Returns: This method returns the index of the next block in the chain.
2807 * It will return the constants:
2808 * BLOCK_SPECIAL - If the block given was not part of a
2810 * BLOCK_END_OF_CHAIN - If the block given was the last in
2812 * BLOCK_UNUSED - If the block given was not past of a chain
2814 * BLOCK_EXTBBDEPOT - This block is part of the extended
2817 * See Windows documentation for more details on IStorage methods.
2819 static HRESULT StorageImpl_GetNextBlockInChain(
2822 ULONG* nextBlockIndex)
2824 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2825 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2826 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2827 BYTE depotBuffer[BIG_BLOCK_SIZE];
2829 ULONG depotBlockIndexPos;
2832 *nextBlockIndex = BLOCK_SPECIAL;
2834 if(depotBlockCount >= This->bigBlockDepotCount)
2836 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
2837 This->bigBlockDepotCount);
2838 return STG_E_READFAULT;
2842 * Cache the currently accessed depot block.
2844 if (depotBlockCount != This->indexBlockDepotCached)
2846 This->indexBlockDepotCached = depotBlockCount;
2848 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2850 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2855 * We have to look in the extended depot.
2857 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2860 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2863 return STG_E_READFAULT;
2865 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2867 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2868 This->blockDepotCached[index] = *nextBlockIndex;
2872 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2877 /******************************************************************************
2878 * Storage32Impl_GetNextExtendedBlock
2880 * Given an extended block this method will return the next extended block.
2883 * The last ULONG of an extended block is the block index of the next
2884 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2888 * - The index of the next extended block
2889 * - BLOCK_UNUSED: there is no next extended block.
2890 * - Any other return values denotes failure.
2892 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2894 ULONG nextBlockIndex = BLOCK_SPECIAL;
2895 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2897 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
2900 return nextBlockIndex;
2903 /******************************************************************************
2904 * Storage32Impl_SetNextBlockInChain
2906 * This method will write the index of the specified block's next block
2907 * in the big block depot.
2909 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2912 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2913 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2914 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2917 static void StorageImpl_SetNextBlockInChain(
2922 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2923 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2924 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2925 ULONG depotBlockIndexPos;
2927 assert(depotBlockCount < This->bigBlockDepotCount);
2928 assert(blockIndex != nextBlock);
2930 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2932 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2937 * We have to look in the extended depot.
2939 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2942 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
2945 * Update the cached block depot, if necessary.
2947 if (depotBlockCount == This->indexBlockDepotCached)
2949 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2953 /******************************************************************************
2954 * Storage32Impl_LoadFileHeader
2956 * This method will read in the file header, i.e. big block index -1.
2958 static HRESULT StorageImpl_LoadFileHeader(
2961 HRESULT hr = STG_E_FILENOTFOUND;
2962 BYTE headerBigBlock[BIG_BLOCK_SIZE];
2968 * Get a pointer to the big block of data containing the header.
2970 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
2973 * Extract the information from the header.
2978 * Check for the "magic number" signature and return an error if it is not
2981 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2983 return STG_E_OLDFORMAT;
2986 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2988 return STG_E_INVALIDHEADER;
2991 StorageUtl_ReadWord(
2993 OFFSET_BIGBLOCKSIZEBITS,
2994 &This->bigBlockSizeBits);
2996 StorageUtl_ReadWord(
2998 OFFSET_SMALLBLOCKSIZEBITS,
2999 &This->smallBlockSizeBits);
3001 StorageUtl_ReadDWord(
3003 OFFSET_BBDEPOTCOUNT,
3004 &This->bigBlockDepotCount);
3006 StorageUtl_ReadDWord(
3008 OFFSET_ROOTSTARTBLOCK,
3009 &This->rootStartBlock);
3011 StorageUtl_ReadDWord(
3013 OFFSET_SBDEPOTSTART,
3014 &This->smallBlockDepotStart);
3016 StorageUtl_ReadDWord(
3018 OFFSET_EXTBBDEPOTSTART,
3019 &This->extBigBlockDepotStart);
3021 StorageUtl_ReadDWord(
3023 OFFSET_EXTBBDEPOTCOUNT,
3024 &This->extBigBlockDepotCount);
3026 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3028 StorageUtl_ReadDWord(
3030 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3031 &(This->bigBlockDepotStart[index]));
3035 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3037 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3038 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3041 * Right now, the code is making some assumptions about the size of the
3042 * blocks, just make sure they are what we're expecting.
3044 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3045 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3047 WARN("Broken OLE storage file\n");
3048 hr = STG_E_INVALIDHEADER;
3057 /******************************************************************************
3058 * Storage32Impl_SaveFileHeader
3060 * This method will save to the file the header, i.e. big block -1.
3062 static void StorageImpl_SaveFileHeader(
3065 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3070 * Get a pointer to the big block of data containing the header.
3072 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3075 * If the block read failed, the file is probably new.
3080 * Initialize for all unknown fields.
3082 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3085 * Initialize the magic number.
3087 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3090 * And a bunch of things we don't know what they mean
3092 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3093 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3094 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3095 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3099 * Write the information to the header.
3101 StorageUtl_WriteWord(
3103 OFFSET_BIGBLOCKSIZEBITS,
3104 This->bigBlockSizeBits);
3106 StorageUtl_WriteWord(
3108 OFFSET_SMALLBLOCKSIZEBITS,
3109 This->smallBlockSizeBits);
3111 StorageUtl_WriteDWord(
3113 OFFSET_BBDEPOTCOUNT,
3114 This->bigBlockDepotCount);
3116 StorageUtl_WriteDWord(
3118 OFFSET_ROOTSTARTBLOCK,
3119 This->rootStartBlock);
3121 StorageUtl_WriteDWord(
3123 OFFSET_SBDEPOTSTART,
3124 This->smallBlockDepotStart);
3126 StorageUtl_WriteDWord(
3128 OFFSET_SBDEPOTCOUNT,
3129 This->smallBlockDepotChain ?
3130 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3132 StorageUtl_WriteDWord(
3134 OFFSET_EXTBBDEPOTSTART,
3135 This->extBigBlockDepotStart);
3137 StorageUtl_WriteDWord(
3139 OFFSET_EXTBBDEPOTCOUNT,
3140 This->extBigBlockDepotCount);
3142 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3144 StorageUtl_WriteDWord(
3146 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3147 (This->bigBlockDepotStart[index]));
3151 * Write the big block back to the file.
3153 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3156 /******************************************************************************
3157 * Storage32Impl_ReadProperty
3159 * This method will read the specified property from the property chain.
3161 BOOL StorageImpl_ReadProperty(
3164 StgProperty* buffer)
3166 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3167 ULARGE_INTEGER offsetInPropSet;
3171 offsetInPropSet.u.HighPart = 0;
3172 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3174 readRes = BlockChainStream_ReadAt(
3175 This->rootBlockChain,
3181 if (SUCCEEDED(readRes))
3183 /* replace the name of root entry (often "Root Entry") by the file name */
3184 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3185 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3187 memset(buffer->name, 0, sizeof(buffer->name));
3191 PROPERTY_NAME_BUFFER_LEN );
3192 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3194 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3196 StorageUtl_ReadWord(
3198 OFFSET_PS_NAMELENGTH,
3199 &buffer->sizeOfNameString);
3201 StorageUtl_ReadDWord(
3203 OFFSET_PS_PREVIOUSPROP,
3204 &buffer->previousProperty);
3206 StorageUtl_ReadDWord(
3209 &buffer->nextProperty);
3211 StorageUtl_ReadDWord(
3214 &buffer->dirProperty);
3216 StorageUtl_ReadGUID(
3219 &buffer->propertyUniqueID);
3221 StorageUtl_ReadDWord(
3224 &buffer->timeStampS1);
3226 StorageUtl_ReadDWord(
3229 &buffer->timeStampD1);
3231 StorageUtl_ReadDWord(
3234 &buffer->timeStampS2);
3236 StorageUtl_ReadDWord(
3239 &buffer->timeStampD2);
3241 StorageUtl_ReadDWord(
3243 OFFSET_PS_STARTBLOCK,
3244 &buffer->startingBlock);
3246 StorageUtl_ReadDWord(
3249 &buffer->size.u.LowPart);
3251 buffer->size.u.HighPart = 0;
3254 return SUCCEEDED(readRes) ? TRUE : FALSE;
3257 /*********************************************************************
3258 * Write the specified property into the property chain
3260 BOOL StorageImpl_WriteProperty(
3263 const StgProperty* buffer)
3265 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3266 ULARGE_INTEGER offsetInPropSet;
3270 offsetInPropSet.u.HighPart = 0;
3271 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3273 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3276 currentProperty + OFFSET_PS_NAME,
3278 PROPERTY_NAME_BUFFER_LEN );
3280 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3282 StorageUtl_WriteWord(
3284 OFFSET_PS_NAMELENGTH,
3285 buffer->sizeOfNameString);
3287 StorageUtl_WriteDWord(
3289 OFFSET_PS_PREVIOUSPROP,
3290 buffer->previousProperty);
3292 StorageUtl_WriteDWord(
3295 buffer->nextProperty);
3297 StorageUtl_WriteDWord(
3300 buffer->dirProperty);
3302 StorageUtl_WriteGUID(
3305 &buffer->propertyUniqueID);
3307 StorageUtl_WriteDWord(
3310 buffer->timeStampS1);
3312 StorageUtl_WriteDWord(
3315 buffer->timeStampD1);
3317 StorageUtl_WriteDWord(
3320 buffer->timeStampS2);
3322 StorageUtl_WriteDWord(
3325 buffer->timeStampD2);
3327 StorageUtl_WriteDWord(
3329 OFFSET_PS_STARTBLOCK,
3330 buffer->startingBlock);
3332 StorageUtl_WriteDWord(
3335 buffer->size.u.LowPart);
3337 writeRes = BlockChainStream_WriteAt(This->rootBlockChain,
3342 return SUCCEEDED(writeRes) ? TRUE : FALSE;
3345 static BOOL StorageImpl_ReadBigBlock(
3350 ULARGE_INTEGER ulOffset;
3353 ulOffset.u.HighPart = 0;
3354 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3356 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3357 return (read == This->bigBlockSize);
3360 static BOOL StorageImpl_ReadDWordFromBigBlock(
3366 ULARGE_INTEGER ulOffset;
3370 ulOffset.u.HighPart = 0;
3371 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3372 ulOffset.u.LowPart += offset;
3374 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3375 *value = lendian32toh(tmp);
3376 return (read == sizeof(DWORD));
3379 static BOOL StorageImpl_WriteBigBlock(
3384 ULARGE_INTEGER ulOffset;
3387 ulOffset.u.HighPart = 0;
3388 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3390 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3391 return (wrote == This->bigBlockSize);
3394 static BOOL StorageImpl_WriteDWordToBigBlock(
3400 ULARGE_INTEGER ulOffset;
3403 ulOffset.u.HighPart = 0;
3404 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3405 ulOffset.u.LowPart += offset;
3407 value = htole32(value);
3408 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3409 return (wrote == sizeof(DWORD));
3412 /******************************************************************************
3413 * Storage32Impl_SmallBlocksToBigBlocks
3415 * This method will convert a small block chain to a big block chain.
3416 * The small block chain will be destroyed.
3418 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3420 SmallBlockChainStream** ppsbChain)
3422 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3423 ULARGE_INTEGER size, offset;
3424 ULONG cbRead, cbWritten;
3425 ULARGE_INTEGER cbTotalRead;
3426 ULONG propertyIndex;
3427 HRESULT resWrite = S_OK;
3429 StgProperty chainProperty;
3431 BlockChainStream *bbTempChain = NULL;
3432 BlockChainStream *bigBlockChain = NULL;
3435 * Create a temporary big block chain that doesn't have
3436 * an associated property. This temporary chain will be
3437 * used to copy data from small blocks to big blocks.
3439 bbTempChain = BlockChainStream_Construct(This,
3442 if(!bbTempChain) return NULL;
3444 * Grow the big block chain.
3446 size = SmallBlockChainStream_GetSize(*ppsbChain);
3447 BlockChainStream_SetSize(bbTempChain, size);
3450 * Copy the contents of the small block chain to the big block chain
3451 * by small block size increments.
3453 offset.u.LowPart = 0;
3454 offset.u.HighPart = 0;
3455 cbTotalRead.QuadPart = 0;
3457 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3460 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3462 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3465 if (FAILED(resRead))
3470 cbTotalRead.QuadPart += cbRead;
3472 resWrite = BlockChainStream_WriteAt(bbTempChain,
3478 if (FAILED(resWrite))
3481 offset.u.LowPart += cbRead;
3483 } while (cbTotalRead.QuadPart < size.QuadPart);
3484 HeapFree(GetProcessHeap(),0,buffer);
3486 size.u.HighPart = 0;
3489 if (FAILED(resRead) || FAILED(resWrite))
3491 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3492 BlockChainStream_SetSize(bbTempChain, size);
3493 BlockChainStream_Destroy(bbTempChain);
3498 * Destroy the small block chain.
3500 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3501 SmallBlockChainStream_SetSize(*ppsbChain, size);
3502 SmallBlockChainStream_Destroy(*ppsbChain);
3506 * Change the property information. This chain is now a big block chain
3507 * and it doesn't reside in the small blocks chain anymore.
3509 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3511 chainProperty.startingBlock = bbHeadOfChain;
3513 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3516 * Destroy the temporary propertyless big block chain.
3517 * Create a new big block chain associated with this property.
3519 BlockChainStream_Destroy(bbTempChain);
3520 bigBlockChain = BlockChainStream_Construct(This,
3524 return bigBlockChain;
3527 /******************************************************************************
3528 * Storage32Impl_BigBlocksToSmallBlocks
3530 * This method will convert a big block chain to a small block chain.
3531 * The big block chain will be destroyed on success.
3533 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3535 BlockChainStream** ppbbChain)
3537 ULARGE_INTEGER size, offset, cbTotalRead;
3538 ULONG cbRead, cbWritten, propertyIndex, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3539 HRESULT resWrite = S_OK, resRead;
3540 StgProperty chainProperty;
3542 SmallBlockChainStream* sbTempChain;
3544 TRACE("%p %p\n", This, ppbbChain);
3546 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3552 size = BlockChainStream_GetSize(*ppbbChain);
3553 SmallBlockChainStream_SetSize(sbTempChain, size);
3555 offset.u.HighPart = 0;
3556 offset.u.LowPart = 0;
3557 cbTotalRead.QuadPart = 0;
3558 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3561 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3562 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
3570 cbTotalRead.QuadPart += cbRead;
3572 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3573 cbRead, buffer, &cbWritten);
3575 if(FAILED(resWrite))
3578 offset.u.LowPart += cbRead;
3580 }while(cbTotalRead.QuadPart < size.QuadPart);
3581 HeapFree(GetProcessHeap(), 0, buffer);
3583 size.u.HighPart = 0;
3586 if(FAILED(resRead) || FAILED(resWrite))
3588 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3589 SmallBlockChainStream_SetSize(sbTempChain, size);
3590 SmallBlockChainStream_Destroy(sbTempChain);
3594 /* destroy the original big block chain */
3595 propertyIndex = (*ppbbChain)->ownerPropertyIndex;
3596 BlockChainStream_SetSize(*ppbbChain, size);
3597 BlockChainStream_Destroy(*ppbbChain);
3600 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3601 chainProperty.startingBlock = sbHeadOfChain;
3602 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3604 SmallBlockChainStream_Destroy(sbTempChain);
3605 return SmallBlockChainStream_Construct(This, NULL, propertyIndex);
3608 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3610 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3612 HeapFree(GetProcessHeap(), 0, This);
3615 /******************************************************************************
3617 ** Storage32InternalImpl_Commit
3620 static HRESULT WINAPI StorageInternalImpl_Commit(
3622 DWORD grfCommitFlags) /* [in] */
3624 FIXME("(%p,%x): stub\n", iface, grfCommitFlags);
3628 /******************************************************************************
3630 ** Storage32InternalImpl_Revert
3633 static HRESULT WINAPI StorageInternalImpl_Revert(
3636 FIXME("(%p): stub\n", iface);
3640 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3642 IStorage_Release((IStorage*)This->parentStorage);
3643 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3644 HeapFree(GetProcessHeap(), 0, This);
3647 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3648 IEnumSTATSTG* iface,
3652 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3655 return E_INVALIDARG;
3659 if (IsEqualGUID(&IID_IUnknown, riid) ||
3660 IsEqualGUID(&IID_IEnumSTATSTG, riid))
3663 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3667 return E_NOINTERFACE;
3670 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3671 IEnumSTATSTG* iface)
3673 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3674 return InterlockedIncrement(&This->ref);
3677 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3678 IEnumSTATSTG* iface)
3680 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3684 newRef = InterlockedDecrement(&This->ref);
3688 IEnumSTATSTGImpl_Destroy(This);
3694 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3695 IEnumSTATSTG* iface,
3698 ULONG* pceltFetched)
3700 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3702 StgProperty currentProperty;
3703 STATSTG* currentReturnStruct = rgelt;
3704 ULONG objectFetched = 0;
3705 ULONG currentSearchNode;
3707 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3708 return E_INVALIDARG;
3711 * To avoid the special case, get another pointer to a ULONG value if
3712 * the caller didn't supply one.
3714 if (pceltFetched==0)
3715 pceltFetched = &objectFetched;
3718 * Start the iteration, we will iterate until we hit the end of the
3719 * linked list or until we hit the number of items to iterate through
3724 * Start with the node at the top of the stack.
3726 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3728 while ( ( *pceltFetched < celt) &&
3729 ( currentSearchNode!=PROPERTY_NULL) )
3732 * Remove the top node from the stack
3734 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3737 * Read the property from the storage.
3739 StorageImpl_ReadProperty(This->parentStorage,
3744 * Copy the information to the return buffer.
3746 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3751 * Step to the next item in the iteration
3754 currentReturnStruct++;
3757 * Push the next search node in the search stack.
3759 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3762 * continue the iteration.
3764 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3767 if (*pceltFetched == celt)
3774 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3775 IEnumSTATSTG* iface,
3778 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3780 StgProperty currentProperty;
3781 ULONG objectFetched = 0;
3782 ULONG currentSearchNode;
3785 * Start with the node at the top of the stack.
3787 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3789 while ( (objectFetched < celt) &&
3790 (currentSearchNode!=PROPERTY_NULL) )
3793 * Remove the top node from the stack
3795 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3798 * Read the property from the storage.
3800 StorageImpl_ReadProperty(This->parentStorage,
3805 * Step to the next item in the iteration
3810 * Push the next search node in the search stack.
3812 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3815 * continue the iteration.
3817 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3820 if (objectFetched == celt)
3826 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3827 IEnumSTATSTG* iface)
3829 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3831 StgProperty rootProperty;
3832 BOOL readSuccessful;
3835 * Re-initialize the search stack to an empty stack
3837 This->stackSize = 0;
3840 * Read the root property from the storage.
3842 readSuccessful = StorageImpl_ReadProperty(
3843 This->parentStorage,
3844 This->firstPropertyNode,
3849 assert(rootProperty.sizeOfNameString!=0);
3852 * Push the search node in the search stack.
3854 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3860 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3861 IEnumSTATSTG* iface,
3862 IEnumSTATSTG** ppenum)
3864 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3866 IEnumSTATSTGImpl* newClone;
3869 * Perform a sanity check on the parameters.
3872 return E_INVALIDARG;
3874 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3875 This->firstPropertyNode);
3879 * The new clone enumeration must point to the same current node as
3882 newClone->stackSize = This->stackSize ;
3883 newClone->stackMaxSize = This->stackMaxSize ;
3884 newClone->stackToVisit =
3885 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3888 newClone->stackToVisit,
3890 sizeof(ULONG) * newClone->stackSize);
3892 *ppenum = (IEnumSTATSTG*)newClone;
3895 * Don't forget to nail down a reference to the clone before
3898 IEnumSTATSTGImpl_AddRef(*ppenum);
3903 static INT IEnumSTATSTGImpl_FindParentProperty(
3904 IEnumSTATSTGImpl *This,
3905 ULONG childProperty,
3906 StgProperty *currentProperty,
3909 ULONG currentSearchNode;
3913 * To avoid the special case, get another pointer to a ULONG value if
3914 * the caller didn't supply one.
3917 thisNodeId = &foundNode;
3920 * Start with the node at the top of the stack.
3922 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3925 while (currentSearchNode!=PROPERTY_NULL)
3928 * Store the current node in the returned parameters
3930 *thisNodeId = currentSearchNode;
3933 * Remove the top node from the stack
3935 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3938 * Read the property from the storage.
3940 StorageImpl_ReadProperty(
3941 This->parentStorage,
3945 if (currentProperty->previousProperty == childProperty)
3946 return PROPERTY_RELATION_PREVIOUS;
3948 else if (currentProperty->nextProperty == childProperty)
3949 return PROPERTY_RELATION_NEXT;
3951 else if (currentProperty->dirProperty == childProperty)
3952 return PROPERTY_RELATION_DIR;
3955 * Push the next search node in the search stack.
3957 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3960 * continue the iteration.
3962 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3965 return PROPERTY_NULL;
3968 static ULONG IEnumSTATSTGImpl_FindProperty(
3969 IEnumSTATSTGImpl* This,
3970 const OLECHAR* lpszPropName,
3971 StgProperty* currentProperty)
3973 ULONG currentSearchNode;
3976 * Start with the node at the top of the stack.
3978 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3980 while (currentSearchNode!=PROPERTY_NULL)
3983 * Remove the top node from the stack
3985 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3988 * Read the property from the storage.
3990 StorageImpl_ReadProperty(This->parentStorage,
3994 if (propertyNameCmp(currentProperty->name, lpszPropName) == 0)
3995 return currentSearchNode;
3998 * Push the next search node in the search stack.
4000 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4003 * continue the iteration.
4005 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4008 return PROPERTY_NULL;
4011 static void IEnumSTATSTGImpl_PushSearchNode(
4012 IEnumSTATSTGImpl* This,
4015 StgProperty rootProperty;
4016 BOOL readSuccessful;
4019 * First, make sure we're not trying to push an unexisting node.
4021 if (nodeToPush==PROPERTY_NULL)
4025 * First push the node to the stack
4027 if (This->stackSize == This->stackMaxSize)
4029 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4031 This->stackToVisit = HeapReAlloc(
4035 sizeof(ULONG) * This->stackMaxSize);
4038 This->stackToVisit[This->stackSize] = nodeToPush;
4042 * Read the root property from the storage.
4044 readSuccessful = StorageImpl_ReadProperty(
4045 This->parentStorage,
4051 assert(rootProperty.sizeOfNameString!=0);
4054 * Push the previous search node in the search stack.
4056 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4060 static ULONG IEnumSTATSTGImpl_PopSearchNode(
4061 IEnumSTATSTGImpl* This,
4066 if (This->stackSize == 0)
4067 return PROPERTY_NULL;
4069 topNode = This->stackToVisit[This->stackSize-1];
4078 * Virtual function table for the IEnumSTATSTGImpl class.
4080 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4082 IEnumSTATSTGImpl_QueryInterface,
4083 IEnumSTATSTGImpl_AddRef,
4084 IEnumSTATSTGImpl_Release,
4085 IEnumSTATSTGImpl_Next,
4086 IEnumSTATSTGImpl_Skip,
4087 IEnumSTATSTGImpl_Reset,
4088 IEnumSTATSTGImpl_Clone
4091 /******************************************************************************
4092 ** IEnumSTATSTGImpl implementation
4095 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4096 StorageImpl* parentStorage,
4097 ULONG firstPropertyNode)
4099 IEnumSTATSTGImpl* newEnumeration;
4101 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4103 if (newEnumeration!=0)
4106 * Set-up the virtual function table and reference count.
4108 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4109 newEnumeration->ref = 0;
4112 * We want to nail-down the reference to the storage in case the
4113 * enumeration out-lives the storage in the client application.
4115 newEnumeration->parentStorage = parentStorage;
4116 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4118 newEnumeration->firstPropertyNode = firstPropertyNode;
4121 * Initialize the search stack
4123 newEnumeration->stackSize = 0;
4124 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4125 newEnumeration->stackToVisit =
4126 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4129 * Make sure the current node of the iterator is the first one.
4131 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4134 return newEnumeration;
4138 * Virtual function table for the Storage32InternalImpl class.
4140 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4142 StorageBaseImpl_QueryInterface,
4143 StorageBaseImpl_AddRef,
4144 StorageBaseImpl_Release,
4145 StorageBaseImpl_CreateStream,
4146 StorageBaseImpl_OpenStream,
4147 StorageImpl_CreateStorage,
4148 StorageBaseImpl_OpenStorage,
4150 StorageImpl_MoveElementTo,
4151 StorageInternalImpl_Commit,
4152 StorageInternalImpl_Revert,
4153 StorageBaseImpl_EnumElements,
4154 StorageImpl_DestroyElement,
4155 StorageBaseImpl_RenameElement,
4156 StorageImpl_SetElementTimes,
4157 StorageBaseImpl_SetClass,
4158 StorageImpl_SetStateBits,
4159 StorageBaseImpl_Stat
4162 /******************************************************************************
4163 ** Storage32InternalImpl implementation
4166 static StorageInternalImpl* StorageInternalImpl_Construct(
4167 StorageImpl* ancestorStorage,
4169 ULONG rootPropertyIndex)
4171 StorageInternalImpl* newStorage;
4173 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
4178 * Initialize the stream list
4180 list_init(&newStorage->base.strmHead);
4183 * Initialize the virtual function table.
4185 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4186 newStorage->base.v_destructor = StorageInternalImpl_Destroy;
4187 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4190 * Keep the ancestor storage pointer but do not nail a reference to it.
4192 newStorage->base.ancestorStorage = ancestorStorage;
4195 * Keep the index of the root property set for this storage,
4197 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4205 /******************************************************************************
4206 ** StorageUtl implementation
4209 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4213 memcpy(&tmp, buffer+offset, sizeof(WORD));
4214 *value = lendian16toh(tmp);
4217 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4219 value = htole16(value);
4220 memcpy(buffer+offset, &value, sizeof(WORD));
4223 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4227 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4228 *value = lendian32toh(tmp);
4231 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4233 value = htole32(value);
4234 memcpy(buffer+offset, &value, sizeof(DWORD));
4237 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4238 ULARGE_INTEGER* value)
4240 #ifdef WORDS_BIGENDIAN
4243 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4244 value->u.LowPart = htole32(tmp.u.HighPart);
4245 value->u.HighPart = htole32(tmp.u.LowPart);
4247 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4251 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4252 const ULARGE_INTEGER *value)
4254 #ifdef WORDS_BIGENDIAN
4257 tmp.u.LowPart = htole32(value->u.HighPart);
4258 tmp.u.HighPart = htole32(value->u.LowPart);
4259 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4261 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4265 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4267 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4268 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4269 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4271 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4274 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4276 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4277 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4278 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4280 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4283 void StorageUtl_CopyPropertyToSTATSTG(
4284 STATSTG* destination,
4285 const StgProperty* source,
4289 * The copy of the string occurs only when the flag is not set
4291 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4292 (source->name == NULL) ||
4293 (source->name[0] == 0) )
4295 destination->pwcsName = 0;
4299 destination->pwcsName =
4300 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4302 strcpyW(destination->pwcsName, source->name);
4305 switch (source->propertyType)
4307 case PROPTYPE_STORAGE:
4309 destination->type = STGTY_STORAGE;
4311 case PROPTYPE_STREAM:
4312 destination->type = STGTY_STREAM;
4315 destination->type = STGTY_STREAM;
4319 destination->cbSize = source->size;
4321 currentReturnStruct->mtime = {0}; TODO
4322 currentReturnStruct->ctime = {0};
4323 currentReturnStruct->atime = {0};
4325 destination->grfMode = 0;
4326 destination->grfLocksSupported = 0;
4327 destination->clsid = source->propertyUniqueID;
4328 destination->grfStateBits = 0;
4329 destination->reserved = 0;
4332 /******************************************************************************
4333 ** BlockChainStream implementation
4336 BlockChainStream* BlockChainStream_Construct(
4337 StorageImpl* parentStorage,
4338 ULONG* headOfStreamPlaceHolder,
4339 ULONG propertyIndex)
4341 BlockChainStream* newStream;
4344 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4346 newStream->parentStorage = parentStorage;
4347 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4348 newStream->ownerPropertyIndex = propertyIndex;
4349 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4350 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4351 newStream->numBlocks = 0;
4353 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4355 while (blockIndex != BLOCK_END_OF_CHAIN)
4357 newStream->numBlocks++;
4358 newStream->tailIndex = blockIndex;
4360 if(FAILED(StorageImpl_GetNextBlockInChain(
4365 HeapFree(GetProcessHeap(), 0, newStream);
4373 void BlockChainStream_Destroy(BlockChainStream* This)
4375 HeapFree(GetProcessHeap(), 0, This);
4378 /******************************************************************************
4379 * BlockChainStream_GetHeadOfChain
4381 * Returns the head of this stream chain.
4382 * Some special chains don't have properties, their heads are kept in
4383 * This->headOfStreamPlaceHolder.
4386 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4388 StgProperty chainProperty;
4389 BOOL readSuccessful;
4391 if (This->headOfStreamPlaceHolder != 0)
4392 return *(This->headOfStreamPlaceHolder);
4394 if (This->ownerPropertyIndex != PROPERTY_NULL)
4396 readSuccessful = StorageImpl_ReadProperty(
4397 This->parentStorage,
4398 This->ownerPropertyIndex,
4403 return chainProperty.startingBlock;
4407 return BLOCK_END_OF_CHAIN;
4410 /******************************************************************************
4411 * BlockChainStream_GetCount
4413 * Returns the number of blocks that comprises this chain.
4414 * This is not the size of the stream as the last block may not be full!
4417 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
4422 blockIndex = BlockChainStream_GetHeadOfChain(This);
4424 while (blockIndex != BLOCK_END_OF_CHAIN)
4428 if(FAILED(StorageImpl_GetNextBlockInChain(
4429 This->parentStorage,
4438 /******************************************************************************
4439 * BlockChainStream_ReadAt
4441 * Reads a specified number of bytes from this chain at the specified offset.
4442 * bytesRead may be NULL.
4443 * Failure will be returned if the specified number of bytes has not been read.
4445 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
4446 ULARGE_INTEGER offset,
4451 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4452 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4453 ULONG bytesToReadInBuffer;
4457 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
4460 * Find the first block in the stream that contains part of the buffer.
4462 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4463 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4464 (blockNoInSequence < This->lastBlockNoInSequence) )
4466 blockIndex = BlockChainStream_GetHeadOfChain(This);
4467 This->lastBlockNoInSequence = blockNoInSequence;
4471 ULONG temp = blockNoInSequence;
4473 blockIndex = This->lastBlockNoInSequenceIndex;
4474 blockNoInSequence -= This->lastBlockNoInSequence;
4475 This->lastBlockNoInSequence = temp;
4478 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4480 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4481 return STG_E_DOCFILECORRUPT;
4482 blockNoInSequence--;
4485 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4486 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
4488 This->lastBlockNoInSequenceIndex = blockIndex;
4491 * Start reading the buffer.
4494 bufferWalker = buffer;
4496 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4498 ULARGE_INTEGER ulOffset;
4501 * Calculate how many bytes we can copy from this big block.
4503 bytesToReadInBuffer =
4504 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4506 TRACE("block %i\n",blockIndex);
4507 ulOffset.u.HighPart = 0;
4508 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4511 StorageImpl_ReadAt(This->parentStorage,
4514 bytesToReadInBuffer,
4517 * Step to the next big block.
4519 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4520 return STG_E_DOCFILECORRUPT;
4522 bufferWalker += bytesReadAt;
4523 size -= bytesReadAt;
4524 *bytesRead += bytesReadAt;
4525 offsetInBlock = 0; /* There is no offset on the next block */
4527 if (bytesToReadInBuffer != bytesReadAt)
4531 return (size == 0) ? S_OK : STG_E_READFAULT;
4534 /******************************************************************************
4535 * BlockChainStream_WriteAt
4537 * Writes the specified number of bytes to this chain at the specified offset.
4538 * Will fail if not all specified number of bytes have been written.
4540 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
4541 ULARGE_INTEGER offset,
4544 ULONG* bytesWritten)
4546 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4547 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4550 const BYTE* bufferWalker;
4553 * Find the first block in the stream that contains part of the buffer.
4555 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4556 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4557 (blockNoInSequence < This->lastBlockNoInSequence) )
4559 blockIndex = BlockChainStream_GetHeadOfChain(This);
4560 This->lastBlockNoInSequence = blockNoInSequence;
4564 ULONG temp = blockNoInSequence;
4566 blockIndex = This->lastBlockNoInSequenceIndex;
4567 blockNoInSequence -= This->lastBlockNoInSequence;
4568 This->lastBlockNoInSequence = temp;
4571 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4573 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4575 return STG_E_DOCFILECORRUPT;
4576 blockNoInSequence--;
4579 This->lastBlockNoInSequenceIndex = blockIndex;
4581 /* BlockChainStream_SetSize should have already been called to ensure we have
4582 * enough blocks in the chain to write into */
4583 if (blockIndex == BLOCK_END_OF_CHAIN)
4585 ERR("not enough blocks in chain to write data\n");
4586 return STG_E_DOCFILECORRUPT;
4590 bufferWalker = buffer;
4592 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4594 ULARGE_INTEGER ulOffset;
4595 DWORD bytesWrittenAt;
4597 * Calculate how many bytes we can copy from this big block.
4600 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4602 TRACE("block %i\n",blockIndex);
4603 ulOffset.u.HighPart = 0;
4604 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4607 StorageImpl_WriteAt(This->parentStorage,
4614 * Step to the next big block.
4616 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4618 return STG_E_DOCFILECORRUPT;
4620 bufferWalker += bytesWrittenAt;
4621 size -= bytesWrittenAt;
4622 *bytesWritten += bytesWrittenAt;
4623 offsetInBlock = 0; /* There is no offset on the next block */
4625 if (bytesWrittenAt != bytesToWrite)
4629 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
4632 /******************************************************************************
4633 * BlockChainStream_Shrink
4635 * Shrinks this chain in the big block depot.
4637 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
4638 ULARGE_INTEGER newSize)
4640 ULONG blockIndex, extraBlock;
4645 * Reset the last accessed block cache.
4647 This->lastBlockNoInSequence = 0xFFFFFFFF;
4648 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4651 * Figure out how many blocks are needed to contain the new size
4653 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4655 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4658 blockIndex = BlockChainStream_GetHeadOfChain(This);
4661 * Go to the new end of chain
4663 while (count < numBlocks)
4665 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4671 /* Get the next block before marking the new end */
4672 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4676 /* Mark the new end of chain */
4677 StorageImpl_SetNextBlockInChain(
4678 This->parentStorage,
4680 BLOCK_END_OF_CHAIN);
4682 This->tailIndex = blockIndex;
4683 This->numBlocks = numBlocks;
4686 * Mark the extra blocks as free
4688 while (extraBlock != BLOCK_END_OF_CHAIN)
4690 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4693 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4694 extraBlock = blockIndex;
4700 /******************************************************************************
4701 * BlockChainStream_Enlarge
4703 * Grows this chain in the big block depot.
4705 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4706 ULARGE_INTEGER newSize)
4708 ULONG blockIndex, currentBlock;
4710 ULONG oldNumBlocks = 0;
4712 blockIndex = BlockChainStream_GetHeadOfChain(This);
4715 * Empty chain. Create the head.
4717 if (blockIndex == BLOCK_END_OF_CHAIN)
4719 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4720 StorageImpl_SetNextBlockInChain(This->parentStorage,
4722 BLOCK_END_OF_CHAIN);
4724 if (This->headOfStreamPlaceHolder != 0)
4726 *(This->headOfStreamPlaceHolder) = blockIndex;
4730 StgProperty chainProp;
4731 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4733 StorageImpl_ReadProperty(
4734 This->parentStorage,
4735 This->ownerPropertyIndex,
4738 chainProp.startingBlock = blockIndex;
4740 StorageImpl_WriteProperty(
4741 This->parentStorage,
4742 This->ownerPropertyIndex,
4746 This->tailIndex = blockIndex;
4747 This->numBlocks = 1;
4751 * Figure out how many blocks are needed to contain this stream
4753 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4755 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4759 * Go to the current end of chain
4761 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4763 currentBlock = blockIndex;
4765 while (blockIndex != BLOCK_END_OF_CHAIN)
4768 currentBlock = blockIndex;
4770 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4775 This->tailIndex = currentBlock;
4778 currentBlock = This->tailIndex;
4779 oldNumBlocks = This->numBlocks;
4782 * Add new blocks to the chain
4784 if (oldNumBlocks < newNumBlocks)
4786 while (oldNumBlocks < newNumBlocks)
4788 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4790 StorageImpl_SetNextBlockInChain(
4791 This->parentStorage,
4795 StorageImpl_SetNextBlockInChain(
4796 This->parentStorage,
4798 BLOCK_END_OF_CHAIN);
4800 currentBlock = blockIndex;
4804 This->tailIndex = blockIndex;
4805 This->numBlocks = newNumBlocks;
4811 /******************************************************************************
4812 * BlockChainStream_SetSize
4814 * Sets the size of this stream. The big block depot will be updated.
4815 * The file will grow if we grow the chain.
4817 * TODO: Free the actual blocks in the file when we shrink the chain.
4818 * Currently, the blocks are still in the file. So the file size
4819 * doesn't shrink even if we shrink streams.
4821 BOOL BlockChainStream_SetSize(
4822 BlockChainStream* This,
4823 ULARGE_INTEGER newSize)
4825 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4827 if (newSize.u.LowPart == size.u.LowPart)
4830 if (newSize.u.LowPart < size.u.LowPart)
4832 BlockChainStream_Shrink(This, newSize);
4836 BlockChainStream_Enlarge(This, newSize);
4842 /******************************************************************************
4843 * BlockChainStream_GetSize
4845 * Returns the size of this chain.
4846 * Will return the block count if this chain doesn't have a property.
4848 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4850 StgProperty chainProperty;
4852 if(This->headOfStreamPlaceHolder == NULL)
4855 * This chain is a data stream read the property and return
4856 * the appropriate size
4858 StorageImpl_ReadProperty(
4859 This->parentStorage,
4860 This->ownerPropertyIndex,
4863 return chainProperty.size;
4868 * this chain is a chain that does not have a property, figure out the
4869 * size by making the product number of used blocks times the
4872 ULARGE_INTEGER result;
4873 result.u.HighPart = 0;
4876 BlockChainStream_GetCount(This) *
4877 This->parentStorage->bigBlockSize;
4883 /******************************************************************************
4884 ** SmallBlockChainStream implementation
4887 SmallBlockChainStream* SmallBlockChainStream_Construct(
4888 StorageImpl* parentStorage,
4889 ULONG* headOfStreamPlaceHolder,
4890 ULONG propertyIndex)
4892 SmallBlockChainStream* newStream;
4894 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4896 newStream->parentStorage = parentStorage;
4897 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4898 newStream->ownerPropertyIndex = propertyIndex;
4903 void SmallBlockChainStream_Destroy(
4904 SmallBlockChainStream* This)
4906 HeapFree(GetProcessHeap(), 0, This);
4909 /******************************************************************************
4910 * SmallBlockChainStream_GetHeadOfChain
4912 * Returns the head of this chain of small blocks.
4914 static ULONG SmallBlockChainStream_GetHeadOfChain(
4915 SmallBlockChainStream* This)
4917 StgProperty chainProperty;
4918 BOOL readSuccessful;
4920 if (This->headOfStreamPlaceHolder != NULL)
4921 return *(This->headOfStreamPlaceHolder);
4923 if (This->ownerPropertyIndex)
4925 readSuccessful = StorageImpl_ReadProperty(
4926 This->parentStorage,
4927 This->ownerPropertyIndex,
4932 return chainProperty.startingBlock;
4937 return BLOCK_END_OF_CHAIN;
4940 /******************************************************************************
4941 * SmallBlockChainStream_GetNextBlockInChain
4943 * Returns the index of the next small block in this chain.
4946 * - BLOCK_END_OF_CHAIN: end of this chain
4947 * - BLOCK_UNUSED: small block 'blockIndex' is free
4949 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
4950 SmallBlockChainStream* This,
4952 ULONG* nextBlockInChain)
4954 ULARGE_INTEGER offsetOfBlockInDepot;
4959 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4961 offsetOfBlockInDepot.u.HighPart = 0;
4962 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4965 * Read those bytes in the buffer from the small block file.
4967 res = BlockChainStream_ReadAt(
4968 This->parentStorage->smallBlockDepotChain,
4969 offsetOfBlockInDepot,
4976 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
4983 /******************************************************************************
4984 * SmallBlockChainStream_SetNextBlockInChain
4986 * Writes the index of the next block of the specified block in the small
4988 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4989 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4991 static void SmallBlockChainStream_SetNextBlockInChain(
4992 SmallBlockChainStream* This,
4996 ULARGE_INTEGER offsetOfBlockInDepot;
5000 offsetOfBlockInDepot.u.HighPart = 0;
5001 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5003 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
5006 * Read those bytes in the buffer from the small block file.
5008 BlockChainStream_WriteAt(
5009 This->parentStorage->smallBlockDepotChain,
5010 offsetOfBlockInDepot,
5016 /******************************************************************************
5017 * SmallBlockChainStream_FreeBlock
5019 * Flag small block 'blockIndex' as free in the small block depot.
5021 static void SmallBlockChainStream_FreeBlock(
5022 SmallBlockChainStream* This,
5025 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5028 /******************************************************************************
5029 * SmallBlockChainStream_GetNextFreeBlock
5031 * Returns the index of a free small block. The small block depot will be
5032 * enlarged if necessary. The small block chain will also be enlarged if
5035 static ULONG SmallBlockChainStream_GetNextFreeBlock(
5036 SmallBlockChainStream* This)
5038 ULARGE_INTEGER offsetOfBlockInDepot;
5041 ULONG blockIndex = 0;
5042 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5044 ULONG smallBlocksPerBigBlock;
5046 offsetOfBlockInDepot.u.HighPart = 0;
5049 * Scan the small block depot for a free block
5051 while (nextBlockIndex != BLOCK_UNUSED)
5053 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5055 res = BlockChainStream_ReadAt(
5056 This->parentStorage->smallBlockDepotChain,
5057 offsetOfBlockInDepot,
5063 * If we run out of space for the small block depot, enlarge it
5067 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5069 if (nextBlockIndex != BLOCK_UNUSED)
5075 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5077 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5078 ULONG nextBlock, newsbdIndex;
5079 BYTE smallBlockDepot[BIG_BLOCK_SIZE];
5081 nextBlock = sbdIndex;
5082 while (nextBlock != BLOCK_END_OF_CHAIN)
5084 sbdIndex = nextBlock;
5085 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5088 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5089 if (sbdIndex != BLOCK_END_OF_CHAIN)
5090 StorageImpl_SetNextBlockInChain(
5091 This->parentStorage,
5095 StorageImpl_SetNextBlockInChain(
5096 This->parentStorage,
5098 BLOCK_END_OF_CHAIN);
5101 * Initialize all the small blocks to free
5103 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5104 StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
5109 * We have just created the small block depot.
5111 StgProperty rootProp;
5115 * Save it in the header
5117 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5118 StorageImpl_SaveFileHeader(This->parentStorage);
5121 * And allocate the first big block that will contain small blocks
5124 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5126 StorageImpl_SetNextBlockInChain(
5127 This->parentStorage,
5129 BLOCK_END_OF_CHAIN);
5131 StorageImpl_ReadProperty(
5132 This->parentStorage,
5133 This->parentStorage->base.rootPropertySetIndex,
5136 rootProp.startingBlock = sbStartIndex;
5137 rootProp.size.u.HighPart = 0;
5138 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
5140 StorageImpl_WriteProperty(
5141 This->parentStorage,
5142 This->parentStorage->base.rootPropertySetIndex,
5146 StorageImpl_SaveFileHeader(This->parentStorage);
5150 smallBlocksPerBigBlock =
5151 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5154 * Verify if we have to allocate big blocks to contain small blocks
5156 if (blockIndex % smallBlocksPerBigBlock == 0)
5158 StgProperty rootProp;
5159 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5161 StorageImpl_ReadProperty(
5162 This->parentStorage,
5163 This->parentStorage->base.rootPropertySetIndex,
5166 if (rootProp.size.u.LowPart <
5167 (blocksRequired * This->parentStorage->bigBlockSize))
5169 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5171 BlockChainStream_SetSize(
5172 This->parentStorage->smallBlockRootChain,
5175 StorageImpl_WriteProperty(
5176 This->parentStorage,
5177 This->parentStorage->base.rootPropertySetIndex,
5185 /******************************************************************************
5186 * SmallBlockChainStream_ReadAt
5188 * Reads a specified number of bytes from this chain at the specified offset.
5189 * bytesRead may be NULL.
5190 * Failure will be returned if the specified number of bytes has not been read.
5192 HRESULT SmallBlockChainStream_ReadAt(
5193 SmallBlockChainStream* This,
5194 ULARGE_INTEGER offset,
5200 ULARGE_INTEGER offsetInBigBlockFile;
5201 ULONG blockNoInSequence =
5202 offset.u.LowPart / This->parentStorage->smallBlockSize;
5204 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5205 ULONG bytesToReadInBuffer;
5207 ULONG bytesReadFromBigBlockFile;
5211 * This should never happen on a small block file.
5213 assert(offset.u.HighPart==0);
5216 * Find the first block in the stream that contains part of the buffer.
5218 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5220 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5222 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5225 blockNoInSequence--;
5229 * Start reading the buffer.
5232 bufferWalker = buffer;
5234 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5237 * Calculate how many bytes we can copy from this small block.
5239 bytesToReadInBuffer =
5240 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5243 * Calculate the offset of the small block in the small block file.
5245 offsetInBigBlockFile.u.HighPart = 0;
5246 offsetInBigBlockFile.u.LowPart =
5247 blockIndex * This->parentStorage->smallBlockSize;
5249 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5252 * Read those bytes in the buffer from the small block file.
5253 * The small block has already been identified so it shouldn't fail
5254 * unless the file is corrupt.
5256 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5257 offsetInBigBlockFile,
5258 bytesToReadInBuffer,
5260 &bytesReadFromBigBlockFile);
5266 * Step to the next big block.
5268 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5270 return STG_E_DOCFILECORRUPT;
5272 bufferWalker += bytesReadFromBigBlockFile;
5273 size -= bytesReadFromBigBlockFile;
5274 *bytesRead += bytesReadFromBigBlockFile;
5275 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5278 return (size == 0) ? S_OK : STG_E_READFAULT;
5281 /******************************************************************************
5282 * SmallBlockChainStream_WriteAt
5284 * Writes the specified number of bytes to this chain at the specified offset.
5285 * Will fail if not all specified number of bytes have been written.
5287 HRESULT SmallBlockChainStream_WriteAt(
5288 SmallBlockChainStream* This,
5289 ULARGE_INTEGER offset,
5292 ULONG* bytesWritten)
5294 ULARGE_INTEGER offsetInBigBlockFile;
5295 ULONG blockNoInSequence =
5296 offset.u.LowPart / This->parentStorage->smallBlockSize;
5298 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5299 ULONG bytesToWriteInBuffer;
5301 ULONG bytesWrittenToBigBlockFile;
5302 const BYTE* bufferWalker;
5306 * This should never happen on a small block file.
5308 assert(offset.u.HighPart==0);
5311 * Find the first block in the stream that contains part of the buffer.
5313 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5315 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5317 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5318 return STG_E_DOCFILECORRUPT;
5319 blockNoInSequence--;
5323 * Start writing the buffer.
5326 bufferWalker = buffer;
5327 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5330 * Calculate how many bytes we can copy to this small block.
5332 bytesToWriteInBuffer =
5333 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5336 * Calculate the offset of the small block in the small block file.
5338 offsetInBigBlockFile.u.HighPart = 0;
5339 offsetInBigBlockFile.u.LowPart =
5340 blockIndex * This->parentStorage->smallBlockSize;
5342 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5345 * Write those bytes in the buffer to the small block file.
5347 res = BlockChainStream_WriteAt(
5348 This->parentStorage->smallBlockRootChain,
5349 offsetInBigBlockFile,
5350 bytesToWriteInBuffer,
5352 &bytesWrittenToBigBlockFile);
5357 * Step to the next big block.
5359 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5362 bufferWalker += bytesWrittenToBigBlockFile;
5363 size -= bytesWrittenToBigBlockFile;
5364 *bytesWritten += bytesWrittenToBigBlockFile;
5365 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
5368 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5371 /******************************************************************************
5372 * SmallBlockChainStream_Shrink
5374 * Shrinks this chain in the small block depot.
5376 static BOOL SmallBlockChainStream_Shrink(
5377 SmallBlockChainStream* This,
5378 ULARGE_INTEGER newSize)
5380 ULONG blockIndex, extraBlock;
5384 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5386 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5389 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5392 * Go to the new end of chain
5394 while (count < numBlocks)
5396 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5403 * If the count is 0, we have a special case, the head of the chain was
5408 StgProperty chainProp;
5410 StorageImpl_ReadProperty(This->parentStorage,
5411 This->ownerPropertyIndex,
5414 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5416 StorageImpl_WriteProperty(This->parentStorage,
5417 This->ownerPropertyIndex,
5421 * We start freeing the chain at the head block.
5423 extraBlock = blockIndex;
5427 /* Get the next block before marking the new end */
5428 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5432 /* Mark the new end of chain */
5433 SmallBlockChainStream_SetNextBlockInChain(
5436 BLOCK_END_OF_CHAIN);
5440 * Mark the extra blocks as free
5442 while (extraBlock != BLOCK_END_OF_CHAIN)
5444 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5447 SmallBlockChainStream_FreeBlock(This, extraBlock);
5448 extraBlock = blockIndex;
5454 /******************************************************************************
5455 * SmallBlockChainStream_Enlarge
5457 * Grows this chain in the small block depot.
5459 static BOOL SmallBlockChainStream_Enlarge(
5460 SmallBlockChainStream* This,
5461 ULARGE_INTEGER newSize)
5463 ULONG blockIndex, currentBlock;
5465 ULONG oldNumBlocks = 0;
5467 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5470 * Empty chain. Create the head.
5472 if (blockIndex == BLOCK_END_OF_CHAIN)
5474 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5475 SmallBlockChainStream_SetNextBlockInChain(
5478 BLOCK_END_OF_CHAIN);
5480 if (This->headOfStreamPlaceHolder != NULL)
5482 *(This->headOfStreamPlaceHolder) = blockIndex;
5486 StgProperty chainProp;
5488 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5491 chainProp.startingBlock = blockIndex;
5493 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5498 currentBlock = blockIndex;
5501 * Figure out how many blocks are needed to contain this stream
5503 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5505 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5509 * Go to the current end of chain
5511 while (blockIndex != BLOCK_END_OF_CHAIN)
5514 currentBlock = blockIndex;
5515 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5520 * Add new blocks to the chain
5522 while (oldNumBlocks < newNumBlocks)
5524 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5525 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5527 SmallBlockChainStream_SetNextBlockInChain(
5530 BLOCK_END_OF_CHAIN);
5532 currentBlock = blockIndex;
5539 /******************************************************************************
5540 * SmallBlockChainStream_SetSize
5542 * Sets the size of this stream.
5543 * The file will grow if we grow the chain.
5545 * TODO: Free the actual blocks in the file when we shrink the chain.
5546 * Currently, the blocks are still in the file. So the file size
5547 * doesn't shrink even if we shrink streams.
5549 BOOL SmallBlockChainStream_SetSize(
5550 SmallBlockChainStream* This,
5551 ULARGE_INTEGER newSize)
5553 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5555 if (newSize.u.LowPart == size.u.LowPart)
5558 if (newSize.u.LowPart < size.u.LowPart)
5560 SmallBlockChainStream_Shrink(This, newSize);
5564 SmallBlockChainStream_Enlarge(This, newSize);
5570 /******************************************************************************
5571 * SmallBlockChainStream_GetCount
5573 * Returns the number of small blocks that comprises this chain.
5574 * This is not the size of the stream as the last block may not be full!
5577 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5582 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5584 while(blockIndex != BLOCK_END_OF_CHAIN)
5588 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
5589 blockIndex, &blockIndex)))
5596 /******************************************************************************
5597 * SmallBlockChainStream_GetSize
5599 * Returns the size of this chain.
5601 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5603 StgProperty chainProperty;
5605 if(This->headOfStreamPlaceHolder != NULL)
5607 ULARGE_INTEGER result;
5608 result.u.HighPart = 0;
5610 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
5611 This->parentStorage->smallBlockSize;
5616 StorageImpl_ReadProperty(
5617 This->parentStorage,
5618 This->ownerPropertyIndex,
5621 return chainProperty.size;
5624 /******************************************************************************
5625 * StgCreateDocfile [OLE32.@]
5626 * Creates a new compound file storage object
5629 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5630 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5631 * reserved [ ?] unused?, usually 0
5632 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5635 * S_OK if the file was successfully created
5636 * some STG_E_ value if error
5638 * if pwcsName is NULL, create file with new unique name
5639 * the function can returns
5640 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5643 HRESULT WINAPI StgCreateDocfile(
5647 IStorage **ppstgOpen)
5649 StorageImpl* newStorage = 0;
5650 HANDLE hFile = INVALID_HANDLE_VALUE;
5651 HRESULT hr = STG_E_INVALIDFLAG;
5655 DWORD fileAttributes;
5656 WCHAR tempFileName[MAX_PATH];
5658 TRACE("(%s, %x, %d, %p)\n",
5659 debugstr_w(pwcsName), grfMode,
5660 reserved, ppstgOpen);
5663 return STG_E_INVALIDPOINTER;
5665 return STG_E_INVALIDPARAMETER;
5667 /* if no share mode given then DENY_NONE is the default */
5668 if (STGM_SHARE_MODE(grfMode) == 0)
5669 grfMode |= STGM_SHARE_DENY_NONE;
5671 if ( FAILED( validateSTGM(grfMode) ))
5674 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5675 switch(STGM_ACCESS_MODE(grfMode))
5678 case STGM_READWRITE:
5684 /* in direct mode, can only use SHARE_EXCLUSIVE */
5685 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5688 /* but in transacted mode, any share mode is valid */
5691 * Generate a unique name.
5695 WCHAR tempPath[MAX_PATH];
5696 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5698 memset(tempPath, 0, sizeof(tempPath));
5699 memset(tempFileName, 0, sizeof(tempFileName));
5701 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5704 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5705 pwcsName = tempFileName;
5708 hr = STG_E_INSUFFICIENTMEMORY;
5712 creationMode = TRUNCATE_EXISTING;
5716 creationMode = GetCreationModeFromSTGM(grfMode);
5720 * Interpret the STGM value grfMode
5722 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
5723 accessMode = GetAccessModeFromSTGM(grfMode);
5725 if (grfMode & STGM_DELETEONRELEASE)
5726 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5728 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5730 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
5731 FIXME("Storage share mode not implemented.\n");
5733 if (grfMode & STGM_TRANSACTED)
5734 FIXME("Transacted mode not implemented.\n");
5738 hFile = CreateFileW(pwcsName,
5746 if (hFile == INVALID_HANDLE_VALUE)
5748 if(GetLastError() == ERROR_FILE_EXISTS)
5749 hr = STG_E_FILEALREADYEXISTS;
5756 * Allocate and initialize the new IStorage32object.
5758 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5760 if (newStorage == 0)
5762 hr = STG_E_INSUFFICIENTMEMORY;
5766 hr = StorageImpl_Construct(
5777 HeapFree(GetProcessHeap(), 0, newStorage);
5782 * Get an "out" pointer for the caller.
5784 hr = StorageBaseImpl_QueryInterface(
5785 (IStorage*)newStorage,
5789 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
5794 /******************************************************************************
5795 * StgCreateStorageEx [OLE32.@]
5797 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5799 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5800 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5802 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5804 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5805 return STG_E_INVALIDPARAMETER;
5808 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5810 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5811 return STG_E_INVALIDPARAMETER;
5814 if (stgfmt == STGFMT_FILE)
5816 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5817 return STG_E_INVALIDPARAMETER;
5820 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5822 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5823 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5826 ERR("Invalid stgfmt argument\n");
5827 return STG_E_INVALIDPARAMETER;
5830 /******************************************************************************
5831 * StgCreatePropSetStg [OLE32.@]
5833 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5834 IPropertySetStorage **ppPropSetStg)
5838 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
5840 hr = STG_E_INVALIDPARAMETER;
5842 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5843 (void**)ppPropSetStg);
5847 /******************************************************************************
5848 * StgOpenStorageEx [OLE32.@]
5850 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5852 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5853 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5855 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5857 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5858 return STG_E_INVALIDPARAMETER;
5864 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5865 return STG_E_INVALIDPARAMETER;
5867 case STGFMT_STORAGE:
5870 case STGFMT_DOCFILE:
5871 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
5873 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5874 return STG_E_INVALIDPARAMETER;
5876 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5880 WARN("STGFMT_ANY assuming storage\n");
5884 return STG_E_INVALIDPARAMETER;
5887 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
5891 /******************************************************************************
5892 * StgOpenStorage [OLE32.@]
5894 HRESULT WINAPI StgOpenStorage(
5895 const OLECHAR *pwcsName,
5896 IStorage *pstgPriority,
5900 IStorage **ppstgOpen)
5902 StorageImpl* newStorage = 0;
5907 WCHAR fullname[MAX_PATH];
5909 TRACE("(%s, %p, %x, %p, %d, %p)\n",
5910 debugstr_w(pwcsName), pstgPriority, grfMode,
5911 snbExclude, reserved, ppstgOpen);
5915 hr = STG_E_INVALIDNAME;
5921 hr = STG_E_INVALIDPOINTER;
5927 hr = STG_E_INVALIDPARAMETER;
5931 if (grfMode & STGM_PRIORITY)
5933 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
5934 return STG_E_INVALIDFLAG;
5935 if (grfMode & STGM_DELETEONRELEASE)
5936 return STG_E_INVALIDFUNCTION;
5937 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
5938 return STG_E_INVALIDFLAG;
5939 grfMode &= ~0xf0; /* remove the existing sharing mode */
5940 grfMode |= STGM_SHARE_DENY_NONE;
5942 /* STGM_PRIORITY stops other IStorage objects on the same file from
5943 * committing until the STGM_PRIORITY IStorage is closed. it also
5944 * stops non-transacted mode StgOpenStorage calls with write access from
5945 * succeeding. obviously, both of these cannot be achieved through just
5946 * file share flags */
5947 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5951 * Validate the sharing mode
5953 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
5954 switch(STGM_SHARE_MODE(grfMode))
5956 case STGM_SHARE_EXCLUSIVE:
5957 case STGM_SHARE_DENY_WRITE:
5960 hr = STG_E_INVALIDFLAG;
5964 if ( FAILED( validateSTGM(grfMode) ) ||
5965 (grfMode&STGM_CREATE))
5967 hr = STG_E_INVALIDFLAG;
5971 /* shared reading requires transacted mode */
5972 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5973 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
5974 !(grfMode&STGM_TRANSACTED) )
5976 hr = STG_E_INVALIDFLAG;
5981 * Interpret the STGM value grfMode
5983 shareMode = GetShareModeFromSTGM(grfMode);
5984 accessMode = GetAccessModeFromSTGM(grfMode);
5988 hFile = CreateFileW( pwcsName,
5993 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5996 if (hFile==INVALID_HANDLE_VALUE)
5998 DWORD last_error = GetLastError();
6004 case ERROR_FILE_NOT_FOUND:
6005 hr = STG_E_FILENOTFOUND;
6008 case ERROR_PATH_NOT_FOUND:
6009 hr = STG_E_PATHNOTFOUND;
6012 case ERROR_ACCESS_DENIED:
6013 case ERROR_WRITE_PROTECT:
6014 hr = STG_E_ACCESSDENIED;
6017 case ERROR_SHARING_VIOLATION:
6018 hr = STG_E_SHAREVIOLATION;
6029 * Refuse to open the file if it's too small to be a structured storage file
6030 * FIXME: verify the file when reading instead of here
6032 if (GetFileSize(hFile, NULL) < 0x100)
6035 hr = STG_E_FILEALREADYEXISTS;
6040 * Allocate and initialize the new IStorage32object.
6042 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6044 if (newStorage == 0)
6046 hr = STG_E_INSUFFICIENTMEMORY;
6050 /* Initialize the storage */
6051 hr = StorageImpl_Construct(
6062 HeapFree(GetProcessHeap(), 0, newStorage);
6064 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6066 if(hr == STG_E_INVALIDHEADER)
6067 hr = STG_E_FILEALREADYEXISTS;
6071 /* prepare the file name string given in lieu of the root property name */
6072 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
6073 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
6074 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
6077 * Get an "out" pointer for the caller.
6079 hr = StorageBaseImpl_QueryInterface(
6080 (IStorage*)newStorage,
6085 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6089 /******************************************************************************
6090 * StgCreateDocfileOnILockBytes [OLE32.@]
6092 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6096 IStorage** ppstgOpen)
6098 StorageImpl* newStorage = 0;
6101 if ((ppstgOpen == 0) || (plkbyt == 0))
6102 return STG_E_INVALIDPOINTER;
6105 * Allocate and initialize the new IStorage object.
6107 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6109 if (newStorage == 0)
6110 return STG_E_INSUFFICIENTMEMORY;
6112 hr = StorageImpl_Construct(
6123 HeapFree(GetProcessHeap(), 0, newStorage);
6128 * Get an "out" pointer for the caller.
6130 hr = StorageBaseImpl_QueryInterface(
6131 (IStorage*)newStorage,
6138 /******************************************************************************
6139 * StgOpenStorageOnILockBytes [OLE32.@]
6141 HRESULT WINAPI StgOpenStorageOnILockBytes(
6143 IStorage *pstgPriority,
6147 IStorage **ppstgOpen)
6149 StorageImpl* newStorage = 0;
6152 if ((plkbyt == 0) || (ppstgOpen == 0))
6153 return STG_E_INVALIDPOINTER;
6155 if ( FAILED( validateSTGM(grfMode) ))
6156 return STG_E_INVALIDFLAG;
6161 * Allocate and initialize the new IStorage object.
6163 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6165 if (newStorage == 0)
6166 return STG_E_INSUFFICIENTMEMORY;
6168 hr = StorageImpl_Construct(
6179 HeapFree(GetProcessHeap(), 0, newStorage);
6184 * Get an "out" pointer for the caller.
6186 hr = StorageBaseImpl_QueryInterface(
6187 (IStorage*)newStorage,
6194 /******************************************************************************
6195 * StgSetTimes [ole32.@]
6196 * StgSetTimes [OLE32.@]
6200 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6201 FILETIME const *patime, FILETIME const *pmtime)
6203 IStorage *stg = NULL;
6206 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6208 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6212 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6213 IStorage_Release(stg);
6219 /******************************************************************************
6220 * StgIsStorageILockBytes [OLE32.@]
6222 * Determines if the ILockBytes contains a storage object.
6224 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6227 ULARGE_INTEGER offset;
6229 offset.u.HighPart = 0;
6230 offset.u.LowPart = 0;
6232 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6234 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6240 /******************************************************************************
6241 * WriteClassStg [OLE32.@]
6243 * This method will store the specified CLSID in the specified storage object
6245 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6250 return E_INVALIDARG;
6253 return STG_E_INVALIDPOINTER;
6255 hRes = IStorage_SetClass(pStg, rclsid);
6260 /***********************************************************************
6261 * ReadClassStg (OLE32.@)
6263 * This method reads the CLSID previously written to a storage object with
6264 * the WriteClassStg.
6267 * pstg [I] IStorage pointer
6268 * pclsid [O] Pointer to where the CLSID is written
6272 * Failure: HRESULT code.
6274 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6279 TRACE("(%p, %p)\n", pstg, pclsid);
6281 if(!pstg || !pclsid)
6282 return E_INVALIDARG;
6285 * read a STATSTG structure (contains the clsid) from the storage
6287 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
6290 *pclsid=pstatstg.clsid;
6295 /***********************************************************************
6296 * OleLoadFromStream (OLE32.@)
6298 * This function loads an object from stream
6300 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6304 LPPERSISTSTREAM xstm;
6306 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6308 res=ReadClassStm(pStm,&clsid);
6311 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6314 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6316 IUnknown_Release((IUnknown*)*ppvObj);
6319 res=IPersistStream_Load(xstm,pStm);
6320 IPersistStream_Release(xstm);
6321 /* FIXME: all refcounts ok at this point? I think they should be:
6324 * xstm : 0 (released)
6329 /***********************************************************************
6330 * OleSaveToStream (OLE32.@)
6332 * This function saves an object with the IPersistStream interface on it
6333 * to the specified stream.
6335 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6341 TRACE("(%p,%p)\n",pPStm,pStm);
6343 res=IPersistStream_GetClassID(pPStm,&clsid);
6345 if (SUCCEEDED(res)){
6347 res=WriteClassStm(pStm,&clsid);
6351 res=IPersistStream_Save(pPStm,pStm,TRUE);
6354 TRACE("Finished Save\n");
6358 /****************************************************************************
6359 * This method validate a STGM parameter that can contain the values below
6361 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6362 * The stgm values contained in 0xffff0000 are bitmasks.
6364 * STGM_DIRECT 0x00000000
6365 * STGM_TRANSACTED 0x00010000
6366 * STGM_SIMPLE 0x08000000
6368 * STGM_READ 0x00000000
6369 * STGM_WRITE 0x00000001
6370 * STGM_READWRITE 0x00000002
6372 * STGM_SHARE_DENY_NONE 0x00000040
6373 * STGM_SHARE_DENY_READ 0x00000030
6374 * STGM_SHARE_DENY_WRITE 0x00000020
6375 * STGM_SHARE_EXCLUSIVE 0x00000010
6377 * STGM_PRIORITY 0x00040000
6378 * STGM_DELETEONRELEASE 0x04000000
6380 * STGM_CREATE 0x00001000
6381 * STGM_CONVERT 0x00020000
6382 * STGM_FAILIFTHERE 0x00000000
6384 * STGM_NOSCRATCH 0x00100000
6385 * STGM_NOSNAPSHOT 0x00200000
6387 static HRESULT validateSTGM(DWORD stgm)
6389 DWORD access = STGM_ACCESS_MODE(stgm);
6390 DWORD share = STGM_SHARE_MODE(stgm);
6391 DWORD create = STGM_CREATE_MODE(stgm);
6393 if (stgm&~STGM_KNOWN_FLAGS)
6395 ERR("unknown flags %08x\n", stgm);
6403 case STGM_READWRITE:
6411 case STGM_SHARE_DENY_NONE:
6412 case STGM_SHARE_DENY_READ:
6413 case STGM_SHARE_DENY_WRITE:
6414 case STGM_SHARE_EXCLUSIVE:
6423 case STGM_FAILIFTHERE:
6430 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6432 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6436 * STGM_CREATE | STGM_CONVERT
6437 * if both are false, STGM_FAILIFTHERE is set to TRUE
6439 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6443 * STGM_NOSCRATCH requires STGM_TRANSACTED
6445 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6449 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6450 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6452 if ( (stgm & STGM_NOSNAPSHOT) &&
6453 (!(stgm & STGM_TRANSACTED) ||
6454 share == STGM_SHARE_EXCLUSIVE ||
6455 share == STGM_SHARE_DENY_WRITE) )
6461 /****************************************************************************
6462 * GetShareModeFromSTGM
6464 * This method will return a share mode flag from a STGM value.
6465 * The STGM value is assumed valid.
6467 static DWORD GetShareModeFromSTGM(DWORD stgm)
6469 switch (STGM_SHARE_MODE(stgm))
6471 case STGM_SHARE_DENY_NONE:
6472 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6473 case STGM_SHARE_DENY_READ:
6474 return FILE_SHARE_WRITE;
6475 case STGM_SHARE_DENY_WRITE:
6476 return FILE_SHARE_READ;
6477 case STGM_SHARE_EXCLUSIVE:
6480 ERR("Invalid share mode!\n");
6485 /****************************************************************************
6486 * GetAccessModeFromSTGM
6488 * This method will return an access mode flag from a STGM value.
6489 * The STGM value is assumed valid.
6491 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6493 switch (STGM_ACCESS_MODE(stgm))
6496 return GENERIC_READ;
6498 case STGM_READWRITE:
6499 return GENERIC_READ | GENERIC_WRITE;
6501 ERR("Invalid access mode!\n");
6506 /****************************************************************************
6507 * GetCreationModeFromSTGM
6509 * This method will return a creation mode flag from a STGM value.
6510 * The STGM value is assumed valid.
6512 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6514 switch(STGM_CREATE_MODE(stgm))
6517 return CREATE_ALWAYS;
6519 FIXME("STGM_CONVERT not implemented!\n");
6521 case STGM_FAILIFTHERE:
6524 ERR("Invalid create mode!\n");
6530 /*************************************************************************
6531 * OLECONVERT_LoadOLE10 [Internal]
6533 * Loads the OLE10 STREAM to memory
6536 * pOleStream [I] The OLESTREAM
6537 * pData [I] Data Structure for the OLESTREAM Data
6541 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6542 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
6545 * This function is used by OleConvertOLESTREAMToIStorage only.
6547 * Memory allocated for pData must be freed by the caller
6549 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6552 HRESULT hRes = S_OK;
6556 pData->pData = NULL;
6557 pData->pstrOleObjFileName = NULL;
6559 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6562 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6563 if(dwSize != sizeof(pData->dwOleID))
6565 hRes = CONVERT10_E_OLESTREAM_GET;
6567 else if(pData->dwOleID != OLESTREAM_ID)
6569 hRes = CONVERT10_E_OLESTREAM_FMT;
6580 /* Get the TypeID... more info needed for this field */
6581 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6582 if(dwSize != sizeof(pData->dwTypeID))
6584 hRes = CONVERT10_E_OLESTREAM_GET;
6589 if(pData->dwTypeID != 0)
6591 /* Get the length of the OleTypeName */
6592 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6593 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6595 hRes = CONVERT10_E_OLESTREAM_GET;
6600 if(pData->dwOleTypeNameLength > 0)
6602 /* Get the OleTypeName */
6603 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
6604 if(dwSize != pData->dwOleTypeNameLength)
6606 hRes = CONVERT10_E_OLESTREAM_GET;
6612 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6613 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6615 hRes = CONVERT10_E_OLESTREAM_GET;
6619 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6620 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6621 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6622 if(pData->pstrOleObjFileName)
6624 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
6625 if(dwSize != pData->dwOleObjFileNameLength)
6627 hRes = CONVERT10_E_OLESTREAM_GET;
6631 hRes = CONVERT10_E_OLESTREAM_GET;
6636 /* Get the Width of the Metafile */
6637 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6638 if(dwSize != sizeof(pData->dwMetaFileWidth))
6640 hRes = CONVERT10_E_OLESTREAM_GET;
6644 /* Get the Height of the Metafile */
6645 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6646 if(dwSize != sizeof(pData->dwMetaFileHeight))
6648 hRes = CONVERT10_E_OLESTREAM_GET;
6654 /* Get the Length of the Data */
6655 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6656 if(dwSize != sizeof(pData->dwDataLength))
6658 hRes = CONVERT10_E_OLESTREAM_GET;
6662 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
6664 if(!bStrem1) /* if it is a second OLE stream data */
6666 pData->dwDataLength -= 8;
6667 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
6668 if(dwSize != sizeof(pData->strUnknown))
6670 hRes = CONVERT10_E_OLESTREAM_GET;
6676 if(pData->dwDataLength > 0)
6678 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6680 /* Get Data (ex. IStorage, Metafile, or BMP) */
6683 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6684 if(dwSize != pData->dwDataLength)
6686 hRes = CONVERT10_E_OLESTREAM_GET;
6691 hRes = CONVERT10_E_OLESTREAM_GET;
6700 /*************************************************************************
6701 * OLECONVERT_SaveOLE10 [Internal]
6703 * Saves the OLE10 STREAM From memory
6706 * pData [I] Data Structure for the OLESTREAM Data
6707 * pOleStream [I] The OLESTREAM to save
6711 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6714 * This function is used by OleConvertIStorageToOLESTREAM only.
6717 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6720 HRESULT hRes = S_OK;
6724 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6725 if(dwSize != sizeof(pData->dwOleID))
6727 hRes = CONVERT10_E_OLESTREAM_PUT;
6732 /* Set the TypeID */
6733 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6734 if(dwSize != sizeof(pData->dwTypeID))
6736 hRes = CONVERT10_E_OLESTREAM_PUT;
6740 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6742 /* Set the Length of the OleTypeName */
6743 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6744 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6746 hRes = CONVERT10_E_OLESTREAM_PUT;
6751 if(pData->dwOleTypeNameLength > 0)
6753 /* Set the OleTypeName */
6754 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
6755 if(dwSize != pData->dwOleTypeNameLength)
6757 hRes = CONVERT10_E_OLESTREAM_PUT;
6764 /* Set the width of the Metafile */
6765 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6766 if(dwSize != sizeof(pData->dwMetaFileWidth))
6768 hRes = CONVERT10_E_OLESTREAM_PUT;
6774 /* Set the height of the Metafile */
6775 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6776 if(dwSize != sizeof(pData->dwMetaFileHeight))
6778 hRes = CONVERT10_E_OLESTREAM_PUT;
6784 /* Set the length of the Data */
6785 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6786 if(dwSize != sizeof(pData->dwDataLength))
6788 hRes = CONVERT10_E_OLESTREAM_PUT;
6794 if(pData->dwDataLength > 0)
6796 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6797 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6798 if(dwSize != pData->dwDataLength)
6800 hRes = CONVERT10_E_OLESTREAM_PUT;
6808 /*************************************************************************
6809 * OLECONVERT_GetOLE20FromOLE10[Internal]
6811 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6812 * opens it, and copies the content to the dest IStorage for
6813 * OleConvertOLESTREAMToIStorage
6817 * pDestStorage [I] The IStorage to copy the data to
6818 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6819 * nBufferLength [I] The size of the buffer
6828 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
6832 IStorage *pTempStorage;
6833 DWORD dwNumOfBytesWritten;
6834 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6835 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6837 /* Create a temp File */
6838 GetTempPathW(MAX_PATH, wstrTempDir);
6839 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6840 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6842 if(hFile != INVALID_HANDLE_VALUE)
6844 /* Write IStorage Data to File */
6845 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6848 /* Open and copy temp storage to the Dest Storage */
6849 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6852 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6853 StorageBaseImpl_Release(pTempStorage);
6855 DeleteFileW(wstrTempFile);
6860 /*************************************************************************
6861 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6863 * Saves the OLE10 STREAM From memory
6866 * pStorage [I] The Src IStorage to copy
6867 * pData [I] The Dest Memory to write to.
6870 * The size in bytes allocated for pData
6873 * Memory allocated for pData must be freed by the caller
6875 * Used by OleConvertIStorageToOLESTREAM only.
6878 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6882 DWORD nDataLength = 0;
6883 IStorage *pTempStorage;
6884 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6885 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6889 /* Create temp Storage */
6890 GetTempPathW(MAX_PATH, wstrTempDir);
6891 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6892 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6896 /* Copy Src Storage to the Temp Storage */
6897 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6898 StorageBaseImpl_Release(pTempStorage);
6900 /* Open Temp Storage as a file and copy to memory */
6901 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6902 if(hFile != INVALID_HANDLE_VALUE)
6904 nDataLength = GetFileSize(hFile, NULL);
6905 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6906 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6909 DeleteFileW(wstrTempFile);
6914 /*************************************************************************
6915 * OLECONVERT_CreateOleStream [Internal]
6917 * Creates the "\001OLE" stream in the IStorage if necessary.
6920 * pStorage [I] Dest storage to create the stream in
6926 * This function is used by OleConvertOLESTREAMToIStorage only.
6928 * This stream is still unknown, MS Word seems to have extra data
6929 * but since the data is stored in the OLESTREAM there should be
6930 * no need to recreate the stream. If the stream is manually
6931 * deleted it will create it with this default data.
6934 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6938 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6939 BYTE pOleStreamHeader [] =
6941 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6942 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6943 0x00, 0x00, 0x00, 0x00
6946 /* Create stream if not present */
6947 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6948 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6952 /* Write default Data */
6953 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6954 IStream_Release(pStream);
6958 /* write a string to a stream, preceded by its length */
6959 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6966 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6967 r = IStream_Write( stm, &len, sizeof(len), NULL);
6972 str = CoTaskMemAlloc( len );
6973 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6974 r = IStream_Write( stm, str, len, NULL);
6975 CoTaskMemFree( str );
6979 /* read a string preceded by its length from a stream */
6980 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6983 DWORD len, count = 0;
6987 r = IStream_Read( stm, &len, sizeof(len), &count );
6990 if( count != sizeof(len) )
6991 return E_OUTOFMEMORY;
6993 TRACE("%d bytes\n",len);
6995 str = CoTaskMemAlloc( len );
6997 return E_OUTOFMEMORY;
6999 r = IStream_Read( stm, str, len, &count );
7004 CoTaskMemFree( str );
7005 return E_OUTOFMEMORY;
7008 TRACE("Read string %s\n",debugstr_an(str,len));
7010 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7011 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7013 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7014 CoTaskMemFree( str );
7022 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7023 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7027 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7029 static const BYTE unknown1[12] =
7030 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7031 0xFF, 0xFF, 0xFF, 0xFF};
7032 static const BYTE unknown2[16] =
7033 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7034 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7036 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7037 debugstr_w(lpszUserType), debugstr_w(szClipName),
7038 debugstr_w(szProgIDName));
7040 /* Create a CompObj stream */
7041 r = IStorage_CreateStream(pstg, szwStreamName,
7042 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7046 /* Write CompObj Structure to stream */
7047 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7049 if( SUCCEEDED( r ) )
7050 r = WriteClassStm( pstm, clsid );
7052 if( SUCCEEDED( r ) )
7053 r = STREAM_WriteString( pstm, lpszUserType );
7054 if( SUCCEEDED( r ) )
7055 r = STREAM_WriteString( pstm, szClipName );
7056 if( SUCCEEDED( r ) )
7057 r = STREAM_WriteString( pstm, szProgIDName );
7058 if( SUCCEEDED( r ) )
7059 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7061 IStream_Release( pstm );
7066 /***********************************************************************
7067 * WriteFmtUserTypeStg (OLE32.@)
7069 HRESULT WINAPI WriteFmtUserTypeStg(
7070 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7073 WCHAR szwClipName[0x40];
7074 CLSID clsid = CLSID_NULL;
7075 LPWSTR wstrProgID = NULL;
7078 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7080 /* get the clipboard format name */
7081 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
7084 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7086 /* FIXME: There's room to save a CLSID and its ProgID, but
7087 the CLSID is not looked up in the registry and in all the
7088 tests I wrote it was CLSID_NULL. Where does it come from?
7091 /* get the real program ID. This may fail, but that's fine */
7092 ProgIDFromCLSID(&clsid, &wstrProgID);
7094 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7096 r = STORAGE_WriteCompObj( pstg, &clsid,
7097 lpszUserType, szwClipName, wstrProgID );
7099 CoTaskMemFree(wstrProgID);
7105 /******************************************************************************
7106 * ReadFmtUserTypeStg [OLE32.@]
7108 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7112 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7113 unsigned char unknown1[12];
7114 unsigned char unknown2[16];
7116 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7119 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7121 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7122 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7125 WARN("Failed to open stream r = %08x\n", r);
7129 /* read the various parts of the structure */
7130 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7131 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7133 r = ReadClassStm( stm, &clsid );
7137 r = STREAM_ReadString( stm, &szCLSIDName );
7141 r = STREAM_ReadString( stm, &szOleTypeName );
7145 r = STREAM_ReadString( stm, &szProgIDName );
7149 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7150 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7153 /* ok, success... now we just need to store what we found */
7155 *pcf = RegisterClipboardFormatW( szOleTypeName );
7156 CoTaskMemFree( szOleTypeName );
7158 if( lplpszUserType )
7159 *lplpszUserType = szCLSIDName;
7160 CoTaskMemFree( szProgIDName );
7163 IStream_Release( stm );
7169 /*************************************************************************
7170 * OLECONVERT_CreateCompObjStream [Internal]
7172 * Creates a "\001CompObj" is the destination IStorage if necessary.
7175 * pStorage [I] The dest IStorage to create the CompObj Stream
7177 * strOleTypeName [I] The ProgID
7181 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7184 * This function is used by OleConvertOLESTREAMToIStorage only.
7186 * The stream data is stored in the OLESTREAM and there should be
7187 * no need to recreate the stream. If the stream is manually
7188 * deleted it will attempt to create it by querying the registry.
7192 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7195 HRESULT hStorageRes, hRes = S_OK;
7196 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7197 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7198 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7200 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7201 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7203 /* Initialize the CompObj structure */
7204 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7205 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
7206 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
7209 /* Create a CompObj stream if it doesn't exist */
7210 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7211 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7212 if(hStorageRes == S_OK)
7214 /* copy the OleTypeName to the compobj struct */
7215 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7216 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7218 /* copy the OleTypeName to the compobj struct */
7219 /* Note: in the test made, these were Identical */
7220 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7221 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7224 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7225 bufferW, OLESTREAM_MAX_STR_LEN );
7226 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7232 /* Get the CLSID Default Name from the Registry */
7233 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7234 if(hErr == ERROR_SUCCESS)
7236 char strTemp[OLESTREAM_MAX_STR_LEN];
7237 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7238 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7239 if(hErr == ERROR_SUCCESS)
7241 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7247 /* Write CompObj Structure to stream */
7248 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7250 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7252 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7253 if(IStorageCompObj.dwCLSIDNameLength > 0)
7255 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7257 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7258 if(IStorageCompObj.dwOleTypeNameLength > 0)
7260 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7262 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7263 if(IStorageCompObj.dwProgIDNameLength > 0)
7265 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7267 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7268 IStream_Release(pStream);
7274 /*************************************************************************
7275 * OLECONVERT_CreateOlePresStream[Internal]
7277 * Creates the "\002OlePres000" Stream with the Metafile data
7280 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7281 * dwExtentX [I] Width of the Metafile
7282 * dwExtentY [I] Height of the Metafile
7283 * pData [I] Metafile data
7284 * dwDataLength [I] Size of the Metafile data
7288 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7291 * This function is used by OleConvertOLESTREAMToIStorage only.
7294 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7298 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7299 BYTE pOlePresStreamHeader [] =
7301 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7302 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7303 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7304 0x00, 0x00, 0x00, 0x00
7307 BYTE pOlePresStreamHeaderEmpty [] =
7309 0x00, 0x00, 0x00, 0x00,
7310 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7311 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7312 0x00, 0x00, 0x00, 0x00
7315 /* Create the OlePres000 Stream */
7316 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7317 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7322 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7324 memset(&OlePres, 0, sizeof(OlePres));
7325 /* Do we have any metafile data to save */
7326 if(dwDataLength > 0)
7328 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7329 nHeaderSize = sizeof(pOlePresStreamHeader);
7333 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7334 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7336 /* Set width and height of the metafile */
7337 OlePres.dwExtentX = dwExtentX;
7338 OlePres.dwExtentY = -dwExtentY;
7340 /* Set Data and Length */
7341 if(dwDataLength > sizeof(METAFILEPICT16))
7343 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7344 OlePres.pData = &(pData[8]);
7346 /* Save OlePres000 Data to Stream */
7347 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7348 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7349 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7350 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7351 if(OlePres.dwSize > 0)
7353 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7355 IStream_Release(pStream);
7359 /*************************************************************************
7360 * OLECONVERT_CreateOle10NativeStream [Internal]
7362 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7365 * pStorage [I] Dest storage to create the stream in
7366 * pData [I] Ole10 Native Data (ex. bmp)
7367 * dwDataLength [I] Size of the Ole10 Native Data
7373 * This function is used by OleConvertOLESTREAMToIStorage only.
7375 * Might need to verify the data and return appropriate error message
7378 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
7382 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7384 /* Create the Ole10Native Stream */
7385 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7386 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7390 /* Write info to stream */
7391 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7392 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7393 IStream_Release(pStream);
7398 /*************************************************************************
7399 * OLECONVERT_GetOLE10ProgID [Internal]
7401 * Finds the ProgID (or OleTypeID) from the IStorage
7404 * pStorage [I] The Src IStorage to get the ProgID
7405 * strProgID [I] the ProgID string to get
7406 * dwSize [I] the size of the string
7410 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7413 * This function is used by OleConvertIStorageToOLESTREAM only.
7417 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7421 LARGE_INTEGER iSeekPos;
7422 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7423 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7425 /* Open the CompObj Stream */
7426 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7427 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7431 /*Get the OleType from the CompObj Stream */
7432 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7433 iSeekPos.u.HighPart = 0;
7435 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7436 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7437 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7438 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7439 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7440 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7441 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7443 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7446 IStream_Read(pStream, strProgID, *dwSize, NULL);
7448 IStream_Release(pStream);
7453 LPOLESTR wstrProgID;
7455 /* Get the OleType from the registry */
7456 REFCLSID clsid = &(stat.clsid);
7457 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7458 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7461 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7468 /*************************************************************************
7469 * OLECONVERT_GetOle10PresData [Internal]
7471 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7474 * pStorage [I] Src IStroage
7475 * pOleStream [I] Dest OleStream Mem Struct
7481 * This function is used by OleConvertIStorageToOLESTREAM only.
7483 * Memory allocated for pData must be freed by the caller
7487 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7492 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7494 /* Initialize Default data for OLESTREAM */
7495 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7496 pOleStreamData[0].dwTypeID = 2;
7497 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7498 pOleStreamData[1].dwTypeID = 0;
7499 pOleStreamData[0].dwMetaFileWidth = 0;
7500 pOleStreamData[0].dwMetaFileHeight = 0;
7501 pOleStreamData[0].pData = NULL;
7502 pOleStreamData[1].pData = NULL;
7504 /* Open Ole10Native Stream */
7505 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7506 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7510 /* Read Size and Data */
7511 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7512 if(pOleStreamData->dwDataLength > 0)
7514 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7515 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7517 IStream_Release(pStream);
7523 /*************************************************************************
7524 * OLECONVERT_GetOle20PresData[Internal]
7526 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7529 * pStorage [I] Src IStroage
7530 * pOleStreamData [I] Dest OleStream Mem Struct
7536 * This function is used by OleConvertIStorageToOLESTREAM only.
7538 * Memory allocated for pData must be freed by the caller
7540 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7544 OLECONVERT_ISTORAGE_OLEPRES olePress;
7545 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7547 /* Initialize Default data for OLESTREAM */
7548 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7549 pOleStreamData[0].dwTypeID = 2;
7550 pOleStreamData[0].dwMetaFileWidth = 0;
7551 pOleStreamData[0].dwMetaFileHeight = 0;
7552 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7553 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7554 pOleStreamData[1].dwTypeID = 0;
7555 pOleStreamData[1].dwOleTypeNameLength = 0;
7556 pOleStreamData[1].strOleTypeName[0] = 0;
7557 pOleStreamData[1].dwMetaFileWidth = 0;
7558 pOleStreamData[1].dwMetaFileHeight = 0;
7559 pOleStreamData[1].pData = NULL;
7560 pOleStreamData[1].dwDataLength = 0;
7563 /* Open OlePress000 stream */
7564 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7565 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7568 LARGE_INTEGER iSeekPos;
7569 METAFILEPICT16 MetaFilePict;
7570 static const char strMetafilePictName[] = "METAFILEPICT";
7572 /* Set the TypeID for a Metafile */
7573 pOleStreamData[1].dwTypeID = 5;
7575 /* Set the OleTypeName to Metafile */
7576 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7577 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7579 iSeekPos.u.HighPart = 0;
7580 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7582 /* Get Presentation Data */
7583 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7584 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7585 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7586 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7588 /*Set width and Height */
7589 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7590 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7591 if(olePress.dwSize > 0)
7594 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7596 /* Set MetaFilePict struct */
7597 MetaFilePict.mm = 8;
7598 MetaFilePict.xExt = olePress.dwExtentX;
7599 MetaFilePict.yExt = olePress.dwExtentY;
7600 MetaFilePict.hMF = 0;
7602 /* Get Metafile Data */
7603 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7604 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7605 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7607 IStream_Release(pStream);
7611 /*************************************************************************
7612 * OleConvertOLESTREAMToIStorage [OLE32.@]
7617 * DVTARGETDEVICE parameter is not handled
7618 * Still unsure of some mem fields for OLE 10 Stream
7619 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7620 * and "\001OLE" streams
7623 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7624 LPOLESTREAM pOleStream,
7626 const DVTARGETDEVICE* ptd)
7630 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7632 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
7634 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7638 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7641 if(pstg == NULL || pOleStream == NULL)
7643 hRes = E_INVALIDARG;
7648 /* Load the OLESTREAM to Memory */
7649 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7654 /* Load the OLESTREAM to Memory (part 2)*/
7655 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7661 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7663 /* Do we have the IStorage Data in the OLESTREAM */
7664 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7666 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7667 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7671 /* It must be an original OLE 1.0 source */
7672 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7677 /* It must be an original OLE 1.0 source */
7678 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7681 /* Create CompObj Stream if necessary */
7682 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7685 /*Create the Ole Stream if necessary */
7686 OLECONVERT_CreateOleStream(pstg);
7691 /* Free allocated memory */
7692 for(i=0; i < 2; i++)
7694 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7695 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7696 pOleStreamData[i].pstrOleObjFileName = NULL;
7701 /*************************************************************************
7702 * OleConvertIStorageToOLESTREAM [OLE32.@]
7709 * Still unsure of some mem fields for OLE 10 Stream
7710 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7711 * and "\001OLE" streams.
7714 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7716 LPOLESTREAM pOleStream)
7719 HRESULT hRes = S_OK;
7721 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7722 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7724 TRACE("%p %p\n", pstg, pOleStream);
7726 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7728 if(pstg == NULL || pOleStream == NULL)
7730 hRes = E_INVALIDARG;
7734 /* Get the ProgID */
7735 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7736 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7740 /* Was it originally Ole10 */
7741 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7744 IStream_Release(pStream);
7745 /* Get Presentation Data for Ole10Native */
7746 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7750 /* Get Presentation Data (OLE20) */
7751 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7754 /* Save OLESTREAM */
7755 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7758 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7763 /* Free allocated memory */
7764 for(i=0; i < 2; i++)
7766 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7772 /***********************************************************************
7773 * GetConvertStg (OLE32.@)
7775 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7776 FIXME("unimplemented stub!\n");
7780 /******************************************************************************
7781 * StgIsStorageFile [OLE32.@]
7782 * Verify if the file contains a storage object
7788 * S_OK if file has magic bytes as a storage object
7789 * S_FALSE if file is not storage
7792 StgIsStorageFile(LPCOLESTR fn)
7798 TRACE("%s\n", debugstr_w(fn));
7799 hf = CreateFileW(fn, GENERIC_READ,
7800 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7801 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7803 if (hf == INVALID_HANDLE_VALUE)
7804 return STG_E_FILENOTFOUND;
7806 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7808 WARN(" unable to read file\n");
7815 if (bytes_read != 8) {
7816 WARN(" too short\n");
7820 if (!memcmp(magic,STORAGE_magic,8)) {
7825 WARN(" -> Invalid header.\n");
7829 /***********************************************************************
7830 * WriteClassStm (OLE32.@)
7832 * Writes a CLSID to a stream.
7835 * pStm [I] Stream to write to.
7836 * rclsid [I] CLSID to write.
7840 * Failure: HRESULT code.
7842 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
7844 TRACE("(%p,%p)\n",pStm,rclsid);
7846 if (!pStm || !rclsid)
7847 return E_INVALIDARG;
7849 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
7852 /***********************************************************************
7853 * ReadClassStm (OLE32.@)
7855 * Reads a CLSID from a stream.
7858 * pStm [I] Stream to read from.
7859 * rclsid [O] CLSID to read.
7863 * Failure: HRESULT code.
7865 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
7870 TRACE("(%p,%p)\n",pStm,pclsid);
7872 if (!pStm || !pclsid)
7873 return E_INVALIDARG;
7875 /* clear the output args */
7876 *pclsid = CLSID_NULL;
7878 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
7883 if (nbByte != sizeof(CLSID))
7884 return STG_E_READFAULT;