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 StorageBaseImpl *parentStorage,
164 ULONG foundPropertyIndexToDelete,
165 StgProperty propertyToDelete);
167 static HRESULT deleteStreamProperty(
168 StorageBaseImpl *parentStorage,
169 ULONG foundPropertyIndexToDelete,
170 StgProperty propertyToDelete);
172 static HRESULT removeFromTree(
174 ULONG parentStorageIndex,
177 /***********************************************************************
178 * Declaration of the functions used to manipulate StgProperty
181 static HRESULT createDirEntry(
182 StorageImpl *storage,
183 const StgProperty *newData,
186 static HRESULT destroyDirEntry(
187 StorageImpl *storage,
190 static void updatePropertyChain(
191 StorageBaseImpl *storage,
192 ULONG newPropertyIndex,
193 StgProperty newProperty);
195 static LONG propertyNameCmp(
196 const OLECHAR *newProperty,
197 const OLECHAR *currentProperty);
199 static ULONG findElement(
200 StorageImpl *storage,
205 static HRESULT findTreeParent(
206 StorageImpl *storage,
208 const OLECHAR *childName,
209 StgProperty *parentData,
213 /***********************************************************************
214 * Declaration of miscellaneous functions...
216 static HRESULT validateSTGM(DWORD stgmValue);
218 static DWORD GetShareModeFromSTGM(DWORD stgm);
219 static DWORD GetAccessModeFromSTGM(DWORD stgm);
220 static DWORD GetCreationModeFromSTGM(DWORD stgm);
222 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
225 /****************************************************************************
226 * IEnumSTATSTGImpl definitions.
228 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
229 * This class allows iterating through the content of a storage and to find
230 * specific items inside it.
232 struct IEnumSTATSTGImpl
234 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
235 * since we want to cast this in an IEnumSTATSTG pointer */
237 LONG ref; /* Reference count */
238 StorageImpl* parentStorage; /* Reference to the parent storage */
239 ULONG firstPropertyNode; /* Index of the root of the storage to enumerate */
242 * The current implementation of the IEnumSTATSTGImpl class uses a stack
243 * to walk the property sets to get the content of a storage. This stack
244 * is implemented by the following 3 data members
250 #define ENUMSTATSGT_SIZE_INCREMENT 10
254 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageImpl* This, ULONG firstPropertyNode);
255 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
256 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl* This, ULONG nodeToPush);
257 static ULONG IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl* This, BOOL remove);
259 /************************************************************************
263 static ULONG BLOCK_GetBigBlockOffset(ULONG index)
265 if (index == 0xffffffff)
270 return index * BIG_BLOCK_SIZE;
273 /************************************************************************
274 ** Storage32BaseImpl implementation
276 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
277 ULARGE_INTEGER offset,
282 return BIGBLOCKFILE_ReadAt(This->bigBlockFile,offset,buffer,size,bytesRead);
285 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
286 ULARGE_INTEGER offset,
291 return BIGBLOCKFILE_WriteAt(This->bigBlockFile,offset,buffer,size,bytesWritten);
294 /************************************************************************
295 * Storage32BaseImpl_QueryInterface (IUnknown)
297 * This method implements the common QueryInterface for all IStorage32
298 * implementations contained in this file.
300 * See Windows documentation for more details on IUnknown methods.
302 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
307 StorageBaseImpl *This = (StorageBaseImpl *)iface;
309 if ( (This==0) || (ppvObject==0) )
314 if (IsEqualGUID(&IID_IUnknown, riid) ||
315 IsEqualGUID(&IID_IStorage, riid))
319 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
321 *ppvObject = &This->pssVtbl;
325 return E_NOINTERFACE;
327 IStorage_AddRef(iface);
332 /************************************************************************
333 * Storage32BaseImpl_AddRef (IUnknown)
335 * This method implements the common AddRef for all IStorage32
336 * implementations contained in this file.
338 * See Windows documentation for more details on IUnknown methods.
340 static ULONG WINAPI StorageBaseImpl_AddRef(
343 StorageBaseImpl *This = (StorageBaseImpl *)iface;
344 ULONG ref = InterlockedIncrement(&This->ref);
346 TRACE("(%p) AddRef to %d\n", This, ref);
351 /************************************************************************
352 * Storage32BaseImpl_Release (IUnknown)
354 * This method implements the common Release for all IStorage32
355 * implementations contained in this file.
357 * See Windows documentation for more details on IUnknown methods.
359 static ULONG WINAPI StorageBaseImpl_Release(
362 StorageBaseImpl *This = (StorageBaseImpl *)iface;
364 ULONG ref = InterlockedDecrement(&This->ref);
366 TRACE("(%p) ReleaseRef to %d\n", This, ref);
371 * Since we are using a system of base-classes, we want to call the
372 * destructor of the appropriate derived class. To do this, we are
373 * using virtual functions to implement the destructor.
375 This->v_destructor(This);
381 /************************************************************************
382 * Storage32BaseImpl_OpenStream (IStorage)
384 * This method will open the specified stream object from the current storage.
386 * See Windows documentation for more details on IStorage methods.
388 static HRESULT WINAPI StorageBaseImpl_OpenStream(
390 const OLECHAR* pwcsName, /* [string][in] */
391 void* reserved1, /* [unique][in] */
392 DWORD grfMode, /* [in] */
393 DWORD reserved2, /* [in] */
394 IStream** ppstm) /* [out] */
396 StorageBaseImpl *This = (StorageBaseImpl *)iface;
397 StgStreamImpl* newStream;
398 StgProperty currentProperty;
399 ULONG foundPropertyIndex;
400 HRESULT res = STG_E_UNKNOWN;
402 TRACE("(%p, %s, %p, %x, %d, %p)\n",
403 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
405 if ( (pwcsName==NULL) || (ppstm==0) )
413 if ( FAILED( validateSTGM(grfMode) ) ||
414 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
416 res = STG_E_INVALIDFLAG;
423 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
425 res = STG_E_INVALIDFUNCTION;
430 * Check that we're compatible with the parent's storage mode, but
431 * only if we are not in transacted mode
433 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
434 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
436 res = STG_E_ACCESSDENIED;
442 * Search for the element with the given name
444 foundPropertyIndex = findElement(
445 This->ancestorStorage,
446 This->rootPropertySetIndex,
451 * If it was found, construct the stream object and return a pointer to it.
453 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
454 (currentProperty.propertyType==PROPTYPE_STREAM) )
456 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
460 newStream->grfMode = grfMode;
461 *ppstm = (IStream*)newStream;
463 IStream_AddRef(*ppstm);
473 res = STG_E_FILENOTFOUND;
477 TRACE("<-- IStream %p\n", *ppstm);
478 TRACE("<-- %08x\n", res);
482 /************************************************************************
483 * Storage32BaseImpl_OpenStorage (IStorage)
485 * This method will open a new storage object from the current storage.
487 * See Windows documentation for more details on IStorage methods.
489 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
491 const OLECHAR* pwcsName, /* [string][unique][in] */
492 IStorage* pstgPriority, /* [unique][in] */
493 DWORD grfMode, /* [in] */
494 SNB snbExclude, /* [unique][in] */
495 DWORD reserved, /* [in] */
496 IStorage** ppstg) /* [out] */
498 StorageBaseImpl *This = (StorageBaseImpl *)iface;
499 StorageInternalImpl* newStorage;
500 StgProperty currentProperty;
501 ULONG foundPropertyIndex;
502 HRESULT res = STG_E_UNKNOWN;
504 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
505 iface, debugstr_w(pwcsName), pstgPriority,
506 grfMode, snbExclude, reserved, ppstg);
508 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
515 if (snbExclude != NULL)
517 res = STG_E_INVALIDPARAMETER;
521 if ( FAILED( validateSTGM(grfMode) ))
523 res = STG_E_INVALIDFLAG;
530 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
531 (grfMode & STGM_DELETEONRELEASE) ||
532 (grfMode & STGM_PRIORITY) )
534 res = STG_E_INVALIDFUNCTION;
539 * Check that we're compatible with the parent's storage mode,
540 * but only if we are not transacted
542 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
543 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
545 res = STG_E_ACCESSDENIED;
552 foundPropertyIndex = findElement(
553 This->ancestorStorage,
554 This->rootPropertySetIndex,
558 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
559 (currentProperty.propertyType==PROPTYPE_STORAGE) )
561 newStorage = StorageInternalImpl_Construct(
562 This->ancestorStorage,
568 *ppstg = (IStorage*)newStorage;
570 StorageBaseImpl_AddRef(*ppstg);
576 res = STG_E_INSUFFICIENTMEMORY;
580 res = STG_E_FILENOTFOUND;
583 TRACE("<-- %08x\n", res);
587 /************************************************************************
588 * Storage32BaseImpl_EnumElements (IStorage)
590 * This method will create an enumerator object that can be used to
591 * retrieve information about all the properties in the storage object.
593 * See Windows documentation for more details on IStorage methods.
595 static HRESULT WINAPI StorageBaseImpl_EnumElements(
597 DWORD reserved1, /* [in] */
598 void* reserved2, /* [size_is][unique][in] */
599 DWORD reserved3, /* [in] */
600 IEnumSTATSTG** ppenum) /* [out] */
602 StorageBaseImpl *This = (StorageBaseImpl *)iface;
603 IEnumSTATSTGImpl* newEnum;
605 TRACE("(%p, %d, %p, %d, %p)\n",
606 iface, reserved1, reserved2, reserved3, ppenum);
608 if ( (This==0) || (ppenum==0))
611 newEnum = IEnumSTATSTGImpl_Construct(
612 This->ancestorStorage,
613 This->rootPropertySetIndex);
617 *ppenum = (IEnumSTATSTG*)newEnum;
619 IEnumSTATSTG_AddRef(*ppenum);
624 return E_OUTOFMEMORY;
627 /************************************************************************
628 * Storage32BaseImpl_Stat (IStorage)
630 * This method will retrieve information about this storage object.
632 * See Windows documentation for more details on IStorage methods.
634 static HRESULT WINAPI StorageBaseImpl_Stat(
636 STATSTG* pstatstg, /* [out] */
637 DWORD grfStatFlag) /* [in] */
639 StorageBaseImpl *This = (StorageBaseImpl *)iface;
640 StgProperty curProperty;
642 HRESULT res = STG_E_UNKNOWN;
644 TRACE("(%p, %p, %x)\n",
645 iface, pstatstg, grfStatFlag);
647 if ( (This==0) || (pstatstg==0))
653 readSuccessful = StorageImpl_ReadProperty(
654 This->ancestorStorage,
655 This->rootPropertySetIndex,
660 StorageUtl_CopyPropertyToSTATSTG(
665 pstatstg->grfMode = This->openFlags;
666 pstatstg->grfStateBits = This->stateBits;
677 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);
679 TRACE("<-- %08x\n", res);
683 /************************************************************************
684 * Storage32BaseImpl_RenameElement (IStorage)
686 * This method will rename the specified element.
688 * See Windows documentation for more details on IStorage methods.
690 * Implementation notes: The method used to rename consists of creating a clone
691 * of the deleted StgProperty object setting it with the new name and to
692 * perform a DestroyElement of the old StgProperty.
694 static HRESULT WINAPI StorageBaseImpl_RenameElement(
696 const OLECHAR* pwcsOldName, /* [in] */
697 const OLECHAR* pwcsNewName) /* [in] */
699 StorageBaseImpl *This = (StorageBaseImpl *)iface;
700 StgProperty currentProperty;
701 ULONG foundPropertyIndex;
703 TRACE("(%p, %s, %s)\n",
704 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
706 foundPropertyIndex = findElement(This->ancestorStorage,
707 This->rootPropertySetIndex,
711 if (foundPropertyIndex != PROPERTY_NULL)
714 * There is already a property with the new name
716 return STG_E_FILEALREADYEXISTS;
720 * Search for the old element name
722 foundPropertyIndex = findElement(This->ancestorStorage,
723 This->rootPropertySetIndex,
727 if (foundPropertyIndex != PROPERTY_NULL)
729 StgProperty renamedProperty;
730 ULONG renamedPropertyIndex;
733 * Setup a new property for the renamed property
735 renamedProperty.sizeOfNameString =
736 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
738 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
739 return STG_E_INVALIDNAME;
741 strcpyW(renamedProperty.name, pwcsNewName);
743 renamedProperty.propertyType = currentProperty.propertyType;
744 renamedProperty.startingBlock = currentProperty.startingBlock;
745 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
746 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
748 renamedProperty.leftChild = PROPERTY_NULL;
749 renamedProperty.rightChild = PROPERTY_NULL;
752 * Bring the dirProperty link in case it is a storage and in which
753 * case the renamed storage elements don't require to be reorganized.
755 renamedProperty.dirProperty = currentProperty.dirProperty;
757 /* call CoFileTime to get the current time
758 renamedProperty.ctime
759 renamedProperty.mtime
760 renamedProperty.propertyUniqueID
764 * Save the new property into a new property spot
766 createDirEntry(This->ancestorStorage, &renamedProperty, &renamedPropertyIndex);
769 * Find a spot in the property chain for our newly created property.
773 renamedPropertyIndex,
777 * At this point the renamed property has been inserted in the tree,
778 * now, before Destroying the old property we must zero its dirProperty
779 * otherwise the DestroyProperty below will zap it all and we do not want
781 * Also, we fake that the old property is a storage so the DestroyProperty
782 * will not do a SetSize(0) on the stream data.
784 * This means that we need to tweak the StgProperty if it is a stream or a
787 StorageImpl_ReadProperty(This->ancestorStorage,
791 currentProperty.dirProperty = PROPERTY_NULL;
792 currentProperty.propertyType = PROPTYPE_STORAGE;
793 StorageImpl_WriteProperty(
794 This->ancestorStorage,
799 * Invoke Destroy to get rid of the ole property and automatically redo
800 * the linking of its previous and next members...
802 IStorage_DestroyElement(iface, pwcsOldName);
808 * There is no property with the old name
810 return STG_E_FILENOTFOUND;
816 /************************************************************************
817 * Storage32BaseImpl_CreateStream (IStorage)
819 * This method will create a stream object within this storage
821 * See Windows documentation for more details on IStorage methods.
823 static HRESULT WINAPI StorageBaseImpl_CreateStream(
825 const OLECHAR* pwcsName, /* [string][in] */
826 DWORD grfMode, /* [in] */
827 DWORD reserved1, /* [in] */
828 DWORD reserved2, /* [in] */
829 IStream** ppstm) /* [out] */
831 StorageBaseImpl *This = (StorageBaseImpl *)iface;
832 StgStreamImpl* newStream;
833 StgProperty currentProperty, newStreamProperty;
834 ULONG foundPropertyIndex, newPropertyIndex;
836 TRACE("(%p, %s, %x, %d, %d, %p)\n",
837 iface, debugstr_w(pwcsName), grfMode,
838 reserved1, reserved2, ppstm);
841 return STG_E_INVALIDPOINTER;
844 return STG_E_INVALIDNAME;
846 if (reserved1 || reserved2)
847 return STG_E_INVALIDPARAMETER;
849 if ( FAILED( validateSTGM(grfMode) ))
850 return STG_E_INVALIDFLAG;
852 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
853 return STG_E_INVALIDFLAG;
858 if ((grfMode & STGM_DELETEONRELEASE) ||
859 (grfMode & STGM_TRANSACTED))
860 return STG_E_INVALIDFUNCTION;
862 /* Can't create a stream on read-only storage */
863 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
864 return STG_E_ACCESSDENIED;
867 * Check that we're compatible with the parent's storage mode
868 * if not in transacted mode
870 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
871 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
872 return STG_E_ACCESSDENIED;
875 if(This->ancestorStorage->base.openFlags & STGM_SIMPLE)
876 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
880 foundPropertyIndex = findElement(This->ancestorStorage,
881 This->rootPropertySetIndex,
885 if (foundPropertyIndex != PROPERTY_NULL)
888 * An element with this name already exists
890 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
894 LIST_FOR_EACH_ENTRY(strm, &This->strmHead, StgStreamImpl, StrmListEntry)
896 if (strm->ownerProperty == foundPropertyIndex)
898 TRACE("Stream deleted %p\n", strm);
899 strm->parentStorage = NULL;
900 list_remove(&strm->StrmListEntry);
903 IStorage_DestroyElement(iface, pwcsName);
906 return STG_E_FILEALREADYEXISTS;
908 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
910 WARN("read-only storage\n");
911 return STG_E_ACCESSDENIED;
915 * memset the empty property
917 memset(&newStreamProperty, 0, sizeof(StgProperty));
919 newStreamProperty.sizeOfNameString =
920 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
922 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
923 return STG_E_INVALIDNAME;
925 strcpyW(newStreamProperty.name, pwcsName);
927 newStreamProperty.propertyType = PROPTYPE_STREAM;
928 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
929 newStreamProperty.size.u.LowPart = 0;
930 newStreamProperty.size.u.HighPart = 0;
932 newStreamProperty.leftChild = PROPERTY_NULL;
933 newStreamProperty.rightChild = PROPERTY_NULL;
934 newStreamProperty.dirProperty = PROPERTY_NULL;
936 /* call CoFileTime to get the current time
937 newStreamProperty.ctime
938 newStreamProperty.mtime
941 /* newStreamProperty.propertyUniqueID */
944 * Save the new property into a new property spot
946 createDirEntry(This->ancestorStorage, &newStreamProperty, &newPropertyIndex);
949 * Find a spot in the property chain for our newly created property.
957 * Open the stream to return it.
959 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
963 *ppstm = (IStream*)newStream;
965 IStream_AddRef(*ppstm);
969 return STG_E_INSUFFICIENTMEMORY;
975 /************************************************************************
976 * Storage32BaseImpl_SetClass (IStorage)
978 * This method will write the specified CLSID in the property of this
981 * See Windows documentation for more details on IStorage methods.
983 static HRESULT WINAPI StorageBaseImpl_SetClass(
985 REFCLSID clsid) /* [in] */
987 StorageBaseImpl *This = (StorageBaseImpl *)iface;
988 HRESULT hRes = E_FAIL;
989 StgProperty curProperty;
992 TRACE("(%p, %p)\n", iface, clsid);
994 success = StorageImpl_ReadProperty(This->ancestorStorage,
995 This->rootPropertySetIndex,
999 curProperty.propertyUniqueID = *clsid;
1001 success = StorageImpl_WriteProperty(This->ancestorStorage,
1002 This->rootPropertySetIndex,
1011 /************************************************************************
1012 ** Storage32Impl implementation
1015 /************************************************************************
1016 * Storage32BaseImpl_CreateStorage (IStorage)
1018 * This method will create the storage object within the provided storage.
1020 * See Windows documentation for more details on IStorage methods.
1022 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1024 const OLECHAR *pwcsName, /* [string][in] */
1025 DWORD grfMode, /* [in] */
1026 DWORD reserved1, /* [in] */
1027 DWORD reserved2, /* [in] */
1028 IStorage **ppstg) /* [out] */
1030 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1032 StgProperty currentProperty;
1033 StgProperty newProperty;
1034 ULONG foundPropertyIndex;
1035 ULONG newPropertyIndex;
1038 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1039 iface, debugstr_w(pwcsName), grfMode,
1040 reserved1, reserved2, ppstg);
1043 return STG_E_INVALIDPOINTER;
1046 return STG_E_INVALIDNAME;
1050 if ( FAILED( validateSTGM(grfMode) ) ||
1051 (grfMode & STGM_DELETEONRELEASE) )
1053 WARN("bad grfMode: 0x%x\n", grfMode);
1054 return STG_E_INVALIDFLAG;
1058 * Check that we're compatible with the parent's storage mode
1060 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1062 WARN("access denied\n");
1063 return STG_E_ACCESSDENIED;
1066 foundPropertyIndex = findElement(This->ancestorStorage,
1067 This->rootPropertySetIndex,
1071 if (foundPropertyIndex != PROPERTY_NULL)
1074 * An element with this name already exists
1076 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1077 STGM_ACCESS_MODE(This->openFlags) != STGM_READ)
1079 hr = IStorage_DestroyElement(iface, pwcsName);
1085 WARN("file already exists\n");
1086 return STG_E_FILEALREADYEXISTS;
1089 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1091 WARN("read-only storage\n");
1092 return STG_E_ACCESSDENIED;
1096 * memset the empty property
1098 memset(&newProperty, 0, sizeof(StgProperty));
1100 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1102 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1104 FIXME("name too long\n");
1105 return STG_E_INVALIDNAME;
1108 strcpyW(newProperty.name, pwcsName);
1110 newProperty.propertyType = PROPTYPE_STORAGE;
1111 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1112 newProperty.size.u.LowPart = 0;
1113 newProperty.size.u.HighPart = 0;
1115 newProperty.leftChild = PROPERTY_NULL;
1116 newProperty.rightChild = PROPERTY_NULL;
1117 newProperty.dirProperty = PROPERTY_NULL;
1119 /* call CoFileTime to get the current time
1124 /* newStorageProperty.propertyUniqueID */
1127 * Save the new property into a new property spot
1129 createDirEntry(This->ancestorStorage, &newProperty, &newPropertyIndex);
1132 * Find a spot in the property chain for our newly created property.
1134 updatePropertyChain(
1140 * Open it to get a pointer to return.
1142 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1144 if( (hr != S_OK) || (*ppstg == NULL))
1154 /***************************************************************************
1158 * Reserve a directory entry in the file and initialize it.
1160 static HRESULT createDirEntry(
1161 StorageImpl *storage,
1162 const StgProperty *newData,
1165 ULONG currentPropertyIndex = 0;
1166 ULONG newPropertyIndex = PROPERTY_NULL;
1168 BYTE currentData[PROPSET_BLOCK_SIZE];
1169 WORD sizeOfNameString;
1173 hr = StorageImpl_ReadRawDirEntry(storage,
1174 currentPropertyIndex,
1179 StorageUtl_ReadWord(
1181 OFFSET_PS_NAMELENGTH,
1184 if (sizeOfNameString == 0)
1187 * The property existis and is available, we found it.
1189 newPropertyIndex = currentPropertyIndex;
1195 * We exhausted the property list, we will create more space below
1197 newPropertyIndex = currentPropertyIndex;
1199 currentPropertyIndex++;
1201 } while (newPropertyIndex == PROPERTY_NULL);
1204 * grow the property chain
1208 BYTE emptyData[PROPSET_BLOCK_SIZE];
1209 ULARGE_INTEGER newSize;
1210 ULONG propertyIndex;
1211 ULONG lastProperty = 0;
1212 ULONG blockCount = 0;
1215 * obtain the new count of property blocks
1217 blockCount = BlockChainStream_GetCount(
1218 storage->rootBlockChain)+1;
1221 * initialize the size used by the property stream
1223 newSize.u.HighPart = 0;
1224 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1227 * add a property block to the property chain
1229 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1232 * memset the empty property in order to initialize the unused newly
1235 memset(&emptyData, 0, PROPSET_BLOCK_SIZE);
1240 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1243 propertyIndex = newPropertyIndex + 1;
1244 propertyIndex < lastProperty;
1247 StorageImpl_WriteRawDirEntry(
1254 UpdateRawDirEntry(currentData, newData);
1256 hr = StorageImpl_WriteRawDirEntry(storage, newPropertyIndex, currentData);
1259 *index = newPropertyIndex;
1264 /***************************************************************************
1268 * Mark a directory entry in the file as free.
1270 static HRESULT destroyDirEntry(
1271 StorageImpl *storage,
1275 BYTE emptyData[PROPSET_BLOCK_SIZE];
1277 memset(&emptyData, 0, PROPSET_BLOCK_SIZE);
1279 hr = StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1285 /****************************************************************************
1289 * Case insensitive comparison of StgProperty.name by first considering
1292 * Returns <0 when newProperty < currentProperty
1293 * >0 when newProperty > currentProperty
1294 * 0 when newProperty == currentProperty
1296 static LONG propertyNameCmp(
1297 const OLECHAR *newProperty,
1298 const OLECHAR *currentProperty)
1300 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1305 * We compare the string themselves only when they are of the same length
1307 diff = lstrcmpiW( newProperty, currentProperty);
1313 /****************************************************************************
1317 * Properly link this new element in the property chain.
1319 static void updatePropertyChain(
1320 StorageBaseImpl *storage,
1321 ULONG newPropertyIndex,
1322 StgProperty newProperty)
1324 StgProperty currentProperty;
1327 * Read the root property
1329 StorageImpl_ReadProperty(storage->ancestorStorage,
1330 storage->rootPropertySetIndex,
1333 if (currentProperty.dirProperty != PROPERTY_NULL)
1336 * The root storage contains some element, therefore, start the research
1337 * for the appropriate location.
1340 ULONG current, next, previous, currentPropertyId;
1343 * Keep the StgProperty sequence number of the storage first property
1345 currentPropertyId = currentProperty.dirProperty;
1350 StorageImpl_ReadProperty(storage->ancestorStorage,
1351 currentProperty.dirProperty,
1354 previous = currentProperty.leftChild;
1355 next = currentProperty.rightChild;
1356 current = currentPropertyId;
1360 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1364 if (previous != PROPERTY_NULL)
1366 StorageImpl_ReadProperty(storage->ancestorStorage,
1373 currentProperty.leftChild = newPropertyIndex;
1374 StorageImpl_WriteProperty(storage->ancestorStorage,
1382 if (next != PROPERTY_NULL)
1384 StorageImpl_ReadProperty(storage->ancestorStorage,
1391 currentProperty.rightChild = newPropertyIndex;
1392 StorageImpl_WriteProperty(storage->ancestorStorage,
1401 * Trying to insert an item with the same name in the
1402 * subtree structure.
1407 previous = currentProperty.leftChild;
1408 next = currentProperty.rightChild;
1414 * The root storage is empty, link the new property to its dir property
1416 currentProperty.dirProperty = newPropertyIndex;
1417 StorageImpl_WriteProperty(storage->ancestorStorage,
1418 storage->rootPropertySetIndex,
1423 /****************************************************************************
1427 * Find and read the element of a storage with the given name.
1429 static ULONG findElement(StorageImpl *storage, ULONG storageEntry,
1430 const OLECHAR *name, StgProperty *data)
1434 /* Read the storage entry to find the root of the tree. */
1435 StorageImpl_ReadProperty(storage, storageEntry, data);
1437 currentEntry = data->dirProperty;
1439 while (currentEntry != PROPERTY_NULL)
1443 StorageImpl_ReadProperty(storage, currentEntry, data);
1445 cmp = propertyNameCmp(name, data->name);
1452 currentEntry = data->leftChild;
1455 currentEntry = data->rightChild;
1458 return currentEntry;
1461 /****************************************************************************
1465 * Find and read the binary tree parent of the element with the given name.
1467 * If there is no such element, find a place where it could be inserted and
1468 * return STG_E_FILENOTFOUND.
1470 static HRESULT findTreeParent(StorageImpl *storage, ULONG storageEntry,
1471 const OLECHAR *childName, StgProperty *parentData, ULONG *parentEntry,
1475 StgProperty childData;
1477 /* Read the storage entry to find the root of the tree. */
1478 StorageImpl_ReadProperty(storage, storageEntry, parentData);
1480 *parentEntry = storageEntry;
1481 *relation = PROPERTY_RELATION_DIR;
1483 childEntry = parentData->dirProperty;
1485 while (childEntry != PROPERTY_NULL)
1489 StorageImpl_ReadProperty(storage, childEntry, &childData);
1491 cmp = propertyNameCmp(childName, childData.name);
1499 *parentData = childData;
1500 *parentEntry = childEntry;
1501 *relation = PROPERTY_RELATION_PREVIOUS;
1503 childEntry = parentData->leftChild;
1508 *parentData = childData;
1509 *parentEntry = childEntry;
1510 *relation = PROPERTY_RELATION_NEXT;
1512 childEntry = parentData->rightChild;
1516 if (childEntry == PROPERTY_NULL)
1517 return STG_E_FILENOTFOUND;
1523 /*************************************************************************
1526 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1528 DWORD ciidExclude, /* [in] */
1529 const IID* rgiidExclude, /* [size_is][unique][in] */
1530 SNB snbExclude, /* [unique][in] */
1531 IStorage* pstgDest) /* [unique][in] */
1533 IEnumSTATSTG *elements = 0;
1534 STATSTG curElement, strStat;
1536 IStorage *pstgTmp, *pstgChild;
1537 IStream *pstrTmp, *pstrChild;
1538 BOOL skip = FALSE, skip_storage = FALSE, skip_stream = FALSE;
1541 TRACE("(%p, %d, %p, %p, %p)\n",
1542 iface, ciidExclude, rgiidExclude,
1543 snbExclude, pstgDest);
1545 if ( pstgDest == 0 )
1546 return STG_E_INVALIDPOINTER;
1549 * Enumerate the elements
1551 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1559 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1560 IStorage_SetClass( pstgDest, &curElement.clsid );
1562 for(i = 0; i < ciidExclude; ++i)
1564 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1565 skip_storage = TRUE;
1566 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1569 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1575 * Obtain the next element
1577 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1579 if ( hr == S_FALSE )
1581 hr = S_OK; /* done, every element has been copied */
1587 WCHAR **snb = snbExclude;
1589 while ( *snb != NULL && !skip )
1591 if ( lstrcmpW(curElement.pwcsName, *snb) == 0 )
1600 if (curElement.type == STGTY_STORAGE)
1606 * open child source storage
1608 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1609 STGM_READ|STGM_SHARE_EXCLUSIVE,
1610 NULL, 0, &pstgChild );
1616 * Check if destination storage is not a child of the source
1617 * storage, which will cause an infinite loop
1619 if (pstgChild == pstgDest)
1621 IEnumSTATSTG_Release(elements);
1623 return STG_E_ACCESSDENIED;
1627 * create a new storage in destination storage
1629 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1630 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1634 * if it already exist, don't create a new one use this one
1636 if (hr == STG_E_FILEALREADYEXISTS)
1638 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1639 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1640 NULL, 0, &pstgTmp );
1648 * do the copy recursively
1650 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1653 IStorage_Release( pstgTmp );
1654 IStorage_Release( pstgChild );
1656 else if (curElement.type == STGTY_STREAM)
1662 * create a new stream in destination storage. If the stream already
1663 * exist, it will be deleted and a new one will be created.
1665 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1666 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1673 * open child stream storage
1675 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1676 STGM_READ|STGM_SHARE_EXCLUSIVE,
1683 * Get the size of the source stream
1685 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1688 * Set the size of the destination stream.
1690 IStream_SetSize(pstrTmp, strStat.cbSize);
1695 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1698 IStream_Release( pstrTmp );
1699 IStream_Release( pstrChild );
1703 WARN("unknown element type: %d\n", curElement.type);
1706 } while (hr == S_OK);
1711 IEnumSTATSTG_Release(elements);
1716 /*************************************************************************
1717 * MoveElementTo (IStorage)
1719 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1721 const OLECHAR *pwcsName, /* [string][in] */
1722 IStorage *pstgDest, /* [unique][in] */
1723 const OLECHAR *pwcsNewName,/* [string][in] */
1724 DWORD grfFlags) /* [in] */
1726 FIXME("(%p %s %p %s %u): stub\n", iface,
1727 debugstr_w(pwcsName), pstgDest,
1728 debugstr_w(pwcsNewName), grfFlags);
1732 /*************************************************************************
1735 * Ensures that any changes made to a storage object open in transacted mode
1736 * are reflected in the parent storage
1739 * Wine doesn't implement transacted mode, which seems to be a basic
1740 * optimization, so we can ignore this stub for now.
1742 static HRESULT WINAPI StorageImpl_Commit(
1744 DWORD grfCommitFlags)/* [in] */
1746 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1750 /*************************************************************************
1753 * Discard all changes that have been made since the last commit operation
1755 static HRESULT WINAPI StorageImpl_Revert(
1758 FIXME("(%p): stub\n", iface);
1762 /*************************************************************************
1763 * DestroyElement (IStorage)
1765 * Strategy: This implementation is built this way for simplicity not for speed.
1766 * I always delete the topmost element of the enumeration and adjust
1767 * the deleted element pointer all the time. This takes longer to
1768 * do but allow to reinvoke DestroyElement whenever we encounter a
1769 * storage object. The optimisation resides in the usage of another
1770 * enumeration strategy that would give all the leaves of a storage
1771 * first. (postfix order)
1773 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1775 const OLECHAR *pwcsName)/* [string][in] */
1777 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1780 StgProperty propertyToDelete;
1781 ULONG foundPropertyIndexToDelete;
1784 iface, debugstr_w(pwcsName));
1787 return STG_E_INVALIDPOINTER;
1789 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1790 return STG_E_ACCESSDENIED;
1792 foundPropertyIndexToDelete = findElement(
1793 This->ancestorStorage,
1794 This->rootPropertySetIndex,
1798 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1800 return STG_E_FILENOTFOUND;
1803 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1805 hr = deleteStorageProperty(
1807 foundPropertyIndexToDelete,
1810 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1812 hr = deleteStreamProperty(
1814 foundPropertyIndexToDelete,
1822 * Adjust the property chain
1824 hr = removeFromTree(
1825 This->ancestorStorage,
1826 This->rootPropertySetIndex,
1827 foundPropertyIndexToDelete);
1830 * Invalidate the property
1833 destroyDirEntry(This->ancestorStorage,
1834 foundPropertyIndexToDelete);
1840 /************************************************************************
1841 * StorageImpl_Stat (IStorage)
1843 * This method will retrieve information about this storage object.
1845 * See Windows documentation for more details on IStorage methods.
1847 static HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1848 STATSTG* pstatstg, /* [out] */
1849 DWORD grfStatFlag) /* [in] */
1851 StorageImpl* const This = (StorageImpl*)iface;
1852 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1854 if ( SUCCEEDED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1856 CoTaskMemFree(pstatstg->pwcsName);
1857 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1858 strcpyW(pstatstg->pwcsName, This->pwcsName);
1864 /******************************************************************************
1865 * Internal stream list handlers
1868 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1870 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1871 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1874 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1876 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1877 list_remove(&(strm->StrmListEntry));
1880 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1882 struct list *cur, *cur2;
1883 StgStreamImpl *strm=NULL;
1885 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1886 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1887 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1888 strm->parentStorage = NULL;
1894 /*********************************************************************
1898 * Perform the deletion of a complete storage node
1901 static HRESULT deleteStorageProperty(
1902 StorageBaseImpl *parentStorage,
1903 ULONG indexOfPropertyToDelete,
1904 StgProperty propertyToDelete)
1906 IEnumSTATSTG *elements = 0;
1907 IStorage *childStorage = 0;
1908 STATSTG currentElement;
1910 HRESULT destroyHr = S_OK;
1913 * Open the storage and enumerate it
1915 hr = StorageBaseImpl_OpenStorage(
1916 (IStorage*)parentStorage,
1917 propertyToDelete.name,
1919 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1930 * Enumerate the elements
1932 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1937 * Obtain the next element
1939 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
1942 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
1944 CoTaskMemFree(currentElement.pwcsName);
1948 * We need to Reset the enumeration every time because we delete elements
1949 * and the enumeration could be invalid
1951 IEnumSTATSTG_Reset(elements);
1953 } while ((hr == S_OK) && (destroyHr == S_OK));
1955 IStorage_Release(childStorage);
1956 IEnumSTATSTG_Release(elements);
1961 /*********************************************************************
1965 * Perform the deletion of a stream node
1968 static HRESULT deleteStreamProperty(
1969 StorageBaseImpl *parentStorage,
1970 ULONG indexOfPropertyToDelete,
1971 StgProperty propertyToDelete)
1975 ULARGE_INTEGER size;
1977 size.u.HighPart = 0;
1980 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
1981 propertyToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
1991 hr = IStream_SetSize(pis, size);
1999 * Release the stream object.
2001 IStream_Release(pis);
2006 static void setPropertyLink(StgProperty *property, ULONG relation, ULONG new_target)
2010 case PROPERTY_RELATION_PREVIOUS:
2011 property->leftChild = new_target;
2013 case PROPERTY_RELATION_NEXT:
2014 property->rightChild = new_target;
2016 case PROPERTY_RELATION_DIR:
2017 property->dirProperty = new_target;
2024 /*************************************************************************
2028 * This method removes a directory entry from its parent storage tree without
2029 * freeing any resources attached to it.
2031 static HRESULT removeFromTree(
2033 ULONG parentStorageIndex,
2038 StgProperty propertyToDelete;
2039 StgProperty parentProperty;
2040 ULONG parentPropertyId;
2041 ULONG typeOfRelation;
2043 res = StorageImpl_ReadProperty(This, deletedIndex, &propertyToDelete);
2046 * Find the property that links to the one we want to delete.
2048 hr = findTreeParent(This, parentStorageIndex, propertyToDelete.name,
2049 &parentProperty, &parentPropertyId, &typeOfRelation);
2054 if (propertyToDelete.leftChild != PROPERTY_NULL)
2057 * Replace the deleted entry with its left child
2059 setPropertyLink(&parentProperty, typeOfRelation, propertyToDelete.leftChild);
2061 res = StorageImpl_WriteProperty(
2070 if (propertyToDelete.rightChild != PROPERTY_NULL)
2073 * We need to reinsert the right child somewhere. We already know it and
2074 * its children are greater than everything in the left tree, so we
2075 * insert it at the rightmost point in the left tree.
2077 ULONG newRightChildParent = propertyToDelete.leftChild;
2078 StgProperty newRightChildParentProperty;
2082 res = StorageImpl_ReadProperty(
2084 newRightChildParent,
2085 &newRightChildParentProperty);
2091 if (newRightChildParentProperty.rightChild != PROPERTY_NULL)
2092 newRightChildParent = newRightChildParentProperty.rightChild;
2093 } while (newRightChildParentProperty.rightChild != PROPERTY_NULL);
2095 newRightChildParentProperty.rightChild = propertyToDelete.rightChild;
2097 res = StorageImpl_WriteProperty(
2099 newRightChildParent,
2100 &newRightChildParentProperty);
2110 * Replace the deleted entry with its right child
2112 setPropertyLink(&parentProperty, typeOfRelation, propertyToDelete.rightChild);
2114 res = StorageImpl_WriteProperty(
2128 /******************************************************************************
2129 * SetElementTimes (IStorage)
2131 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2133 const OLECHAR *pwcsName,/* [string][in] */
2134 const FILETIME *pctime, /* [in] */
2135 const FILETIME *patime, /* [in] */
2136 const FILETIME *pmtime) /* [in] */
2138 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2142 /******************************************************************************
2143 * SetStateBits (IStorage)
2145 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2147 DWORD grfStateBits,/* [in] */
2148 DWORD grfMask) /* [in] */
2150 StorageBaseImpl* const This = (StorageBaseImpl*)iface;
2151 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2156 * Virtual function table for the IStorage32Impl class.
2158 static const IStorageVtbl Storage32Impl_Vtbl =
2160 StorageBaseImpl_QueryInterface,
2161 StorageBaseImpl_AddRef,
2162 StorageBaseImpl_Release,
2163 StorageBaseImpl_CreateStream,
2164 StorageBaseImpl_OpenStream,
2165 StorageBaseImpl_CreateStorage,
2166 StorageBaseImpl_OpenStorage,
2167 StorageBaseImpl_CopyTo,
2168 StorageBaseImpl_MoveElementTo,
2171 StorageBaseImpl_EnumElements,
2172 StorageBaseImpl_DestroyElement,
2173 StorageBaseImpl_RenameElement,
2174 StorageBaseImpl_SetElementTimes,
2175 StorageBaseImpl_SetClass,
2176 StorageBaseImpl_SetStateBits,
2180 static HRESULT StorageImpl_Construct(
2190 StgProperty currentProperty;
2191 BOOL readSuccessful;
2192 ULONG currentPropertyIndex;
2194 if ( FAILED( validateSTGM(openFlags) ))
2195 return STG_E_INVALIDFLAG;
2197 memset(This, 0, sizeof(StorageImpl));
2199 list_init(&This->base.strmHead);
2201 This->base.lpVtbl = &Storage32Impl_Vtbl;
2202 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2203 This->base.v_destructor = StorageImpl_Destroy;
2204 This->base.openFlags = (openFlags & ~STGM_CREATE);
2205 This->create = create;
2208 * This is the top-level storage so initialize the ancestor pointer
2211 This->base.ancestorStorage = This;
2213 This->hFile = hFile;
2216 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2217 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2218 if (!This->pwcsName)
2219 return STG_E_INSUFFICIENTMEMORY;
2220 strcpyW(This->pwcsName, pwcsName);
2224 * Initialize the big block cache.
2226 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2227 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2228 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2234 if (This->bigBlockFile == 0)
2239 ULARGE_INTEGER size;
2240 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2243 * Initialize all header variables:
2244 * - The big block depot consists of one block and it is at block 0
2245 * - The properties start at block 1
2246 * - There is no small block depot
2248 memset( This->bigBlockDepotStart,
2250 sizeof(This->bigBlockDepotStart));
2252 This->bigBlockDepotCount = 1;
2253 This->bigBlockDepotStart[0] = 0;
2254 This->rootStartBlock = 1;
2255 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2256 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2257 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2258 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2259 This->extBigBlockDepotCount = 0;
2261 StorageImpl_SaveFileHeader(This);
2264 * Add one block for the big block depot and one block for the properties
2266 size.u.HighPart = 0;
2267 size.u.LowPart = This->bigBlockSize * 3;
2268 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2271 * Initialize the big block depot
2273 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2274 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2275 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2276 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2281 * Load the header for the file.
2283 hr = StorageImpl_LoadFileHeader(This);
2287 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2294 * There is no block depot cached yet.
2296 This->indexBlockDepotCached = 0xFFFFFFFF;
2299 * Start searching for free blocks with block 0.
2301 This->prevFreeBlock = 0;
2304 * Create the block chain abstractions.
2306 if(!(This->rootBlockChain =
2307 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2308 return STG_E_READFAULT;
2310 if(!(This->smallBlockDepotChain =
2311 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2313 return STG_E_READFAULT;
2316 * Write the root property (memory only)
2320 StgProperty rootProp;
2322 * Initialize the property chain
2324 memset(&rootProp, 0, sizeof(rootProp));
2325 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2326 sizeof(rootProp.name)/sizeof(WCHAR) );
2327 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2328 rootProp.propertyType = PROPTYPE_ROOT;
2329 rootProp.leftChild = PROPERTY_NULL;
2330 rootProp.rightChild = PROPERTY_NULL;
2331 rootProp.dirProperty = PROPERTY_NULL;
2332 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2333 rootProp.size.u.HighPart = 0;
2334 rootProp.size.u.LowPart = 0;
2336 StorageImpl_WriteProperty(This, 0, &rootProp);
2340 * Find the ID of the root in the property sets.
2342 currentPropertyIndex = 0;
2346 readSuccessful = StorageImpl_ReadProperty(
2348 currentPropertyIndex,
2353 if ( (currentProperty.sizeOfNameString != 0 ) &&
2354 (currentProperty.propertyType == PROPTYPE_ROOT) )
2356 This->base.rootPropertySetIndex = currentPropertyIndex;
2360 currentPropertyIndex++;
2362 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2364 if (!readSuccessful)
2367 return STG_E_READFAULT;
2371 * Create the block chain abstraction for the small block root chain.
2373 if(!(This->smallBlockRootChain =
2374 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2375 return STG_E_READFAULT;
2380 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2382 StorageImpl *This = (StorageImpl*) iface;
2383 TRACE("(%p)\n", This);
2385 StorageBaseImpl_DeleteAll(&This->base);
2387 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2389 BlockChainStream_Destroy(This->smallBlockRootChain);
2390 BlockChainStream_Destroy(This->rootBlockChain);
2391 BlockChainStream_Destroy(This->smallBlockDepotChain);
2393 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2394 HeapFree(GetProcessHeap(), 0, This);
2397 /******************************************************************************
2398 * Storage32Impl_GetNextFreeBigBlock
2400 * Returns the index of the next free big block.
2401 * If the big block depot is filled, this method will enlarge it.
2404 static ULONG StorageImpl_GetNextFreeBigBlock(
2407 ULONG depotBlockIndexPos;
2408 BYTE depotBuffer[BIG_BLOCK_SIZE];
2410 ULONG depotBlockOffset;
2411 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2412 ULONG nextBlockIndex = BLOCK_SPECIAL;
2414 ULONG freeBlock = BLOCK_UNUSED;
2416 depotIndex = This->prevFreeBlock / blocksPerDepot;
2417 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2420 * Scan the entire big block depot until we find a block marked free
2422 while (nextBlockIndex != BLOCK_UNUSED)
2424 if (depotIndex < COUNT_BBDEPOTINHEADER)
2426 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2429 * Grow the primary depot.
2431 if (depotBlockIndexPos == BLOCK_UNUSED)
2433 depotBlockIndexPos = depotIndex*blocksPerDepot;
2436 * Add a block depot.
2438 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2439 This->bigBlockDepotCount++;
2440 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2443 * Flag it as a block depot.
2445 StorageImpl_SetNextBlockInChain(This,
2449 /* Save new header information.
2451 StorageImpl_SaveFileHeader(This);
2456 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2458 if (depotBlockIndexPos == BLOCK_UNUSED)
2461 * Grow the extended depot.
2463 ULONG extIndex = BLOCK_UNUSED;
2464 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2465 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2467 if (extBlockOffset == 0)
2469 /* We need an extended block.
2471 extIndex = Storage32Impl_AddExtBlockDepot(This);
2472 This->extBigBlockDepotCount++;
2473 depotBlockIndexPos = extIndex + 1;
2476 depotBlockIndexPos = depotIndex * blocksPerDepot;
2479 * Add a block depot and mark it in the extended block.
2481 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2482 This->bigBlockDepotCount++;
2483 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2485 /* Flag the block depot.
2487 StorageImpl_SetNextBlockInChain(This,
2491 /* If necessary, flag the extended depot block.
2493 if (extIndex != BLOCK_UNUSED)
2494 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2496 /* Save header information.
2498 StorageImpl_SaveFileHeader(This);
2502 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2506 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2507 ( nextBlockIndex != BLOCK_UNUSED))
2509 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2511 if (nextBlockIndex == BLOCK_UNUSED)
2513 freeBlock = (depotIndex * blocksPerDepot) +
2514 (depotBlockOffset/sizeof(ULONG));
2517 depotBlockOffset += sizeof(ULONG);
2522 depotBlockOffset = 0;
2526 * make sure that the block physically exists before using it
2528 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2530 This->prevFreeBlock = freeBlock;
2535 /******************************************************************************
2536 * Storage32Impl_AddBlockDepot
2538 * This will create a depot block, essentially it is a block initialized
2541 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2543 BYTE blockBuffer[BIG_BLOCK_SIZE];
2546 * Initialize blocks as free
2548 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2549 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2552 /******************************************************************************
2553 * Storage32Impl_GetExtDepotBlock
2555 * Returns the index of the block that corresponds to the specified depot
2556 * index. This method is only for depot indexes equal or greater than
2557 * COUNT_BBDEPOTINHEADER.
2559 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2561 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2562 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2563 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2564 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2565 ULONG blockIndex = BLOCK_UNUSED;
2566 ULONG extBlockIndex = This->extBigBlockDepotStart;
2568 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2570 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2571 return BLOCK_UNUSED;
2573 while (extBlockCount > 0)
2575 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2579 if (extBlockIndex != BLOCK_UNUSED)
2580 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
2581 extBlockOffset * sizeof(ULONG), &blockIndex);
2586 /******************************************************************************
2587 * Storage32Impl_SetExtDepotBlock
2589 * Associates the specified block index to the specified depot index.
2590 * This method is only for depot indexes equal or greater than
2591 * COUNT_BBDEPOTINHEADER.
2593 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2595 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2596 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2597 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2598 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2599 ULONG extBlockIndex = This->extBigBlockDepotStart;
2601 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2603 while (extBlockCount > 0)
2605 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2609 if (extBlockIndex != BLOCK_UNUSED)
2611 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
2612 extBlockOffset * sizeof(ULONG),
2617 /******************************************************************************
2618 * Storage32Impl_AddExtBlockDepot
2620 * Creates an extended depot block.
2622 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2624 ULONG numExtBlocks = This->extBigBlockDepotCount;
2625 ULONG nextExtBlock = This->extBigBlockDepotStart;
2626 BYTE depotBuffer[BIG_BLOCK_SIZE];
2627 ULONG index = BLOCK_UNUSED;
2628 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2629 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2630 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2632 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2633 blocksPerDepotBlock;
2635 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2638 * The first extended block.
2640 This->extBigBlockDepotStart = index;
2646 * Follow the chain to the last one.
2648 for (i = 0; i < (numExtBlocks - 1); i++)
2650 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2654 * Add the new extended block to the chain.
2656 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
2661 * Initialize this block.
2663 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2664 StorageImpl_WriteBigBlock(This, index, depotBuffer);
2669 /******************************************************************************
2670 * Storage32Impl_FreeBigBlock
2672 * This method will flag the specified block as free in the big block depot.
2674 static void StorageImpl_FreeBigBlock(
2678 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2680 if (blockIndex < This->prevFreeBlock)
2681 This->prevFreeBlock = blockIndex;
2684 /************************************************************************
2685 * Storage32Impl_GetNextBlockInChain
2687 * This method will retrieve the block index of the next big block in
2690 * Params: This - Pointer to the Storage object.
2691 * blockIndex - Index of the block to retrieve the chain
2693 * nextBlockIndex - receives the return value.
2695 * Returns: This method returns the index of the next block in the chain.
2696 * It will return the constants:
2697 * BLOCK_SPECIAL - If the block given was not part of a
2699 * BLOCK_END_OF_CHAIN - If the block given was the last in
2701 * BLOCK_UNUSED - If the block given was not past of a chain
2703 * BLOCK_EXTBBDEPOT - This block is part of the extended
2706 * See Windows documentation for more details on IStorage methods.
2708 static HRESULT StorageImpl_GetNextBlockInChain(
2711 ULONG* nextBlockIndex)
2713 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2714 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2715 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2716 BYTE depotBuffer[BIG_BLOCK_SIZE];
2718 ULONG depotBlockIndexPos;
2721 *nextBlockIndex = BLOCK_SPECIAL;
2723 if(depotBlockCount >= This->bigBlockDepotCount)
2725 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
2726 This->bigBlockDepotCount);
2727 return STG_E_READFAULT;
2731 * Cache the currently accessed depot block.
2733 if (depotBlockCount != This->indexBlockDepotCached)
2735 This->indexBlockDepotCached = depotBlockCount;
2737 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2739 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2744 * We have to look in the extended depot.
2746 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2749 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2752 return STG_E_READFAULT;
2754 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2756 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2757 This->blockDepotCached[index] = *nextBlockIndex;
2761 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2766 /******************************************************************************
2767 * Storage32Impl_GetNextExtendedBlock
2769 * Given an extended block this method will return the next extended block.
2772 * The last ULONG of an extended block is the block index of the next
2773 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2777 * - The index of the next extended block
2778 * - BLOCK_UNUSED: there is no next extended block.
2779 * - Any other return values denotes failure.
2781 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2783 ULONG nextBlockIndex = BLOCK_SPECIAL;
2784 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2786 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
2789 return nextBlockIndex;
2792 /******************************************************************************
2793 * Storage32Impl_SetNextBlockInChain
2795 * This method will write the index of the specified block's next block
2796 * in the big block depot.
2798 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2801 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2802 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2803 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2806 static void StorageImpl_SetNextBlockInChain(
2811 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2812 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2813 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2814 ULONG depotBlockIndexPos;
2816 assert(depotBlockCount < This->bigBlockDepotCount);
2817 assert(blockIndex != nextBlock);
2819 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2821 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2826 * We have to look in the extended depot.
2828 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2831 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
2834 * Update the cached block depot, if necessary.
2836 if (depotBlockCount == This->indexBlockDepotCached)
2838 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2842 /******************************************************************************
2843 * Storage32Impl_LoadFileHeader
2845 * This method will read in the file header, i.e. big block index -1.
2847 static HRESULT StorageImpl_LoadFileHeader(
2850 HRESULT hr = STG_E_FILENOTFOUND;
2851 BYTE headerBigBlock[BIG_BLOCK_SIZE];
2857 * Get a pointer to the big block of data containing the header.
2859 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
2862 * Extract the information from the header.
2867 * Check for the "magic number" signature and return an error if it is not
2870 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2872 return STG_E_OLDFORMAT;
2875 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2877 return STG_E_INVALIDHEADER;
2880 StorageUtl_ReadWord(
2882 OFFSET_BIGBLOCKSIZEBITS,
2883 &This->bigBlockSizeBits);
2885 StorageUtl_ReadWord(
2887 OFFSET_SMALLBLOCKSIZEBITS,
2888 &This->smallBlockSizeBits);
2890 StorageUtl_ReadDWord(
2892 OFFSET_BBDEPOTCOUNT,
2893 &This->bigBlockDepotCount);
2895 StorageUtl_ReadDWord(
2897 OFFSET_ROOTSTARTBLOCK,
2898 &This->rootStartBlock);
2900 StorageUtl_ReadDWord(
2902 OFFSET_SBDEPOTSTART,
2903 &This->smallBlockDepotStart);
2905 StorageUtl_ReadDWord(
2907 OFFSET_EXTBBDEPOTSTART,
2908 &This->extBigBlockDepotStart);
2910 StorageUtl_ReadDWord(
2912 OFFSET_EXTBBDEPOTCOUNT,
2913 &This->extBigBlockDepotCount);
2915 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2917 StorageUtl_ReadDWord(
2919 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2920 &(This->bigBlockDepotStart[index]));
2924 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2926 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2927 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2930 * Right now, the code is making some assumptions about the size of the
2931 * blocks, just make sure they are what we're expecting.
2933 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
2934 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
2936 WARN("Broken OLE storage file\n");
2937 hr = STG_E_INVALIDHEADER;
2946 /******************************************************************************
2947 * Storage32Impl_SaveFileHeader
2949 * This method will save to the file the header, i.e. big block -1.
2951 static void StorageImpl_SaveFileHeader(
2954 BYTE headerBigBlock[BIG_BLOCK_SIZE];
2959 * Get a pointer to the big block of data containing the header.
2961 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
2964 * If the block read failed, the file is probably new.
2969 * Initialize for all unknown fields.
2971 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
2974 * Initialize the magic number.
2976 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
2979 * And a bunch of things we don't know what they mean
2981 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
2982 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
2983 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
2984 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
2988 * Write the information to the header.
2990 StorageUtl_WriteWord(
2992 OFFSET_BIGBLOCKSIZEBITS,
2993 This->bigBlockSizeBits);
2995 StorageUtl_WriteWord(
2997 OFFSET_SMALLBLOCKSIZEBITS,
2998 This->smallBlockSizeBits);
3000 StorageUtl_WriteDWord(
3002 OFFSET_BBDEPOTCOUNT,
3003 This->bigBlockDepotCount);
3005 StorageUtl_WriteDWord(
3007 OFFSET_ROOTSTARTBLOCK,
3008 This->rootStartBlock);
3010 StorageUtl_WriteDWord(
3012 OFFSET_SBDEPOTSTART,
3013 This->smallBlockDepotStart);
3015 StorageUtl_WriteDWord(
3017 OFFSET_SBDEPOTCOUNT,
3018 This->smallBlockDepotChain ?
3019 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3021 StorageUtl_WriteDWord(
3023 OFFSET_EXTBBDEPOTSTART,
3024 This->extBigBlockDepotStart);
3026 StorageUtl_WriteDWord(
3028 OFFSET_EXTBBDEPOTCOUNT,
3029 This->extBigBlockDepotCount);
3031 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3033 StorageUtl_WriteDWord(
3035 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3036 (This->bigBlockDepotStart[index]));
3040 * Write the big block back to the file.
3042 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3045 /******************************************************************************
3046 * StorageImpl_ReadRawDirEntry
3048 * This method will read the raw data from a directory entry in the file.
3050 * buffer must be PROPSET_BLOCK_SIZE bytes long.
3052 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3054 ULARGE_INTEGER offset;
3058 offset.u.HighPart = 0;
3059 offset.u.LowPart = index * PROPSET_BLOCK_SIZE;
3061 hr = BlockChainStream_ReadAt(
3062 This->rootBlockChain,
3071 /******************************************************************************
3072 * StorageImpl_WriteRawDirEntry
3074 * This method will write the raw data from a directory entry in the file.
3076 * buffer must be PROPSET_BLOCK_SIZE bytes long.
3078 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3080 ULARGE_INTEGER offset;
3084 offset.u.HighPart = 0;
3085 offset.u.LowPart = index * PROPSET_BLOCK_SIZE;
3087 hr = BlockChainStream_WriteAt(
3088 This->rootBlockChain,
3097 /******************************************************************************
3100 * Update raw directory entry data from the fields in newData.
3102 * buffer must be PROPSET_BLOCK_SIZE bytes long.
3104 void UpdateRawDirEntry(BYTE *buffer, const StgProperty *newData)
3106 memset(buffer, 0, PROPSET_BLOCK_SIZE);
3109 buffer + OFFSET_PS_NAME,
3111 PROPERTY_NAME_BUFFER_LEN );
3113 memcpy(buffer + OFFSET_PS_PROPERTYTYPE, &newData->propertyType, 1);
3115 StorageUtl_WriteWord(
3117 OFFSET_PS_NAMELENGTH,
3118 newData->sizeOfNameString);
3120 StorageUtl_WriteDWord(
3122 OFFSET_PS_LEFTCHILD,
3123 newData->leftChild);
3125 StorageUtl_WriteDWord(
3127 OFFSET_PS_RIGHTCHILD,
3128 newData->rightChild);
3130 StorageUtl_WriteDWord(
3133 newData->dirProperty);
3135 StorageUtl_WriteGUID(
3138 &newData->propertyUniqueID);
3140 StorageUtl_WriteDWord(
3143 newData->ctime.dwLowDateTime);
3145 StorageUtl_WriteDWord(
3147 OFFSET_PS_CTIMEHIGH,
3148 newData->ctime.dwHighDateTime);
3150 StorageUtl_WriteDWord(
3153 newData->mtime.dwLowDateTime);
3155 StorageUtl_WriteDWord(
3157 OFFSET_PS_MTIMEHIGH,
3158 newData->ctime.dwHighDateTime);
3160 StorageUtl_WriteDWord(
3162 OFFSET_PS_STARTBLOCK,
3163 newData->startingBlock);
3165 StorageUtl_WriteDWord(
3168 newData->size.u.LowPart);
3171 /******************************************************************************
3172 * Storage32Impl_ReadProperty
3174 * This method will read the specified property from the property chain.
3176 BOOL StorageImpl_ReadProperty(
3179 StgProperty* buffer)
3181 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3184 readRes = StorageImpl_ReadRawDirEntry(This, index, currentProperty);
3186 if (SUCCEEDED(readRes))
3188 /* replace the name of root entry (often "Root Entry") by the file name */
3189 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3190 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3192 memset(buffer->name, 0, sizeof(buffer->name));
3196 PROPERTY_NAME_BUFFER_LEN );
3197 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3199 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3201 StorageUtl_ReadWord(
3203 OFFSET_PS_NAMELENGTH,
3204 &buffer->sizeOfNameString);
3206 StorageUtl_ReadDWord(
3208 OFFSET_PS_LEFTCHILD,
3209 &buffer->leftChild);
3211 StorageUtl_ReadDWord(
3213 OFFSET_PS_RIGHTCHILD,
3214 &buffer->rightChild);
3216 StorageUtl_ReadDWord(
3219 &buffer->dirProperty);
3221 StorageUtl_ReadGUID(
3224 &buffer->propertyUniqueID);
3226 StorageUtl_ReadDWord(
3229 &buffer->ctime.dwLowDateTime);
3231 StorageUtl_ReadDWord(
3233 OFFSET_PS_CTIMEHIGH,
3234 &buffer->ctime.dwHighDateTime);
3236 StorageUtl_ReadDWord(
3239 &buffer->mtime.dwLowDateTime);
3241 StorageUtl_ReadDWord(
3243 OFFSET_PS_MTIMEHIGH,
3244 &buffer->mtime.dwHighDateTime);
3246 StorageUtl_ReadDWord(
3248 OFFSET_PS_STARTBLOCK,
3249 &buffer->startingBlock);
3251 StorageUtl_ReadDWord(
3254 &buffer->size.u.LowPart);
3256 buffer->size.u.HighPart = 0;
3259 return SUCCEEDED(readRes) ? TRUE : FALSE;
3262 /*********************************************************************
3263 * Write the specified property into the property chain
3265 BOOL StorageImpl_WriteProperty(
3268 const StgProperty* buffer)
3270 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3273 UpdateRawDirEntry(currentProperty, buffer);
3275 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentProperty);
3276 return SUCCEEDED(writeRes) ? TRUE : FALSE;
3279 static BOOL StorageImpl_ReadBigBlock(
3284 ULARGE_INTEGER ulOffset;
3287 ulOffset.u.HighPart = 0;
3288 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3290 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3291 return (read == This->bigBlockSize);
3294 static BOOL StorageImpl_ReadDWordFromBigBlock(
3300 ULARGE_INTEGER ulOffset;
3304 ulOffset.u.HighPart = 0;
3305 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3306 ulOffset.u.LowPart += offset;
3308 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3309 *value = lendian32toh(tmp);
3310 return (read == sizeof(DWORD));
3313 static BOOL StorageImpl_WriteBigBlock(
3318 ULARGE_INTEGER ulOffset;
3321 ulOffset.u.HighPart = 0;
3322 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3324 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3325 return (wrote == This->bigBlockSize);
3328 static BOOL StorageImpl_WriteDWordToBigBlock(
3334 ULARGE_INTEGER ulOffset;
3337 ulOffset.u.HighPart = 0;
3338 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3339 ulOffset.u.LowPart += offset;
3341 value = htole32(value);
3342 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3343 return (wrote == sizeof(DWORD));
3346 /******************************************************************************
3347 * Storage32Impl_SmallBlocksToBigBlocks
3349 * This method will convert a small block chain to a big block chain.
3350 * The small block chain will be destroyed.
3352 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3354 SmallBlockChainStream** ppsbChain)
3356 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3357 ULARGE_INTEGER size, offset;
3358 ULONG cbRead, cbWritten;
3359 ULARGE_INTEGER cbTotalRead;
3360 ULONG propertyIndex;
3361 HRESULT resWrite = S_OK;
3363 StgProperty chainProperty;
3365 BlockChainStream *bbTempChain = NULL;
3366 BlockChainStream *bigBlockChain = NULL;
3369 * Create a temporary big block chain that doesn't have
3370 * an associated property. This temporary chain will be
3371 * used to copy data from small blocks to big blocks.
3373 bbTempChain = BlockChainStream_Construct(This,
3376 if(!bbTempChain) return NULL;
3378 * Grow the big block chain.
3380 size = SmallBlockChainStream_GetSize(*ppsbChain);
3381 BlockChainStream_SetSize(bbTempChain, size);
3384 * Copy the contents of the small block chain to the big block chain
3385 * by small block size increments.
3387 offset.u.LowPart = 0;
3388 offset.u.HighPart = 0;
3389 cbTotalRead.QuadPart = 0;
3391 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3394 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3396 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3399 if (FAILED(resRead))
3404 cbTotalRead.QuadPart += cbRead;
3406 resWrite = BlockChainStream_WriteAt(bbTempChain,
3412 if (FAILED(resWrite))
3415 offset.u.LowPart += cbRead;
3417 } while (cbTotalRead.QuadPart < size.QuadPart);
3418 HeapFree(GetProcessHeap(),0,buffer);
3420 size.u.HighPart = 0;
3423 if (FAILED(resRead) || FAILED(resWrite))
3425 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3426 BlockChainStream_SetSize(bbTempChain, size);
3427 BlockChainStream_Destroy(bbTempChain);
3432 * Destroy the small block chain.
3434 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3435 SmallBlockChainStream_SetSize(*ppsbChain, size);
3436 SmallBlockChainStream_Destroy(*ppsbChain);
3440 * Change the property information. This chain is now a big block chain
3441 * and it doesn't reside in the small blocks chain anymore.
3443 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3445 chainProperty.startingBlock = bbHeadOfChain;
3447 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3450 * Destroy the temporary propertyless big block chain.
3451 * Create a new big block chain associated with this property.
3453 BlockChainStream_Destroy(bbTempChain);
3454 bigBlockChain = BlockChainStream_Construct(This,
3458 return bigBlockChain;
3461 /******************************************************************************
3462 * Storage32Impl_BigBlocksToSmallBlocks
3464 * This method will convert a big block chain to a small block chain.
3465 * The big block chain will be destroyed on success.
3467 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3469 BlockChainStream** ppbbChain)
3471 ULARGE_INTEGER size, offset, cbTotalRead;
3472 ULONG cbRead, cbWritten, propertyIndex, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3473 HRESULT resWrite = S_OK, resRead;
3474 StgProperty chainProperty;
3476 SmallBlockChainStream* sbTempChain;
3478 TRACE("%p %p\n", This, ppbbChain);
3480 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3486 size = BlockChainStream_GetSize(*ppbbChain);
3487 SmallBlockChainStream_SetSize(sbTempChain, size);
3489 offset.u.HighPart = 0;
3490 offset.u.LowPart = 0;
3491 cbTotalRead.QuadPart = 0;
3492 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3495 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3496 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
3504 cbTotalRead.QuadPart += cbRead;
3506 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3507 cbRead, buffer, &cbWritten);
3509 if(FAILED(resWrite))
3512 offset.u.LowPart += cbRead;
3514 }while(cbTotalRead.QuadPart < size.QuadPart);
3515 HeapFree(GetProcessHeap(), 0, buffer);
3517 size.u.HighPart = 0;
3520 if(FAILED(resRead) || FAILED(resWrite))
3522 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3523 SmallBlockChainStream_SetSize(sbTempChain, size);
3524 SmallBlockChainStream_Destroy(sbTempChain);
3528 /* destroy the original big block chain */
3529 propertyIndex = (*ppbbChain)->ownerPropertyIndex;
3530 BlockChainStream_SetSize(*ppbbChain, size);
3531 BlockChainStream_Destroy(*ppbbChain);
3534 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3535 chainProperty.startingBlock = sbHeadOfChain;
3536 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3538 SmallBlockChainStream_Destroy(sbTempChain);
3539 return SmallBlockChainStream_Construct(This, NULL, propertyIndex);
3542 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3544 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3546 HeapFree(GetProcessHeap(), 0, This);
3549 /******************************************************************************
3551 ** Storage32InternalImpl_Commit
3554 static HRESULT WINAPI StorageInternalImpl_Commit(
3556 DWORD grfCommitFlags) /* [in] */
3558 FIXME("(%p,%x): stub\n", iface, grfCommitFlags);
3562 /******************************************************************************
3564 ** Storage32InternalImpl_Revert
3567 static HRESULT WINAPI StorageInternalImpl_Revert(
3570 FIXME("(%p): stub\n", iface);
3574 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3576 IStorage_Release((IStorage*)This->parentStorage);
3577 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3578 HeapFree(GetProcessHeap(), 0, This);
3581 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3582 IEnumSTATSTG* iface,
3586 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3589 return E_INVALIDARG;
3593 if (IsEqualGUID(&IID_IUnknown, riid) ||
3594 IsEqualGUID(&IID_IEnumSTATSTG, riid))
3597 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3601 return E_NOINTERFACE;
3604 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3605 IEnumSTATSTG* iface)
3607 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3608 return InterlockedIncrement(&This->ref);
3611 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3612 IEnumSTATSTG* iface)
3614 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3618 newRef = InterlockedDecrement(&This->ref);
3622 IEnumSTATSTGImpl_Destroy(This);
3628 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3629 IEnumSTATSTG* iface,
3632 ULONG* pceltFetched)
3634 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3636 StgProperty currentProperty;
3637 STATSTG* currentReturnStruct = rgelt;
3638 ULONG objectFetched = 0;
3639 ULONG currentSearchNode;
3641 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3642 return E_INVALIDARG;
3645 * To avoid the special case, get another pointer to a ULONG value if
3646 * the caller didn't supply one.
3648 if (pceltFetched==0)
3649 pceltFetched = &objectFetched;
3652 * Start the iteration, we will iterate until we hit the end of the
3653 * linked list or until we hit the number of items to iterate through
3658 * Start with the node at the top of the stack.
3660 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3662 while ( ( *pceltFetched < celt) &&
3663 ( currentSearchNode!=PROPERTY_NULL) )
3666 * Remove the top node from the stack
3668 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3671 * Read the property from the storage.
3673 StorageImpl_ReadProperty(This->parentStorage,
3678 * Copy the information to the return buffer.
3680 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3685 * Step to the next item in the iteration
3688 currentReturnStruct++;
3691 * Push the next search node in the search stack.
3693 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.rightChild);
3696 * continue the iteration.
3698 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3701 if (*pceltFetched == celt)
3708 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3709 IEnumSTATSTG* iface,
3712 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3714 StgProperty currentProperty;
3715 ULONG objectFetched = 0;
3716 ULONG currentSearchNode;
3719 * Start with the node at the top of the stack.
3721 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3723 while ( (objectFetched < celt) &&
3724 (currentSearchNode!=PROPERTY_NULL) )
3727 * Remove the top node from the stack
3729 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3732 * Read the property from the storage.
3734 StorageImpl_ReadProperty(This->parentStorage,
3739 * Step to the next item in the iteration
3744 * Push the next search node in the search stack.
3746 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.rightChild);
3749 * continue the iteration.
3751 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3754 if (objectFetched == celt)
3760 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3761 IEnumSTATSTG* iface)
3763 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3765 StgProperty rootProperty;
3766 BOOL readSuccessful;
3769 * Re-initialize the search stack to an empty stack
3771 This->stackSize = 0;
3774 * Read the root property from the storage.
3776 readSuccessful = StorageImpl_ReadProperty(
3777 This->parentStorage,
3778 This->firstPropertyNode,
3783 assert(rootProperty.sizeOfNameString!=0);
3786 * Push the search node in the search stack.
3788 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3794 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3795 IEnumSTATSTG* iface,
3796 IEnumSTATSTG** ppenum)
3798 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3800 IEnumSTATSTGImpl* newClone;
3803 * Perform a sanity check on the parameters.
3806 return E_INVALIDARG;
3808 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3809 This->firstPropertyNode);
3813 * The new clone enumeration must point to the same current node as
3816 newClone->stackSize = This->stackSize ;
3817 newClone->stackMaxSize = This->stackMaxSize ;
3818 newClone->stackToVisit =
3819 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3822 newClone->stackToVisit,
3824 sizeof(ULONG) * newClone->stackSize);
3826 *ppenum = (IEnumSTATSTG*)newClone;
3829 * Don't forget to nail down a reference to the clone before
3832 IEnumSTATSTGImpl_AddRef(*ppenum);
3837 static void IEnumSTATSTGImpl_PushSearchNode(
3838 IEnumSTATSTGImpl* This,
3841 StgProperty rootProperty;
3842 BOOL readSuccessful;
3845 * First, make sure we're not trying to push an unexisting node.
3847 if (nodeToPush==PROPERTY_NULL)
3851 * First push the node to the stack
3853 if (This->stackSize == This->stackMaxSize)
3855 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
3857 This->stackToVisit = HeapReAlloc(
3861 sizeof(ULONG) * This->stackMaxSize);
3864 This->stackToVisit[This->stackSize] = nodeToPush;
3868 * Read the root property from the storage.
3870 readSuccessful = StorageImpl_ReadProperty(
3871 This->parentStorage,
3877 assert(rootProperty.sizeOfNameString!=0);
3880 * Push the previous search node in the search stack.
3882 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.leftChild);
3886 static ULONG IEnumSTATSTGImpl_PopSearchNode(
3887 IEnumSTATSTGImpl* This,
3892 if (This->stackSize == 0)
3893 return PROPERTY_NULL;
3895 topNode = This->stackToVisit[This->stackSize-1];
3904 * Virtual function table for the IEnumSTATSTGImpl class.
3906 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
3908 IEnumSTATSTGImpl_QueryInterface,
3909 IEnumSTATSTGImpl_AddRef,
3910 IEnumSTATSTGImpl_Release,
3911 IEnumSTATSTGImpl_Next,
3912 IEnumSTATSTGImpl_Skip,
3913 IEnumSTATSTGImpl_Reset,
3914 IEnumSTATSTGImpl_Clone
3917 /******************************************************************************
3918 ** IEnumSTATSTGImpl implementation
3921 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3922 StorageImpl* parentStorage,
3923 ULONG firstPropertyNode)
3925 IEnumSTATSTGImpl* newEnumeration;
3927 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3929 if (newEnumeration!=0)
3932 * Set-up the virtual function table and reference count.
3934 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
3935 newEnumeration->ref = 0;
3938 * We want to nail-down the reference to the storage in case the
3939 * enumeration out-lives the storage in the client application.
3941 newEnumeration->parentStorage = parentStorage;
3942 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3944 newEnumeration->firstPropertyNode = firstPropertyNode;
3947 * Initialize the search stack
3949 newEnumeration->stackSize = 0;
3950 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3951 newEnumeration->stackToVisit =
3952 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3955 * Make sure the current node of the iterator is the first one.
3957 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3960 return newEnumeration;
3964 * Virtual function table for the Storage32InternalImpl class.
3966 static const IStorageVtbl Storage32InternalImpl_Vtbl =
3968 StorageBaseImpl_QueryInterface,
3969 StorageBaseImpl_AddRef,
3970 StorageBaseImpl_Release,
3971 StorageBaseImpl_CreateStream,
3972 StorageBaseImpl_OpenStream,
3973 StorageBaseImpl_CreateStorage,
3974 StorageBaseImpl_OpenStorage,
3975 StorageBaseImpl_CopyTo,
3976 StorageBaseImpl_MoveElementTo,
3977 StorageInternalImpl_Commit,
3978 StorageInternalImpl_Revert,
3979 StorageBaseImpl_EnumElements,
3980 StorageBaseImpl_DestroyElement,
3981 StorageBaseImpl_RenameElement,
3982 StorageBaseImpl_SetElementTimes,
3983 StorageBaseImpl_SetClass,
3984 StorageBaseImpl_SetStateBits,
3985 StorageBaseImpl_Stat
3988 /******************************************************************************
3989 ** Storage32InternalImpl implementation
3992 static StorageInternalImpl* StorageInternalImpl_Construct(
3993 StorageImpl* ancestorStorage,
3995 ULONG rootPropertyIndex)
3997 StorageInternalImpl* newStorage;
3999 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
4004 * Initialize the stream list
4006 list_init(&newStorage->base.strmHead);
4009 * Initialize the virtual function table.
4011 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4012 newStorage->base.v_destructor = StorageInternalImpl_Destroy;
4013 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4016 * Keep the ancestor storage pointer but do not nail a reference to it.
4018 newStorage->base.ancestorStorage = ancestorStorage;
4021 * Keep the index of the root property set for this storage,
4023 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4031 /******************************************************************************
4032 ** StorageUtl implementation
4035 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4039 memcpy(&tmp, buffer+offset, sizeof(WORD));
4040 *value = lendian16toh(tmp);
4043 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4045 value = htole16(value);
4046 memcpy(buffer+offset, &value, sizeof(WORD));
4049 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4053 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4054 *value = lendian32toh(tmp);
4057 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4059 value = htole32(value);
4060 memcpy(buffer+offset, &value, sizeof(DWORD));
4063 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4064 ULARGE_INTEGER* value)
4066 #ifdef WORDS_BIGENDIAN
4069 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4070 value->u.LowPart = htole32(tmp.u.HighPart);
4071 value->u.HighPart = htole32(tmp.u.LowPart);
4073 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4077 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4078 const ULARGE_INTEGER *value)
4080 #ifdef WORDS_BIGENDIAN
4083 tmp.u.LowPart = htole32(value->u.HighPart);
4084 tmp.u.HighPart = htole32(value->u.LowPart);
4085 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4087 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4091 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4093 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4094 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4095 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4097 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4100 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4102 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4103 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4104 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4106 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4109 void StorageUtl_CopyPropertyToSTATSTG(
4110 STATSTG* destination,
4111 const StgProperty* source,
4115 * The copy of the string occurs only when the flag is not set
4117 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4118 (source->name == NULL) ||
4119 (source->name[0] == 0) )
4121 destination->pwcsName = 0;
4125 destination->pwcsName =
4126 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4128 strcpyW(destination->pwcsName, source->name);
4131 switch (source->propertyType)
4133 case PROPTYPE_STORAGE:
4135 destination->type = STGTY_STORAGE;
4137 case PROPTYPE_STREAM:
4138 destination->type = STGTY_STREAM;
4141 destination->type = STGTY_STREAM;
4145 destination->cbSize = source->size;
4147 currentReturnStruct->mtime = {0}; TODO
4148 currentReturnStruct->ctime = {0};
4149 currentReturnStruct->atime = {0};
4151 destination->grfMode = 0;
4152 destination->grfLocksSupported = 0;
4153 destination->clsid = source->propertyUniqueID;
4154 destination->grfStateBits = 0;
4155 destination->reserved = 0;
4158 /******************************************************************************
4159 ** BlockChainStream implementation
4162 BlockChainStream* BlockChainStream_Construct(
4163 StorageImpl* parentStorage,
4164 ULONG* headOfStreamPlaceHolder,
4165 ULONG propertyIndex)
4167 BlockChainStream* newStream;
4170 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4172 newStream->parentStorage = parentStorage;
4173 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4174 newStream->ownerPropertyIndex = propertyIndex;
4175 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4176 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4177 newStream->numBlocks = 0;
4179 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4181 while (blockIndex != BLOCK_END_OF_CHAIN)
4183 newStream->numBlocks++;
4184 newStream->tailIndex = blockIndex;
4186 if(FAILED(StorageImpl_GetNextBlockInChain(
4191 HeapFree(GetProcessHeap(), 0, newStream);
4199 void BlockChainStream_Destroy(BlockChainStream* This)
4201 HeapFree(GetProcessHeap(), 0, This);
4204 /******************************************************************************
4205 * BlockChainStream_GetHeadOfChain
4207 * Returns the head of this stream chain.
4208 * Some special chains don't have properties, their heads are kept in
4209 * This->headOfStreamPlaceHolder.
4212 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4214 StgProperty chainProperty;
4215 BOOL readSuccessful;
4217 if (This->headOfStreamPlaceHolder != 0)
4218 return *(This->headOfStreamPlaceHolder);
4220 if (This->ownerPropertyIndex != PROPERTY_NULL)
4222 readSuccessful = StorageImpl_ReadProperty(
4223 This->parentStorage,
4224 This->ownerPropertyIndex,
4229 return chainProperty.startingBlock;
4233 return BLOCK_END_OF_CHAIN;
4236 /******************************************************************************
4237 * BlockChainStream_GetCount
4239 * Returns the number of blocks that comprises this chain.
4240 * This is not the size of the stream as the last block may not be full!
4243 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
4248 blockIndex = BlockChainStream_GetHeadOfChain(This);
4250 while (blockIndex != BLOCK_END_OF_CHAIN)
4254 if(FAILED(StorageImpl_GetNextBlockInChain(
4255 This->parentStorage,
4264 /******************************************************************************
4265 * BlockChainStream_ReadAt
4267 * Reads a specified number of bytes from this chain at the specified offset.
4268 * bytesRead may be NULL.
4269 * Failure will be returned if the specified number of bytes has not been read.
4271 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
4272 ULARGE_INTEGER offset,
4277 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4278 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4279 ULONG bytesToReadInBuffer;
4283 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
4286 * Find the first block in the stream that contains part of the buffer.
4288 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4289 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4290 (blockNoInSequence < This->lastBlockNoInSequence) )
4292 blockIndex = BlockChainStream_GetHeadOfChain(This);
4293 This->lastBlockNoInSequence = blockNoInSequence;
4297 ULONG temp = blockNoInSequence;
4299 blockIndex = This->lastBlockNoInSequenceIndex;
4300 blockNoInSequence -= This->lastBlockNoInSequence;
4301 This->lastBlockNoInSequence = temp;
4304 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4306 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4307 return STG_E_DOCFILECORRUPT;
4308 blockNoInSequence--;
4311 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4312 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
4314 This->lastBlockNoInSequenceIndex = blockIndex;
4317 * Start reading the buffer.
4320 bufferWalker = buffer;
4322 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4324 ULARGE_INTEGER ulOffset;
4327 * Calculate how many bytes we can copy from this big block.
4329 bytesToReadInBuffer =
4330 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4332 TRACE("block %i\n",blockIndex);
4333 ulOffset.u.HighPart = 0;
4334 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4337 StorageImpl_ReadAt(This->parentStorage,
4340 bytesToReadInBuffer,
4343 * Step to the next big block.
4345 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4346 return STG_E_DOCFILECORRUPT;
4348 bufferWalker += bytesReadAt;
4349 size -= bytesReadAt;
4350 *bytesRead += bytesReadAt;
4351 offsetInBlock = 0; /* There is no offset on the next block */
4353 if (bytesToReadInBuffer != bytesReadAt)
4357 return (size == 0) ? S_OK : STG_E_READFAULT;
4360 /******************************************************************************
4361 * BlockChainStream_WriteAt
4363 * Writes the specified number of bytes to this chain at the specified offset.
4364 * Will fail if not all specified number of bytes have been written.
4366 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
4367 ULARGE_INTEGER offset,
4370 ULONG* bytesWritten)
4372 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4373 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4376 const BYTE* bufferWalker;
4379 * Find the first block in the stream that contains part of the buffer.
4381 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4382 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4383 (blockNoInSequence < This->lastBlockNoInSequence) )
4385 blockIndex = BlockChainStream_GetHeadOfChain(This);
4386 This->lastBlockNoInSequence = blockNoInSequence;
4390 ULONG temp = blockNoInSequence;
4392 blockIndex = This->lastBlockNoInSequenceIndex;
4393 blockNoInSequence -= This->lastBlockNoInSequence;
4394 This->lastBlockNoInSequence = temp;
4397 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4399 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4401 return STG_E_DOCFILECORRUPT;
4402 blockNoInSequence--;
4405 This->lastBlockNoInSequenceIndex = blockIndex;
4407 /* BlockChainStream_SetSize should have already been called to ensure we have
4408 * enough blocks in the chain to write into */
4409 if (blockIndex == BLOCK_END_OF_CHAIN)
4411 ERR("not enough blocks in chain to write data\n");
4412 return STG_E_DOCFILECORRUPT;
4416 bufferWalker = buffer;
4418 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4420 ULARGE_INTEGER ulOffset;
4421 DWORD bytesWrittenAt;
4423 * Calculate how many bytes we can copy from this big block.
4426 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4428 TRACE("block %i\n",blockIndex);
4429 ulOffset.u.HighPart = 0;
4430 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4433 StorageImpl_WriteAt(This->parentStorage,
4440 * Step to the next big block.
4442 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4444 return STG_E_DOCFILECORRUPT;
4446 bufferWalker += bytesWrittenAt;
4447 size -= bytesWrittenAt;
4448 *bytesWritten += bytesWrittenAt;
4449 offsetInBlock = 0; /* There is no offset on the next block */
4451 if (bytesWrittenAt != bytesToWrite)
4455 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
4458 /******************************************************************************
4459 * BlockChainStream_Shrink
4461 * Shrinks this chain in the big block depot.
4463 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
4464 ULARGE_INTEGER newSize)
4466 ULONG blockIndex, extraBlock;
4471 * Reset the last accessed block cache.
4473 This->lastBlockNoInSequence = 0xFFFFFFFF;
4474 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4477 * Figure out how many blocks are needed to contain the new size
4479 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4481 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4484 blockIndex = BlockChainStream_GetHeadOfChain(This);
4487 * Go to the new end of chain
4489 while (count < numBlocks)
4491 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4497 /* Get the next block before marking the new end */
4498 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4502 /* Mark the new end of chain */
4503 StorageImpl_SetNextBlockInChain(
4504 This->parentStorage,
4506 BLOCK_END_OF_CHAIN);
4508 This->tailIndex = blockIndex;
4509 This->numBlocks = numBlocks;
4512 * Mark the extra blocks as free
4514 while (extraBlock != BLOCK_END_OF_CHAIN)
4516 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4519 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4520 extraBlock = blockIndex;
4526 /******************************************************************************
4527 * BlockChainStream_Enlarge
4529 * Grows this chain in the big block depot.
4531 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4532 ULARGE_INTEGER newSize)
4534 ULONG blockIndex, currentBlock;
4536 ULONG oldNumBlocks = 0;
4538 blockIndex = BlockChainStream_GetHeadOfChain(This);
4541 * Empty chain. Create the head.
4543 if (blockIndex == BLOCK_END_OF_CHAIN)
4545 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4546 StorageImpl_SetNextBlockInChain(This->parentStorage,
4548 BLOCK_END_OF_CHAIN);
4550 if (This->headOfStreamPlaceHolder != 0)
4552 *(This->headOfStreamPlaceHolder) = blockIndex;
4556 StgProperty chainProp;
4557 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4559 StorageImpl_ReadProperty(
4560 This->parentStorage,
4561 This->ownerPropertyIndex,
4564 chainProp.startingBlock = blockIndex;
4566 StorageImpl_WriteProperty(
4567 This->parentStorage,
4568 This->ownerPropertyIndex,
4572 This->tailIndex = blockIndex;
4573 This->numBlocks = 1;
4577 * Figure out how many blocks are needed to contain this stream
4579 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4581 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4585 * Go to the current end of chain
4587 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4589 currentBlock = blockIndex;
4591 while (blockIndex != BLOCK_END_OF_CHAIN)
4594 currentBlock = blockIndex;
4596 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4601 This->tailIndex = currentBlock;
4604 currentBlock = This->tailIndex;
4605 oldNumBlocks = This->numBlocks;
4608 * Add new blocks to the chain
4610 if (oldNumBlocks < newNumBlocks)
4612 while (oldNumBlocks < newNumBlocks)
4614 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4616 StorageImpl_SetNextBlockInChain(
4617 This->parentStorage,
4621 StorageImpl_SetNextBlockInChain(
4622 This->parentStorage,
4624 BLOCK_END_OF_CHAIN);
4626 currentBlock = blockIndex;
4630 This->tailIndex = blockIndex;
4631 This->numBlocks = newNumBlocks;
4637 /******************************************************************************
4638 * BlockChainStream_SetSize
4640 * Sets the size of this stream. The big block depot will be updated.
4641 * The file will grow if we grow the chain.
4643 * TODO: Free the actual blocks in the file when we shrink the chain.
4644 * Currently, the blocks are still in the file. So the file size
4645 * doesn't shrink even if we shrink streams.
4647 BOOL BlockChainStream_SetSize(
4648 BlockChainStream* This,
4649 ULARGE_INTEGER newSize)
4651 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4653 if (newSize.u.LowPart == size.u.LowPart)
4656 if (newSize.u.LowPart < size.u.LowPart)
4658 BlockChainStream_Shrink(This, newSize);
4662 BlockChainStream_Enlarge(This, newSize);
4668 /******************************************************************************
4669 * BlockChainStream_GetSize
4671 * Returns the size of this chain.
4672 * Will return the block count if this chain doesn't have a property.
4674 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4676 StgProperty chainProperty;
4678 if(This->headOfStreamPlaceHolder == NULL)
4681 * This chain is a data stream read the property and return
4682 * the appropriate size
4684 StorageImpl_ReadProperty(
4685 This->parentStorage,
4686 This->ownerPropertyIndex,
4689 return chainProperty.size;
4694 * this chain is a chain that does not have a property, figure out the
4695 * size by making the product number of used blocks times the
4698 ULARGE_INTEGER result;
4699 result.u.HighPart = 0;
4702 BlockChainStream_GetCount(This) *
4703 This->parentStorage->bigBlockSize;
4709 /******************************************************************************
4710 ** SmallBlockChainStream implementation
4713 SmallBlockChainStream* SmallBlockChainStream_Construct(
4714 StorageImpl* parentStorage,
4715 ULONG* headOfStreamPlaceHolder,
4716 ULONG propertyIndex)
4718 SmallBlockChainStream* newStream;
4720 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4722 newStream->parentStorage = parentStorage;
4723 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4724 newStream->ownerPropertyIndex = propertyIndex;
4729 void SmallBlockChainStream_Destroy(
4730 SmallBlockChainStream* This)
4732 HeapFree(GetProcessHeap(), 0, This);
4735 /******************************************************************************
4736 * SmallBlockChainStream_GetHeadOfChain
4738 * Returns the head of this chain of small blocks.
4740 static ULONG SmallBlockChainStream_GetHeadOfChain(
4741 SmallBlockChainStream* This)
4743 StgProperty chainProperty;
4744 BOOL readSuccessful;
4746 if (This->headOfStreamPlaceHolder != NULL)
4747 return *(This->headOfStreamPlaceHolder);
4749 if (This->ownerPropertyIndex)
4751 readSuccessful = StorageImpl_ReadProperty(
4752 This->parentStorage,
4753 This->ownerPropertyIndex,
4758 return chainProperty.startingBlock;
4763 return BLOCK_END_OF_CHAIN;
4766 /******************************************************************************
4767 * SmallBlockChainStream_GetNextBlockInChain
4769 * Returns the index of the next small block in this chain.
4772 * - BLOCK_END_OF_CHAIN: end of this chain
4773 * - BLOCK_UNUSED: small block 'blockIndex' is free
4775 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
4776 SmallBlockChainStream* This,
4778 ULONG* nextBlockInChain)
4780 ULARGE_INTEGER offsetOfBlockInDepot;
4785 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4787 offsetOfBlockInDepot.u.HighPart = 0;
4788 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4791 * Read those bytes in the buffer from the small block file.
4793 res = BlockChainStream_ReadAt(
4794 This->parentStorage->smallBlockDepotChain,
4795 offsetOfBlockInDepot,
4802 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
4809 /******************************************************************************
4810 * SmallBlockChainStream_SetNextBlockInChain
4812 * Writes the index of the next block of the specified block in the small
4814 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4815 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4817 static void SmallBlockChainStream_SetNextBlockInChain(
4818 SmallBlockChainStream* This,
4822 ULARGE_INTEGER offsetOfBlockInDepot;
4826 offsetOfBlockInDepot.u.HighPart = 0;
4827 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4829 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
4832 * Read those bytes in the buffer from the small block file.
4834 BlockChainStream_WriteAt(
4835 This->parentStorage->smallBlockDepotChain,
4836 offsetOfBlockInDepot,
4842 /******************************************************************************
4843 * SmallBlockChainStream_FreeBlock
4845 * Flag small block 'blockIndex' as free in the small block depot.
4847 static void SmallBlockChainStream_FreeBlock(
4848 SmallBlockChainStream* This,
4851 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4854 /******************************************************************************
4855 * SmallBlockChainStream_GetNextFreeBlock
4857 * Returns the index of a free small block. The small block depot will be
4858 * enlarged if necessary. The small block chain will also be enlarged if
4861 static ULONG SmallBlockChainStream_GetNextFreeBlock(
4862 SmallBlockChainStream* This)
4864 ULARGE_INTEGER offsetOfBlockInDepot;
4867 ULONG blockIndex = 0;
4868 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4870 ULONG smallBlocksPerBigBlock;
4872 offsetOfBlockInDepot.u.HighPart = 0;
4875 * Scan the small block depot for a free block
4877 while (nextBlockIndex != BLOCK_UNUSED)
4879 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4881 res = BlockChainStream_ReadAt(
4882 This->parentStorage->smallBlockDepotChain,
4883 offsetOfBlockInDepot,
4889 * If we run out of space for the small block depot, enlarge it
4893 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
4895 if (nextBlockIndex != BLOCK_UNUSED)
4901 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4903 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4904 ULONG nextBlock, newsbdIndex;
4905 BYTE smallBlockDepot[BIG_BLOCK_SIZE];
4907 nextBlock = sbdIndex;
4908 while (nextBlock != BLOCK_END_OF_CHAIN)
4910 sbdIndex = nextBlock;
4911 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
4914 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4915 if (sbdIndex != BLOCK_END_OF_CHAIN)
4916 StorageImpl_SetNextBlockInChain(
4917 This->parentStorage,
4921 StorageImpl_SetNextBlockInChain(
4922 This->parentStorage,
4924 BLOCK_END_OF_CHAIN);
4927 * Initialize all the small blocks to free
4929 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4930 StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
4935 * We have just created the small block depot.
4937 StgProperty rootProp;
4941 * Save it in the header
4943 This->parentStorage->smallBlockDepotStart = newsbdIndex;
4944 StorageImpl_SaveFileHeader(This->parentStorage);
4947 * And allocate the first big block that will contain small blocks
4950 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4952 StorageImpl_SetNextBlockInChain(
4953 This->parentStorage,
4955 BLOCK_END_OF_CHAIN);
4957 StorageImpl_ReadProperty(
4958 This->parentStorage,
4959 This->parentStorage->base.rootPropertySetIndex,
4962 rootProp.startingBlock = sbStartIndex;
4963 rootProp.size.u.HighPart = 0;
4964 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
4966 StorageImpl_WriteProperty(
4967 This->parentStorage,
4968 This->parentStorage->base.rootPropertySetIndex,
4972 StorageImpl_SaveFileHeader(This->parentStorage);
4976 smallBlocksPerBigBlock =
4977 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
4980 * Verify if we have to allocate big blocks to contain small blocks
4982 if (blockIndex % smallBlocksPerBigBlock == 0)
4984 StgProperty rootProp;
4985 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
4987 StorageImpl_ReadProperty(
4988 This->parentStorage,
4989 This->parentStorage->base.rootPropertySetIndex,
4992 if (rootProp.size.u.LowPart <
4993 (blocksRequired * This->parentStorage->bigBlockSize))
4995 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
4997 BlockChainStream_SetSize(
4998 This->parentStorage->smallBlockRootChain,
5001 StorageImpl_WriteProperty(
5002 This->parentStorage,
5003 This->parentStorage->base.rootPropertySetIndex,
5011 /******************************************************************************
5012 * SmallBlockChainStream_ReadAt
5014 * Reads a specified number of bytes from this chain at the specified offset.
5015 * bytesRead may be NULL.
5016 * Failure will be returned if the specified number of bytes has not been read.
5018 HRESULT SmallBlockChainStream_ReadAt(
5019 SmallBlockChainStream* This,
5020 ULARGE_INTEGER offset,
5026 ULARGE_INTEGER offsetInBigBlockFile;
5027 ULONG blockNoInSequence =
5028 offset.u.LowPart / This->parentStorage->smallBlockSize;
5030 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5031 ULONG bytesToReadInBuffer;
5033 ULONG bytesReadFromBigBlockFile;
5037 * This should never happen on a small block file.
5039 assert(offset.u.HighPart==0);
5042 * Find the first block in the stream that contains part of the buffer.
5044 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5046 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5048 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5051 blockNoInSequence--;
5055 * Start reading the buffer.
5058 bufferWalker = buffer;
5060 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5063 * Calculate how many bytes we can copy from this small block.
5065 bytesToReadInBuffer =
5066 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5069 * Calculate the offset of the small block in the small block file.
5071 offsetInBigBlockFile.u.HighPart = 0;
5072 offsetInBigBlockFile.u.LowPart =
5073 blockIndex * This->parentStorage->smallBlockSize;
5075 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5078 * Read those bytes in the buffer from the small block file.
5079 * The small block has already been identified so it shouldn't fail
5080 * unless the file is corrupt.
5082 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5083 offsetInBigBlockFile,
5084 bytesToReadInBuffer,
5086 &bytesReadFromBigBlockFile);
5092 * Step to the next big block.
5094 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5096 return STG_E_DOCFILECORRUPT;
5098 bufferWalker += bytesReadFromBigBlockFile;
5099 size -= bytesReadFromBigBlockFile;
5100 *bytesRead += bytesReadFromBigBlockFile;
5101 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5104 return (size == 0) ? S_OK : STG_E_READFAULT;
5107 /******************************************************************************
5108 * SmallBlockChainStream_WriteAt
5110 * Writes the specified number of bytes to this chain at the specified offset.
5111 * Will fail if not all specified number of bytes have been written.
5113 HRESULT SmallBlockChainStream_WriteAt(
5114 SmallBlockChainStream* This,
5115 ULARGE_INTEGER offset,
5118 ULONG* bytesWritten)
5120 ULARGE_INTEGER offsetInBigBlockFile;
5121 ULONG blockNoInSequence =
5122 offset.u.LowPart / This->parentStorage->smallBlockSize;
5124 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5125 ULONG bytesToWriteInBuffer;
5127 ULONG bytesWrittenToBigBlockFile;
5128 const BYTE* bufferWalker;
5132 * This should never happen on a small block file.
5134 assert(offset.u.HighPart==0);
5137 * Find the first block in the stream that contains part of the buffer.
5139 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5141 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5143 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5144 return STG_E_DOCFILECORRUPT;
5145 blockNoInSequence--;
5149 * Start writing the buffer.
5152 bufferWalker = buffer;
5153 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5156 * Calculate how many bytes we can copy to this small block.
5158 bytesToWriteInBuffer =
5159 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5162 * Calculate the offset of the small block in the small block file.
5164 offsetInBigBlockFile.u.HighPart = 0;
5165 offsetInBigBlockFile.u.LowPart =
5166 blockIndex * This->parentStorage->smallBlockSize;
5168 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5171 * Write those bytes in the buffer to the small block file.
5173 res = BlockChainStream_WriteAt(
5174 This->parentStorage->smallBlockRootChain,
5175 offsetInBigBlockFile,
5176 bytesToWriteInBuffer,
5178 &bytesWrittenToBigBlockFile);
5183 * Step to the next big block.
5185 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5188 bufferWalker += bytesWrittenToBigBlockFile;
5189 size -= bytesWrittenToBigBlockFile;
5190 *bytesWritten += bytesWrittenToBigBlockFile;
5191 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
5194 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5197 /******************************************************************************
5198 * SmallBlockChainStream_Shrink
5200 * Shrinks this chain in the small block depot.
5202 static BOOL SmallBlockChainStream_Shrink(
5203 SmallBlockChainStream* This,
5204 ULARGE_INTEGER newSize)
5206 ULONG blockIndex, extraBlock;
5210 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5212 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5215 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5218 * Go to the new end of chain
5220 while (count < numBlocks)
5222 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5229 * If the count is 0, we have a special case, the head of the chain was
5234 StgProperty chainProp;
5236 StorageImpl_ReadProperty(This->parentStorage,
5237 This->ownerPropertyIndex,
5240 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5242 StorageImpl_WriteProperty(This->parentStorage,
5243 This->ownerPropertyIndex,
5247 * We start freeing the chain at the head block.
5249 extraBlock = blockIndex;
5253 /* Get the next block before marking the new end */
5254 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5258 /* Mark the new end of chain */
5259 SmallBlockChainStream_SetNextBlockInChain(
5262 BLOCK_END_OF_CHAIN);
5266 * Mark the extra blocks as free
5268 while (extraBlock != BLOCK_END_OF_CHAIN)
5270 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5273 SmallBlockChainStream_FreeBlock(This, extraBlock);
5274 extraBlock = blockIndex;
5280 /******************************************************************************
5281 * SmallBlockChainStream_Enlarge
5283 * Grows this chain in the small block depot.
5285 static BOOL SmallBlockChainStream_Enlarge(
5286 SmallBlockChainStream* This,
5287 ULARGE_INTEGER newSize)
5289 ULONG blockIndex, currentBlock;
5291 ULONG oldNumBlocks = 0;
5293 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5296 * Empty chain. Create the head.
5298 if (blockIndex == BLOCK_END_OF_CHAIN)
5300 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5301 SmallBlockChainStream_SetNextBlockInChain(
5304 BLOCK_END_OF_CHAIN);
5306 if (This->headOfStreamPlaceHolder != NULL)
5308 *(This->headOfStreamPlaceHolder) = blockIndex;
5312 StgProperty chainProp;
5314 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5317 chainProp.startingBlock = blockIndex;
5319 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5324 currentBlock = blockIndex;
5327 * Figure out how many blocks are needed to contain this stream
5329 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5331 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5335 * Go to the current end of chain
5337 while (blockIndex != BLOCK_END_OF_CHAIN)
5340 currentBlock = blockIndex;
5341 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5346 * Add new blocks to the chain
5348 while (oldNumBlocks < newNumBlocks)
5350 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5351 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5353 SmallBlockChainStream_SetNextBlockInChain(
5356 BLOCK_END_OF_CHAIN);
5358 currentBlock = blockIndex;
5365 /******************************************************************************
5366 * SmallBlockChainStream_SetSize
5368 * Sets the size of this stream.
5369 * The file will grow if we grow the chain.
5371 * TODO: Free the actual blocks in the file when we shrink the chain.
5372 * Currently, the blocks are still in the file. So the file size
5373 * doesn't shrink even if we shrink streams.
5375 BOOL SmallBlockChainStream_SetSize(
5376 SmallBlockChainStream* This,
5377 ULARGE_INTEGER newSize)
5379 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5381 if (newSize.u.LowPart == size.u.LowPart)
5384 if (newSize.u.LowPart < size.u.LowPart)
5386 SmallBlockChainStream_Shrink(This, newSize);
5390 SmallBlockChainStream_Enlarge(This, newSize);
5396 /******************************************************************************
5397 * SmallBlockChainStream_GetCount
5399 * Returns the number of small blocks that comprises this chain.
5400 * This is not the size of the stream as the last block may not be full!
5403 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5408 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5410 while(blockIndex != BLOCK_END_OF_CHAIN)
5414 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
5415 blockIndex, &blockIndex)))
5422 /******************************************************************************
5423 * SmallBlockChainStream_GetSize
5425 * Returns the size of this chain.
5427 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5429 StgProperty chainProperty;
5431 if(This->headOfStreamPlaceHolder != NULL)
5433 ULARGE_INTEGER result;
5434 result.u.HighPart = 0;
5436 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
5437 This->parentStorage->smallBlockSize;
5442 StorageImpl_ReadProperty(
5443 This->parentStorage,
5444 This->ownerPropertyIndex,
5447 return chainProperty.size;
5450 /******************************************************************************
5451 * StgCreateDocfile [OLE32.@]
5452 * Creates a new compound file storage object
5455 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5456 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5457 * reserved [ ?] unused?, usually 0
5458 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5461 * S_OK if the file was successfully created
5462 * some STG_E_ value if error
5464 * if pwcsName is NULL, create file with new unique name
5465 * the function can returns
5466 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5469 HRESULT WINAPI StgCreateDocfile(
5473 IStorage **ppstgOpen)
5475 StorageImpl* newStorage = 0;
5476 HANDLE hFile = INVALID_HANDLE_VALUE;
5477 HRESULT hr = STG_E_INVALIDFLAG;
5481 DWORD fileAttributes;
5482 WCHAR tempFileName[MAX_PATH];
5484 TRACE("(%s, %x, %d, %p)\n",
5485 debugstr_w(pwcsName), grfMode,
5486 reserved, ppstgOpen);
5489 return STG_E_INVALIDPOINTER;
5491 return STG_E_INVALIDPARAMETER;
5493 /* if no share mode given then DENY_NONE is the default */
5494 if (STGM_SHARE_MODE(grfMode) == 0)
5495 grfMode |= STGM_SHARE_DENY_NONE;
5497 if ( FAILED( validateSTGM(grfMode) ))
5500 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5501 switch(STGM_ACCESS_MODE(grfMode))
5504 case STGM_READWRITE:
5510 /* in direct mode, can only use SHARE_EXCLUSIVE */
5511 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5514 /* but in transacted mode, any share mode is valid */
5517 * Generate a unique name.
5521 WCHAR tempPath[MAX_PATH];
5522 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5524 memset(tempPath, 0, sizeof(tempPath));
5525 memset(tempFileName, 0, sizeof(tempFileName));
5527 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5530 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5531 pwcsName = tempFileName;
5534 hr = STG_E_INSUFFICIENTMEMORY;
5538 creationMode = TRUNCATE_EXISTING;
5542 creationMode = GetCreationModeFromSTGM(grfMode);
5546 * Interpret the STGM value grfMode
5548 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
5549 accessMode = GetAccessModeFromSTGM(grfMode);
5551 if (grfMode & STGM_DELETEONRELEASE)
5552 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5554 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5556 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
5557 FIXME("Storage share mode not implemented.\n");
5559 if (grfMode & STGM_TRANSACTED)
5560 FIXME("Transacted mode not implemented.\n");
5564 hFile = CreateFileW(pwcsName,
5572 if (hFile == INVALID_HANDLE_VALUE)
5574 if(GetLastError() == ERROR_FILE_EXISTS)
5575 hr = STG_E_FILEALREADYEXISTS;
5582 * Allocate and initialize the new IStorage32object.
5584 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5586 if (newStorage == 0)
5588 hr = STG_E_INSUFFICIENTMEMORY;
5592 hr = StorageImpl_Construct(
5603 HeapFree(GetProcessHeap(), 0, newStorage);
5608 * Get an "out" pointer for the caller.
5610 hr = StorageBaseImpl_QueryInterface(
5611 (IStorage*)newStorage,
5615 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
5620 /******************************************************************************
5621 * StgCreateStorageEx [OLE32.@]
5623 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5625 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5626 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5628 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5630 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5631 return STG_E_INVALIDPARAMETER;
5634 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5636 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5637 return STG_E_INVALIDPARAMETER;
5640 if (stgfmt == STGFMT_FILE)
5642 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5643 return STG_E_INVALIDPARAMETER;
5646 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5648 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5649 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5652 ERR("Invalid stgfmt argument\n");
5653 return STG_E_INVALIDPARAMETER;
5656 /******************************************************************************
5657 * StgCreatePropSetStg [OLE32.@]
5659 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5660 IPropertySetStorage **ppPropSetStg)
5664 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
5666 hr = STG_E_INVALIDPARAMETER;
5668 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5669 (void**)ppPropSetStg);
5673 /******************************************************************************
5674 * StgOpenStorageEx [OLE32.@]
5676 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5678 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5679 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5681 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5683 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5684 return STG_E_INVALIDPARAMETER;
5690 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5691 return STG_E_INVALIDPARAMETER;
5693 case STGFMT_STORAGE:
5696 case STGFMT_DOCFILE:
5697 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
5699 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5700 return STG_E_INVALIDPARAMETER;
5702 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5706 WARN("STGFMT_ANY assuming storage\n");
5710 return STG_E_INVALIDPARAMETER;
5713 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
5717 /******************************************************************************
5718 * StgOpenStorage [OLE32.@]
5720 HRESULT WINAPI StgOpenStorage(
5721 const OLECHAR *pwcsName,
5722 IStorage *pstgPriority,
5726 IStorage **ppstgOpen)
5728 StorageImpl* newStorage = 0;
5733 WCHAR fullname[MAX_PATH];
5735 TRACE("(%s, %p, %x, %p, %d, %p)\n",
5736 debugstr_w(pwcsName), pstgPriority, grfMode,
5737 snbExclude, reserved, ppstgOpen);
5741 hr = STG_E_INVALIDNAME;
5747 hr = STG_E_INVALIDPOINTER;
5753 hr = STG_E_INVALIDPARAMETER;
5757 if (grfMode & STGM_PRIORITY)
5759 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
5760 return STG_E_INVALIDFLAG;
5761 if (grfMode & STGM_DELETEONRELEASE)
5762 return STG_E_INVALIDFUNCTION;
5763 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
5764 return STG_E_INVALIDFLAG;
5765 grfMode &= ~0xf0; /* remove the existing sharing mode */
5766 grfMode |= STGM_SHARE_DENY_NONE;
5768 /* STGM_PRIORITY stops other IStorage objects on the same file from
5769 * committing until the STGM_PRIORITY IStorage is closed. it also
5770 * stops non-transacted mode StgOpenStorage calls with write access from
5771 * succeeding. obviously, both of these cannot be achieved through just
5772 * file share flags */
5773 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5777 * Validate the sharing mode
5779 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
5780 switch(STGM_SHARE_MODE(grfMode))
5782 case STGM_SHARE_EXCLUSIVE:
5783 case STGM_SHARE_DENY_WRITE:
5786 hr = STG_E_INVALIDFLAG;
5790 if ( FAILED( validateSTGM(grfMode) ) ||
5791 (grfMode&STGM_CREATE))
5793 hr = STG_E_INVALIDFLAG;
5797 /* shared reading requires transacted mode */
5798 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5799 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
5800 !(grfMode&STGM_TRANSACTED) )
5802 hr = STG_E_INVALIDFLAG;
5807 * Interpret the STGM value grfMode
5809 shareMode = GetShareModeFromSTGM(grfMode);
5810 accessMode = GetAccessModeFromSTGM(grfMode);
5814 hFile = CreateFileW( pwcsName,
5819 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5822 if (hFile==INVALID_HANDLE_VALUE)
5824 DWORD last_error = GetLastError();
5830 case ERROR_FILE_NOT_FOUND:
5831 hr = STG_E_FILENOTFOUND;
5834 case ERROR_PATH_NOT_FOUND:
5835 hr = STG_E_PATHNOTFOUND;
5838 case ERROR_ACCESS_DENIED:
5839 case ERROR_WRITE_PROTECT:
5840 hr = STG_E_ACCESSDENIED;
5843 case ERROR_SHARING_VIOLATION:
5844 hr = STG_E_SHAREVIOLATION;
5855 * Refuse to open the file if it's too small to be a structured storage file
5856 * FIXME: verify the file when reading instead of here
5858 if (GetFileSize(hFile, NULL) < 0x100)
5861 hr = STG_E_FILEALREADYEXISTS;
5866 * Allocate and initialize the new IStorage32object.
5868 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5870 if (newStorage == 0)
5872 hr = STG_E_INSUFFICIENTMEMORY;
5876 /* Initialize the storage */
5877 hr = StorageImpl_Construct(
5888 HeapFree(GetProcessHeap(), 0, newStorage);
5890 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5892 if(hr == STG_E_INVALIDHEADER)
5893 hr = STG_E_FILEALREADYEXISTS;
5897 /* prepare the file name string given in lieu of the root property name */
5898 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
5899 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
5900 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
5903 * Get an "out" pointer for the caller.
5905 hr = StorageBaseImpl_QueryInterface(
5906 (IStorage*)newStorage,
5911 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
5915 /******************************************************************************
5916 * StgCreateDocfileOnILockBytes [OLE32.@]
5918 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5922 IStorage** ppstgOpen)
5924 StorageImpl* newStorage = 0;
5927 if ((ppstgOpen == 0) || (plkbyt == 0))
5928 return STG_E_INVALIDPOINTER;
5931 * Allocate and initialize the new IStorage object.
5933 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5935 if (newStorage == 0)
5936 return STG_E_INSUFFICIENTMEMORY;
5938 hr = StorageImpl_Construct(
5949 HeapFree(GetProcessHeap(), 0, newStorage);
5954 * Get an "out" pointer for the caller.
5956 hr = StorageBaseImpl_QueryInterface(
5957 (IStorage*)newStorage,
5964 /******************************************************************************
5965 * StgOpenStorageOnILockBytes [OLE32.@]
5967 HRESULT WINAPI StgOpenStorageOnILockBytes(
5969 IStorage *pstgPriority,
5973 IStorage **ppstgOpen)
5975 StorageImpl* newStorage = 0;
5978 if ((plkbyt == 0) || (ppstgOpen == 0))
5979 return STG_E_INVALIDPOINTER;
5981 if ( FAILED( validateSTGM(grfMode) ))
5982 return STG_E_INVALIDFLAG;
5987 * Allocate and initialize the new IStorage object.
5989 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5991 if (newStorage == 0)
5992 return STG_E_INSUFFICIENTMEMORY;
5994 hr = StorageImpl_Construct(
6005 HeapFree(GetProcessHeap(), 0, newStorage);
6010 * Get an "out" pointer for the caller.
6012 hr = StorageBaseImpl_QueryInterface(
6013 (IStorage*)newStorage,
6020 /******************************************************************************
6021 * StgSetTimes [ole32.@]
6022 * StgSetTimes [OLE32.@]
6026 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6027 FILETIME const *patime, FILETIME const *pmtime)
6029 IStorage *stg = NULL;
6032 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6034 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6038 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6039 IStorage_Release(stg);
6045 /******************************************************************************
6046 * StgIsStorageILockBytes [OLE32.@]
6048 * Determines if the ILockBytes contains a storage object.
6050 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6053 ULARGE_INTEGER offset;
6055 offset.u.HighPart = 0;
6056 offset.u.LowPart = 0;
6058 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6060 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6066 /******************************************************************************
6067 * WriteClassStg [OLE32.@]
6069 * This method will store the specified CLSID in the specified storage object
6071 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6076 return E_INVALIDARG;
6079 return STG_E_INVALIDPOINTER;
6081 hRes = IStorage_SetClass(pStg, rclsid);
6086 /***********************************************************************
6087 * ReadClassStg (OLE32.@)
6089 * This method reads the CLSID previously written to a storage object with
6090 * the WriteClassStg.
6093 * pstg [I] IStorage pointer
6094 * pclsid [O] Pointer to where the CLSID is written
6098 * Failure: HRESULT code.
6100 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6105 TRACE("(%p, %p)\n", pstg, pclsid);
6107 if(!pstg || !pclsid)
6108 return E_INVALIDARG;
6111 * read a STATSTG structure (contains the clsid) from the storage
6113 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
6116 *pclsid=pstatstg.clsid;
6121 /***********************************************************************
6122 * OleLoadFromStream (OLE32.@)
6124 * This function loads an object from stream
6126 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6130 LPPERSISTSTREAM xstm;
6132 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6134 res=ReadClassStm(pStm,&clsid);
6137 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6140 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6142 IUnknown_Release((IUnknown*)*ppvObj);
6145 res=IPersistStream_Load(xstm,pStm);
6146 IPersistStream_Release(xstm);
6147 /* FIXME: all refcounts ok at this point? I think they should be:
6150 * xstm : 0 (released)
6155 /***********************************************************************
6156 * OleSaveToStream (OLE32.@)
6158 * This function saves an object with the IPersistStream interface on it
6159 * to the specified stream.
6161 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6167 TRACE("(%p,%p)\n",pPStm,pStm);
6169 res=IPersistStream_GetClassID(pPStm,&clsid);
6171 if (SUCCEEDED(res)){
6173 res=WriteClassStm(pStm,&clsid);
6177 res=IPersistStream_Save(pPStm,pStm,TRUE);
6180 TRACE("Finished Save\n");
6184 /****************************************************************************
6185 * This method validate a STGM parameter that can contain the values below
6187 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6188 * The stgm values contained in 0xffff0000 are bitmasks.
6190 * STGM_DIRECT 0x00000000
6191 * STGM_TRANSACTED 0x00010000
6192 * STGM_SIMPLE 0x08000000
6194 * STGM_READ 0x00000000
6195 * STGM_WRITE 0x00000001
6196 * STGM_READWRITE 0x00000002
6198 * STGM_SHARE_DENY_NONE 0x00000040
6199 * STGM_SHARE_DENY_READ 0x00000030
6200 * STGM_SHARE_DENY_WRITE 0x00000020
6201 * STGM_SHARE_EXCLUSIVE 0x00000010
6203 * STGM_PRIORITY 0x00040000
6204 * STGM_DELETEONRELEASE 0x04000000
6206 * STGM_CREATE 0x00001000
6207 * STGM_CONVERT 0x00020000
6208 * STGM_FAILIFTHERE 0x00000000
6210 * STGM_NOSCRATCH 0x00100000
6211 * STGM_NOSNAPSHOT 0x00200000
6213 static HRESULT validateSTGM(DWORD stgm)
6215 DWORD access = STGM_ACCESS_MODE(stgm);
6216 DWORD share = STGM_SHARE_MODE(stgm);
6217 DWORD create = STGM_CREATE_MODE(stgm);
6219 if (stgm&~STGM_KNOWN_FLAGS)
6221 ERR("unknown flags %08x\n", stgm);
6229 case STGM_READWRITE:
6237 case STGM_SHARE_DENY_NONE:
6238 case STGM_SHARE_DENY_READ:
6239 case STGM_SHARE_DENY_WRITE:
6240 case STGM_SHARE_EXCLUSIVE:
6249 case STGM_FAILIFTHERE:
6256 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6258 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6262 * STGM_CREATE | STGM_CONVERT
6263 * if both are false, STGM_FAILIFTHERE is set to TRUE
6265 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6269 * STGM_NOSCRATCH requires STGM_TRANSACTED
6271 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6275 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6276 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6278 if ( (stgm & STGM_NOSNAPSHOT) &&
6279 (!(stgm & STGM_TRANSACTED) ||
6280 share == STGM_SHARE_EXCLUSIVE ||
6281 share == STGM_SHARE_DENY_WRITE) )
6287 /****************************************************************************
6288 * GetShareModeFromSTGM
6290 * This method will return a share mode flag from a STGM value.
6291 * The STGM value is assumed valid.
6293 static DWORD GetShareModeFromSTGM(DWORD stgm)
6295 switch (STGM_SHARE_MODE(stgm))
6297 case STGM_SHARE_DENY_NONE:
6298 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6299 case STGM_SHARE_DENY_READ:
6300 return FILE_SHARE_WRITE;
6301 case STGM_SHARE_DENY_WRITE:
6302 return FILE_SHARE_READ;
6303 case STGM_SHARE_EXCLUSIVE:
6306 ERR("Invalid share mode!\n");
6311 /****************************************************************************
6312 * GetAccessModeFromSTGM
6314 * This method will return an access mode flag from a STGM value.
6315 * The STGM value is assumed valid.
6317 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6319 switch (STGM_ACCESS_MODE(stgm))
6322 return GENERIC_READ;
6324 case STGM_READWRITE:
6325 return GENERIC_READ | GENERIC_WRITE;
6327 ERR("Invalid access mode!\n");
6332 /****************************************************************************
6333 * GetCreationModeFromSTGM
6335 * This method will return a creation mode flag from a STGM value.
6336 * The STGM value is assumed valid.
6338 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6340 switch(STGM_CREATE_MODE(stgm))
6343 return CREATE_ALWAYS;
6345 FIXME("STGM_CONVERT not implemented!\n");
6347 case STGM_FAILIFTHERE:
6350 ERR("Invalid create mode!\n");
6356 /*************************************************************************
6357 * OLECONVERT_LoadOLE10 [Internal]
6359 * Loads the OLE10 STREAM to memory
6362 * pOleStream [I] The OLESTREAM
6363 * pData [I] Data Structure for the OLESTREAM Data
6367 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6368 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
6371 * This function is used by OleConvertOLESTREAMToIStorage only.
6373 * Memory allocated for pData must be freed by the caller
6375 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6378 HRESULT hRes = S_OK;
6382 pData->pData = NULL;
6383 pData->pstrOleObjFileName = NULL;
6385 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6388 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6389 if(dwSize != sizeof(pData->dwOleID))
6391 hRes = CONVERT10_E_OLESTREAM_GET;
6393 else if(pData->dwOleID != OLESTREAM_ID)
6395 hRes = CONVERT10_E_OLESTREAM_FMT;
6406 /* Get the TypeID... more info needed for this field */
6407 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6408 if(dwSize != sizeof(pData->dwTypeID))
6410 hRes = CONVERT10_E_OLESTREAM_GET;
6415 if(pData->dwTypeID != 0)
6417 /* Get the length of the OleTypeName */
6418 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6419 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6421 hRes = CONVERT10_E_OLESTREAM_GET;
6426 if(pData->dwOleTypeNameLength > 0)
6428 /* Get the OleTypeName */
6429 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
6430 if(dwSize != pData->dwOleTypeNameLength)
6432 hRes = CONVERT10_E_OLESTREAM_GET;
6438 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6439 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6441 hRes = CONVERT10_E_OLESTREAM_GET;
6445 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6446 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6447 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6448 if(pData->pstrOleObjFileName)
6450 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
6451 if(dwSize != pData->dwOleObjFileNameLength)
6453 hRes = CONVERT10_E_OLESTREAM_GET;
6457 hRes = CONVERT10_E_OLESTREAM_GET;
6462 /* Get the Width of the Metafile */
6463 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6464 if(dwSize != sizeof(pData->dwMetaFileWidth))
6466 hRes = CONVERT10_E_OLESTREAM_GET;
6470 /* Get the Height of the Metafile */
6471 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6472 if(dwSize != sizeof(pData->dwMetaFileHeight))
6474 hRes = CONVERT10_E_OLESTREAM_GET;
6480 /* Get the Length of the Data */
6481 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6482 if(dwSize != sizeof(pData->dwDataLength))
6484 hRes = CONVERT10_E_OLESTREAM_GET;
6488 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
6490 if(!bStrem1) /* if it is a second OLE stream data */
6492 pData->dwDataLength -= 8;
6493 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
6494 if(dwSize != sizeof(pData->strUnknown))
6496 hRes = CONVERT10_E_OLESTREAM_GET;
6502 if(pData->dwDataLength > 0)
6504 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6506 /* Get Data (ex. IStorage, Metafile, or BMP) */
6509 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6510 if(dwSize != pData->dwDataLength)
6512 hRes = CONVERT10_E_OLESTREAM_GET;
6517 hRes = CONVERT10_E_OLESTREAM_GET;
6526 /*************************************************************************
6527 * OLECONVERT_SaveOLE10 [Internal]
6529 * Saves the OLE10 STREAM From memory
6532 * pData [I] Data Structure for the OLESTREAM Data
6533 * pOleStream [I] The OLESTREAM to save
6537 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6540 * This function is used by OleConvertIStorageToOLESTREAM only.
6543 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6546 HRESULT hRes = S_OK;
6550 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6551 if(dwSize != sizeof(pData->dwOleID))
6553 hRes = CONVERT10_E_OLESTREAM_PUT;
6558 /* Set the TypeID */
6559 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6560 if(dwSize != sizeof(pData->dwTypeID))
6562 hRes = CONVERT10_E_OLESTREAM_PUT;
6566 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6568 /* Set the Length of the OleTypeName */
6569 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6570 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6572 hRes = CONVERT10_E_OLESTREAM_PUT;
6577 if(pData->dwOleTypeNameLength > 0)
6579 /* Set the OleTypeName */
6580 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
6581 if(dwSize != pData->dwOleTypeNameLength)
6583 hRes = CONVERT10_E_OLESTREAM_PUT;
6590 /* Set the width of the Metafile */
6591 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6592 if(dwSize != sizeof(pData->dwMetaFileWidth))
6594 hRes = CONVERT10_E_OLESTREAM_PUT;
6600 /* Set the height of the Metafile */
6601 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6602 if(dwSize != sizeof(pData->dwMetaFileHeight))
6604 hRes = CONVERT10_E_OLESTREAM_PUT;
6610 /* Set the length of the Data */
6611 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6612 if(dwSize != sizeof(pData->dwDataLength))
6614 hRes = CONVERT10_E_OLESTREAM_PUT;
6620 if(pData->dwDataLength > 0)
6622 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6623 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6624 if(dwSize != pData->dwDataLength)
6626 hRes = CONVERT10_E_OLESTREAM_PUT;
6634 /*************************************************************************
6635 * OLECONVERT_GetOLE20FromOLE10[Internal]
6637 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6638 * opens it, and copies the content to the dest IStorage for
6639 * OleConvertOLESTREAMToIStorage
6643 * pDestStorage [I] The IStorage to copy the data to
6644 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6645 * nBufferLength [I] The size of the buffer
6654 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
6658 IStorage *pTempStorage;
6659 DWORD dwNumOfBytesWritten;
6660 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6661 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6663 /* Create a temp File */
6664 GetTempPathW(MAX_PATH, wstrTempDir);
6665 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6666 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6668 if(hFile != INVALID_HANDLE_VALUE)
6670 /* Write IStorage Data to File */
6671 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6674 /* Open and copy temp storage to the Dest Storage */
6675 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6678 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6679 IStorage_Release(pTempStorage);
6681 DeleteFileW(wstrTempFile);
6686 /*************************************************************************
6687 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6689 * Saves the OLE10 STREAM From memory
6692 * pStorage [I] The Src IStorage to copy
6693 * pData [I] The Dest Memory to write to.
6696 * The size in bytes allocated for pData
6699 * Memory allocated for pData must be freed by the caller
6701 * Used by OleConvertIStorageToOLESTREAM only.
6704 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6708 DWORD nDataLength = 0;
6709 IStorage *pTempStorage;
6710 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6711 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6715 /* Create temp Storage */
6716 GetTempPathW(MAX_PATH, wstrTempDir);
6717 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6718 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6722 /* Copy Src Storage to the Temp Storage */
6723 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6724 IStorage_Release(pTempStorage);
6726 /* Open Temp Storage as a file and copy to memory */
6727 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6728 if(hFile != INVALID_HANDLE_VALUE)
6730 nDataLength = GetFileSize(hFile, NULL);
6731 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6732 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6735 DeleteFileW(wstrTempFile);
6740 /*************************************************************************
6741 * OLECONVERT_CreateOleStream [Internal]
6743 * Creates the "\001OLE" stream in the IStorage if necessary.
6746 * pStorage [I] Dest storage to create the stream in
6752 * This function is used by OleConvertOLESTREAMToIStorage only.
6754 * This stream is still unknown, MS Word seems to have extra data
6755 * but since the data is stored in the OLESTREAM there should be
6756 * no need to recreate the stream. If the stream is manually
6757 * deleted it will create it with this default data.
6760 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6764 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6765 BYTE pOleStreamHeader [] =
6767 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6768 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6769 0x00, 0x00, 0x00, 0x00
6772 /* Create stream if not present */
6773 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6774 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6778 /* Write default Data */
6779 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6780 IStream_Release(pStream);
6784 /* write a string to a stream, preceded by its length */
6785 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6792 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6793 r = IStream_Write( stm, &len, sizeof(len), NULL);
6798 str = CoTaskMemAlloc( len );
6799 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6800 r = IStream_Write( stm, str, len, NULL);
6801 CoTaskMemFree( str );
6805 /* read a string preceded by its length from a stream */
6806 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6809 DWORD len, count = 0;
6813 r = IStream_Read( stm, &len, sizeof(len), &count );
6816 if( count != sizeof(len) )
6817 return E_OUTOFMEMORY;
6819 TRACE("%d bytes\n",len);
6821 str = CoTaskMemAlloc( len );
6823 return E_OUTOFMEMORY;
6825 r = IStream_Read( stm, str, len, &count );
6830 CoTaskMemFree( str );
6831 return E_OUTOFMEMORY;
6834 TRACE("Read string %s\n",debugstr_an(str,len));
6836 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
6837 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
6839 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
6840 CoTaskMemFree( str );
6848 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
6849 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
6853 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6855 static const BYTE unknown1[12] =
6856 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6857 0xFF, 0xFF, 0xFF, 0xFF};
6858 static const BYTE unknown2[16] =
6859 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6860 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6862 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
6863 debugstr_w(lpszUserType), debugstr_w(szClipName),
6864 debugstr_w(szProgIDName));
6866 /* Create a CompObj stream */
6867 r = IStorage_CreateStream(pstg, szwStreamName,
6868 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
6872 /* Write CompObj Structure to stream */
6873 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
6875 if( SUCCEEDED( r ) )
6876 r = WriteClassStm( pstm, clsid );
6878 if( SUCCEEDED( r ) )
6879 r = STREAM_WriteString( pstm, lpszUserType );
6880 if( SUCCEEDED( r ) )
6881 r = STREAM_WriteString( pstm, szClipName );
6882 if( SUCCEEDED( r ) )
6883 r = STREAM_WriteString( pstm, szProgIDName );
6884 if( SUCCEEDED( r ) )
6885 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
6887 IStream_Release( pstm );
6892 /***********************************************************************
6893 * WriteFmtUserTypeStg (OLE32.@)
6895 HRESULT WINAPI WriteFmtUserTypeStg(
6896 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
6899 WCHAR szwClipName[0x40];
6900 CLSID clsid = CLSID_NULL;
6901 LPWSTR wstrProgID = NULL;
6904 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
6906 /* get the clipboard format name */
6907 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
6910 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
6912 /* FIXME: There's room to save a CLSID and its ProgID, but
6913 the CLSID is not looked up in the registry and in all the
6914 tests I wrote it was CLSID_NULL. Where does it come from?
6917 /* get the real program ID. This may fail, but that's fine */
6918 ProgIDFromCLSID(&clsid, &wstrProgID);
6920 TRACE("progid is %s\n",debugstr_w(wstrProgID));
6922 r = STORAGE_WriteCompObj( pstg, &clsid,
6923 lpszUserType, szwClipName, wstrProgID );
6925 CoTaskMemFree(wstrProgID);
6931 /******************************************************************************
6932 * ReadFmtUserTypeStg [OLE32.@]
6934 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
6938 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
6939 unsigned char unknown1[12];
6940 unsigned char unknown2[16];
6942 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
6945 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
6947 r = IStorage_OpenStream( pstg, szCompObj, NULL,
6948 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
6951 WARN("Failed to open stream r = %08x\n", r);
6955 /* read the various parts of the structure */
6956 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
6957 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
6959 r = ReadClassStm( stm, &clsid );
6963 r = STREAM_ReadString( stm, &szCLSIDName );
6967 r = STREAM_ReadString( stm, &szOleTypeName );
6971 r = STREAM_ReadString( stm, &szProgIDName );
6975 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
6976 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
6979 /* ok, success... now we just need to store what we found */
6981 *pcf = RegisterClipboardFormatW( szOleTypeName );
6982 CoTaskMemFree( szOleTypeName );
6984 if( lplpszUserType )
6985 *lplpszUserType = szCLSIDName;
6986 CoTaskMemFree( szProgIDName );
6989 IStream_Release( stm );
6995 /*************************************************************************
6996 * OLECONVERT_CreateCompObjStream [Internal]
6998 * Creates a "\001CompObj" is the destination IStorage if necessary.
7001 * pStorage [I] The dest IStorage to create the CompObj Stream
7003 * strOleTypeName [I] The ProgID
7007 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7010 * This function is used by OleConvertOLESTREAMToIStorage only.
7012 * The stream data is stored in the OLESTREAM and there should be
7013 * no need to recreate the stream. If the stream is manually
7014 * deleted it will attempt to create it by querying the registry.
7018 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7021 HRESULT hStorageRes, hRes = S_OK;
7022 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7023 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7024 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7026 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7027 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7029 /* Initialize the CompObj structure */
7030 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7031 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
7032 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
7035 /* Create a CompObj stream if it doesn't exist */
7036 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7037 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7038 if(hStorageRes == S_OK)
7040 /* copy the OleTypeName to the compobj struct */
7041 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7042 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7044 /* copy the OleTypeName to the compobj struct */
7045 /* Note: in the test made, these were Identical */
7046 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7047 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7050 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7051 bufferW, OLESTREAM_MAX_STR_LEN );
7052 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7058 /* Get the CLSID Default Name from the Registry */
7059 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7060 if(hErr == ERROR_SUCCESS)
7062 char strTemp[OLESTREAM_MAX_STR_LEN];
7063 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7064 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7065 if(hErr == ERROR_SUCCESS)
7067 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7073 /* Write CompObj Structure to stream */
7074 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7076 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7078 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7079 if(IStorageCompObj.dwCLSIDNameLength > 0)
7081 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7083 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7084 if(IStorageCompObj.dwOleTypeNameLength > 0)
7086 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7088 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7089 if(IStorageCompObj.dwProgIDNameLength > 0)
7091 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7093 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7094 IStream_Release(pStream);
7100 /*************************************************************************
7101 * OLECONVERT_CreateOlePresStream[Internal]
7103 * Creates the "\002OlePres000" Stream with the Metafile data
7106 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7107 * dwExtentX [I] Width of the Metafile
7108 * dwExtentY [I] Height of the Metafile
7109 * pData [I] Metafile data
7110 * dwDataLength [I] Size of the Metafile data
7114 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7117 * This function is used by OleConvertOLESTREAMToIStorage only.
7120 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7124 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7125 BYTE pOlePresStreamHeader [] =
7127 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7128 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7129 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7130 0x00, 0x00, 0x00, 0x00
7133 BYTE pOlePresStreamHeaderEmpty [] =
7135 0x00, 0x00, 0x00, 0x00,
7136 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7137 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7138 0x00, 0x00, 0x00, 0x00
7141 /* Create the OlePres000 Stream */
7142 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7143 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7148 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7150 memset(&OlePres, 0, sizeof(OlePres));
7151 /* Do we have any metafile data to save */
7152 if(dwDataLength > 0)
7154 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7155 nHeaderSize = sizeof(pOlePresStreamHeader);
7159 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7160 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7162 /* Set width and height of the metafile */
7163 OlePres.dwExtentX = dwExtentX;
7164 OlePres.dwExtentY = -dwExtentY;
7166 /* Set Data and Length */
7167 if(dwDataLength > sizeof(METAFILEPICT16))
7169 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7170 OlePres.pData = &(pData[8]);
7172 /* Save OlePres000 Data to Stream */
7173 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7174 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7175 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7176 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7177 if(OlePres.dwSize > 0)
7179 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7181 IStream_Release(pStream);
7185 /*************************************************************************
7186 * OLECONVERT_CreateOle10NativeStream [Internal]
7188 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7191 * pStorage [I] Dest storage to create the stream in
7192 * pData [I] Ole10 Native Data (ex. bmp)
7193 * dwDataLength [I] Size of the Ole10 Native Data
7199 * This function is used by OleConvertOLESTREAMToIStorage only.
7201 * Might need to verify the data and return appropriate error message
7204 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
7208 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7210 /* Create the Ole10Native Stream */
7211 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7212 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7216 /* Write info to stream */
7217 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7218 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7219 IStream_Release(pStream);
7224 /*************************************************************************
7225 * OLECONVERT_GetOLE10ProgID [Internal]
7227 * Finds the ProgID (or OleTypeID) from the IStorage
7230 * pStorage [I] The Src IStorage to get the ProgID
7231 * strProgID [I] the ProgID string to get
7232 * dwSize [I] the size of the string
7236 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7239 * This function is used by OleConvertIStorageToOLESTREAM only.
7243 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7247 LARGE_INTEGER iSeekPos;
7248 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7249 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7251 /* Open the CompObj Stream */
7252 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7253 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7257 /*Get the OleType from the CompObj Stream */
7258 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7259 iSeekPos.u.HighPart = 0;
7261 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7262 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7263 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7264 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7265 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7266 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7267 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7269 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7272 IStream_Read(pStream, strProgID, *dwSize, NULL);
7274 IStream_Release(pStream);
7279 LPOLESTR wstrProgID;
7281 /* Get the OleType from the registry */
7282 REFCLSID clsid = &(stat.clsid);
7283 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7284 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7287 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7294 /*************************************************************************
7295 * OLECONVERT_GetOle10PresData [Internal]
7297 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7300 * pStorage [I] Src IStroage
7301 * pOleStream [I] Dest OleStream Mem Struct
7307 * This function is used by OleConvertIStorageToOLESTREAM only.
7309 * Memory allocated for pData must be freed by the caller
7313 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7318 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7320 /* Initialize Default data for OLESTREAM */
7321 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7322 pOleStreamData[0].dwTypeID = 2;
7323 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7324 pOleStreamData[1].dwTypeID = 0;
7325 pOleStreamData[0].dwMetaFileWidth = 0;
7326 pOleStreamData[0].dwMetaFileHeight = 0;
7327 pOleStreamData[0].pData = NULL;
7328 pOleStreamData[1].pData = NULL;
7330 /* Open Ole10Native Stream */
7331 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7332 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7336 /* Read Size and Data */
7337 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7338 if(pOleStreamData->dwDataLength > 0)
7340 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7341 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7343 IStream_Release(pStream);
7349 /*************************************************************************
7350 * OLECONVERT_GetOle20PresData[Internal]
7352 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7355 * pStorage [I] Src IStroage
7356 * pOleStreamData [I] Dest OleStream Mem Struct
7362 * This function is used by OleConvertIStorageToOLESTREAM only.
7364 * Memory allocated for pData must be freed by the caller
7366 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7370 OLECONVERT_ISTORAGE_OLEPRES olePress;
7371 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7373 /* Initialize Default data for OLESTREAM */
7374 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7375 pOleStreamData[0].dwTypeID = 2;
7376 pOleStreamData[0].dwMetaFileWidth = 0;
7377 pOleStreamData[0].dwMetaFileHeight = 0;
7378 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7379 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7380 pOleStreamData[1].dwTypeID = 0;
7381 pOleStreamData[1].dwOleTypeNameLength = 0;
7382 pOleStreamData[1].strOleTypeName[0] = 0;
7383 pOleStreamData[1].dwMetaFileWidth = 0;
7384 pOleStreamData[1].dwMetaFileHeight = 0;
7385 pOleStreamData[1].pData = NULL;
7386 pOleStreamData[1].dwDataLength = 0;
7389 /* Open OlePress000 stream */
7390 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7391 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7394 LARGE_INTEGER iSeekPos;
7395 METAFILEPICT16 MetaFilePict;
7396 static const char strMetafilePictName[] = "METAFILEPICT";
7398 /* Set the TypeID for a Metafile */
7399 pOleStreamData[1].dwTypeID = 5;
7401 /* Set the OleTypeName to Metafile */
7402 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7403 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7405 iSeekPos.u.HighPart = 0;
7406 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7408 /* Get Presentation Data */
7409 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7410 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7411 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7412 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7414 /*Set width and Height */
7415 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7416 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7417 if(olePress.dwSize > 0)
7420 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7422 /* Set MetaFilePict struct */
7423 MetaFilePict.mm = 8;
7424 MetaFilePict.xExt = olePress.dwExtentX;
7425 MetaFilePict.yExt = olePress.dwExtentY;
7426 MetaFilePict.hMF = 0;
7428 /* Get Metafile Data */
7429 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7430 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7431 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7433 IStream_Release(pStream);
7437 /*************************************************************************
7438 * OleConvertOLESTREAMToIStorage [OLE32.@]
7443 * DVTARGETDEVICE parameter is not handled
7444 * Still unsure of some mem fields for OLE 10 Stream
7445 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7446 * and "\001OLE" streams
7449 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7450 LPOLESTREAM pOleStream,
7452 const DVTARGETDEVICE* ptd)
7456 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7458 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
7460 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7464 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7467 if(pstg == NULL || pOleStream == NULL)
7469 hRes = E_INVALIDARG;
7474 /* Load the OLESTREAM to Memory */
7475 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7480 /* Load the OLESTREAM to Memory (part 2)*/
7481 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7487 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7489 /* Do we have the IStorage Data in the OLESTREAM */
7490 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7492 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7493 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7497 /* It must be an original OLE 1.0 source */
7498 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7503 /* It must be an original OLE 1.0 source */
7504 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7507 /* Create CompObj Stream if necessary */
7508 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7511 /*Create the Ole Stream if necessary */
7512 OLECONVERT_CreateOleStream(pstg);
7517 /* Free allocated memory */
7518 for(i=0; i < 2; i++)
7520 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7521 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7522 pOleStreamData[i].pstrOleObjFileName = NULL;
7527 /*************************************************************************
7528 * OleConvertIStorageToOLESTREAM [OLE32.@]
7535 * Still unsure of some mem fields for OLE 10 Stream
7536 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7537 * and "\001OLE" streams.
7540 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7542 LPOLESTREAM pOleStream)
7545 HRESULT hRes = S_OK;
7547 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7548 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7550 TRACE("%p %p\n", pstg, pOleStream);
7552 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7554 if(pstg == NULL || pOleStream == NULL)
7556 hRes = E_INVALIDARG;
7560 /* Get the ProgID */
7561 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7562 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7566 /* Was it originally Ole10 */
7567 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7570 IStream_Release(pStream);
7571 /* Get Presentation Data for Ole10Native */
7572 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7576 /* Get Presentation Data (OLE20) */
7577 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7580 /* Save OLESTREAM */
7581 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7584 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7589 /* Free allocated memory */
7590 for(i=0; i < 2; i++)
7592 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7598 /***********************************************************************
7599 * GetConvertStg (OLE32.@)
7601 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7602 FIXME("unimplemented stub!\n");
7606 /******************************************************************************
7607 * StgIsStorageFile [OLE32.@]
7608 * Verify if the file contains a storage object
7614 * S_OK if file has magic bytes as a storage object
7615 * S_FALSE if file is not storage
7618 StgIsStorageFile(LPCOLESTR fn)
7624 TRACE("%s\n", debugstr_w(fn));
7625 hf = CreateFileW(fn, GENERIC_READ,
7626 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7627 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7629 if (hf == INVALID_HANDLE_VALUE)
7630 return STG_E_FILENOTFOUND;
7632 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7634 WARN(" unable to read file\n");
7641 if (bytes_read != 8) {
7642 WARN(" too short\n");
7646 if (!memcmp(magic,STORAGE_magic,8)) {
7651 WARN(" -> Invalid header.\n");
7655 /***********************************************************************
7656 * WriteClassStm (OLE32.@)
7658 * Writes a CLSID to a stream.
7661 * pStm [I] Stream to write to.
7662 * rclsid [I] CLSID to write.
7666 * Failure: HRESULT code.
7668 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
7670 TRACE("(%p,%p)\n",pStm,rclsid);
7672 if (!pStm || !rclsid)
7673 return E_INVALIDARG;
7675 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
7678 /***********************************************************************
7679 * ReadClassStm (OLE32.@)
7681 * Reads a CLSID from a stream.
7684 * pStm [I] Stream to read from.
7685 * rclsid [O] CLSID to read.
7689 * Failure: HRESULT code.
7691 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
7696 TRACE("(%p,%p)\n",pStm,pclsid);
7698 if (!pStm || !pclsid)
7699 return E_INVALIDARG;
7701 /* clear the output args */
7702 *pclsid = CLSID_NULL;
7704 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
7709 if (nbByte != sizeof(CLSID))
7710 return STG_E_READFAULT;